Skip to content

Commit 0795575

Browse files
authored
Merge pull request #41 from NillionNetwork/feat/blog-and-fixes
feat/blog-and-fixes
2 parents ceebcc2 + b2b3b39 commit 0795575

File tree

8 files changed

+246
-23
lines changed

8 files changed

+246
-23
lines changed

public/img/message_counter.png

38.7 KB
Loading

public/img/starter_messages.jpeg

10.1 KB
Loading

src/app/blog/page.tsx

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useState } from "react";
1010
export default function ContentPolicy() {
1111
const [isExpandedFirst, setIsExpandedFirst] = useState(false);
1212
const [isExpandedSecond, setIsExpandedSecond] = useState(false);
13+
const [isExpandedThird, setIsExpandedThird] = useState(false);
1314

1415
return (
1516
<div className="min-h-screen bg-cream-50">
@@ -44,7 +45,154 @@ export default function ContentPolicy() {
4445

4546
{/* Content */}
4647
<div className="prose prose-lg max-w-none space-y-8">
47-
{/* First Article - New Post */}
48+
{/* Newest Article - nilGPT v4 */}
49+
<div className="bg-white border-4 border-navy-900 brutalist-shadow p-8 space-y-8">
50+
<article className="space-y-8">
51+
{/* Main Title */}
52+
<h1 className="font-display font-black text-3xl lg:text-4xl text-navy-900 mb-6">
53+
nilGPT v4: Image uploads and bigger models
54+
</h1>
55+
56+
{/* Date */}
57+
<div className="text-navy-600 text-sm font-medium mb-8">
58+
September 11, 2025
59+
</div>
60+
61+
{/* Introduction */}
62+
<p className="text-navy-700 leading-relaxed mb-6">
63+
A new week comes with a new release of nilGPT. We're keeping
64+
up the rhythm and shipping fast. Every release takes us one
65+
step closer toward building the most privacy-preserving and
66+
capable AI assistant. We believe this is something that needs
67+
to exist for those sensitive questions that ChatGPT and others
68+
should never have their eyes on.
69+
</p>
70+
71+
{/* Expandable Content */}
72+
<div
73+
className={`overflow-hidden transition-all duration-500 ease-in-out ${
74+
isExpandedThird ? "max-h-none" : "max-h-0"
75+
}`}
76+
>
77+
<p className="text-navy-700 leading-relaxed mb-4">
78+
Two weeks ago, we shipped attestation flows, giving you the
79+
ability to verify that nilGPT is running exactly the code we
80+
open-sourced. Last week, we rolled out the PWA, so you can
81+
install nilGPT directly on your mobile device for a more
82+
seamless experience.
83+
</p>
84+
85+
<p className="text-navy-700 leading-relaxed mb-4">
86+
This week, we're turning up the dial again with two major
87+
new capabilities: bigger models and image uploads.
88+
</p>
89+
90+
{/* Major Features Section */}
91+
<section className="mb-8">
92+
<h2 className="font-display font-bold text-2xl text-navy-900 mb-4">
93+
Major Features
94+
</h2>
95+
<p className="text-navy-700 leading-relaxed mb-4">
96+
The first big upgrade is the introduction of Gemma 3 27B,
97+
a state-of-the-art model that significantly increases the
98+
power of nilGPT. As a reminder, all of our models run on
99+
bare-metal servers inside TEEs (trusted execution
100+
environments). That means there is no reliance on
101+
third-party clouds, no hidden data pipelines, and the same
102+
uncompromising privacy guarantees we've promised from day
103+
one. With Gemma 3 27B, you get stronger reasoning, more
104+
detailed answers, and faster performance—all while keeping
105+
your data fully secure.
106+
</p>
107+
<p className="text-navy-700 leading-relaxed mb-4">
108+
The second major feature is the ability to upload images.
109+
For the first time, nilGPT can handle images as well as
110+
text. You can now upload screenshots, diagrams, or photos
111+
and interact with them directly in nilGPT.
112+
</p>
113+
</section>
114+
115+
{/* More Improvements Section */}
116+
<section className="mb-8">
117+
<h2 className="font-display font-bold text-2xl text-navy-900 mb-4">
118+
More Improvements
119+
</h2>
120+
<p className="text-navy-700 leading-relaxed mb-4">
121+
Alongside these major features, we've added a set of
122+
smaller but useful improvements. You can now delete and
123+
rename chats, giving you more control over your chat
124+
history. On the landing page, a new counter shows the
125+
total number of messages sent with nilGPT since launch—a
126+
small but satisfying reminder of how quickly the community
127+
is growing.
128+
</p>
129+
130+
{/* Message Counter Image */}
131+
<div className="my-8">
132+
<Image
133+
src="/img/message_counter.png"
134+
alt="nilGPT Message Counter"
135+
width={400}
136+
height={300}
137+
className="w-full max-w-md mx-auto h-auto border-2 border-navy-900 rounded-lg"
138+
priority
139+
/>
140+
</div>
141+
142+
<p className="text-navy-700 leading-relaxed mb-4">
143+
We've also introduced starter messages for each mode,
144+
designed to make it easier to get going with nilGPT right
145+
away.
146+
</p>
147+
148+
{/* Starter Messages Image */}
149+
<div className="my-8">
150+
<Image
151+
src="/img/starter_messages.jpeg"
152+
alt="nilGPT Starter Messages"
153+
width={400}
154+
height={300}
155+
className="w-full max-w-md mx-auto h-auto border-2 border-navy-900 rounded-lg"
156+
priority
157+
/>
158+
</div>
159+
</section>
160+
161+
{/* Looking Ahead Section */}
162+
<section className="mb-8">
163+
<h2 className="font-display font-bold text-2xl text-navy-900 mb-4">
164+
Looking Ahead
165+
</h2>
166+
<p className="text-navy-700 leading-relaxed mb-4">
167+
In the near future, you will be able to search the web
168+
through nilGPT, extending the chatbot's knowledge base
169+
while still preserving the same guarantees of privacy and
170+
security.
171+
</p>
172+
<p className="text-navy-700 leading-relaxed mb-4">
173+
We're also working on voice mode, which will make
174+
conversations with nilGPT feel even more natural and
175+
seamless. The pace won't slow down.
176+
</p>
177+
<p className="text-navy-700 leading-relaxed">
178+
Expect more exciting updates in the weeks ahead.
179+
</p>
180+
</section>
181+
</div>
182+
183+
{/* Toggle Button */}
184+
<div className="text-center mt-6">
185+
<button
186+
onClick={() => setIsExpandedThird(!isExpandedThird)}
187+
className="bg-navy-900 text-cream-50 px-6 py-3 font-bold brutalist-shadow hover:translate-x-1 hover:translate-y-1 hover:shadow-none transition-all duration-200 rounded-lg"
188+
>
189+
{isExpandedThird ? "Show Less" : "Read More"}
190+
</button>
191+
</div>
192+
</article>
193+
</div>
194+
195+
{/* Second Article - Attestation Post */}
48196
<div className="bg-white border-4 border-navy-900 brutalist-shadow p-8 space-y-8">
49197
<article className="space-y-8">
50198
{/* Main Title */}
@@ -297,7 +445,7 @@ export default function ContentPolicy() {
297445
</article>
298446
</div>
299447

300-
{/* Second Article - Original Post */}
448+
{/* Third Article - Original Post */}
301449
<div className="bg-white border-4 border-navy-900 brutalist-shadow p-8 space-y-8">
302450
<article className="space-y-8">
303451
{/* Main Title */}

src/components/AttestationModal.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ const AttestationModal = () => {
7878
<code className="text-sm font-mono text-gray-600 bg-gray-100 px-1 py-0.5 rounded break-all">
7979
https://nilgpt.xyz/nilcc/api/v1/report
8080
</code>
81+
<Button
82+
onClick={() => getMeasurementHash()}
83+
disabled={isLoading}
84+
size="sm"
85+
className="flex items-center gap-2 ml-auto"
86+
data-umami-event="Fetch Attestation Clicked"
87+
>
88+
<RefreshCw
89+
className={`w-4 h-4 ${isLoading ? "animate-spin" : ""}`}
90+
/>
91+
Fetch
92+
</Button>
8193
</div>
8294
</div>
8395

@@ -109,18 +121,6 @@ const AttestationModal = () => {
109121
</div>
110122
</div>
111123
)}
112-
<Button
113-
onClick={() => getMeasurementHash()}
114-
disabled={isLoading}
115-
size="sm"
116-
className="flex items-center gap-2 ml-auto"
117-
data-umami-event="Fetch Attestation Clicked"
118-
>
119-
<RefreshCw
120-
className={`w-4 h-4 ${isLoading ? "animate-spin" : ""}`}
121-
/>
122-
Fetch
123-
</Button>
124124
</div>
125125
</div>
126126
</div>

src/components/auth/AuthModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default function AuthModal({
7171
{mode === "signup" && (
7272
<div className="mb-4">
7373
<label className="block text-sm font-medium text-gray-700 mb-2">
74-
Name
74+
Username
7575
</label>
7676
<input
7777
type="text"

src/components/auth/SecretKeyModal.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ export function SecretKeyModal({ isOpen, onClose }: SecretKeyModalProps) {
4444
</h2>
4545
<p className="text-gray-700 mb-6 leading-relaxed">
4646
Please create or enter your passphrase. This can be any word or
47-
phrase, and is used to encrypt all chat messages locally. Your
48-
passphrase never leaves your browser. <br />
47+
phrase, and is used to encrypt all chat messages locally.
48+
<br />
49+
<br />
50+
Your passphrase never leaves your browser - it is stored in session
51+
storage and deleted if you close the tab.
52+
<br />
4953
<br /> Keep your passphrase secure. You&apos;ll need to use the same
5054
one every time you want to view a chat.
5155
</p>

src/components/chat/Sidebar.tsx

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import {
44
CheckIcon,
55
EditIcon,
66
EllipsisVertical,
7+
LogOut,
8+
Shield,
9+
ShieldCheck,
710
Trash2Icon,
811
XIcon,
912
} from "lucide-react";
@@ -13,10 +16,12 @@ import { usePathname, useRouter } from "next/navigation";
1316
import type React from "react";
1417
import { useEffect, useRef, useState } from "react";
1518
import { v4 as uuidv4 } from "uuid";
19+
import { useApp } from "@/contexts/AppContext";
1620
import { useAuth } from "@/contexts/UnifiedAuthProvider";
1721
import { useEncryption } from "@/hooks/useEncryption";
1822
import { LocalStorageService } from "@/services/LocalStorage";
1923
import AttestationModal from "../AttestationModal";
24+
import { SecretKeyModal } from "../auth/SecretKeyModal";
2025
import { Button } from "../ui/button";
2126
import { Dialog, DialogTrigger } from "../ui/dialog";
2227

@@ -31,7 +36,8 @@ interface SidebarProps {
3136
}
3237

3338
const Sidebar: React.FC<SidebarProps> = ({ isCollapsed, onClose }) => {
34-
const { user } = useAuth();
39+
const { user, signOut } = useAuth();
40+
const { setUserSecretKeySeed } = useApp();
3541
const router = useRouter();
3642
const pathname = usePathname();
3743
const { decrypt, hasSecretKey } = useEncryption();
@@ -41,6 +47,8 @@ const Sidebar: React.FC<SidebarProps> = ({ isCollapsed, onClose }) => {
4147
const [loading, setLoading] = useState(true);
4248
const [isCreatingChat, setIsCreatingChat] = useState(false);
4349
const [isAttestationModalOpen, setIsAttestationModalOpen] = useState(false);
50+
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
51+
const [showSecretKeyModal, setShowSecretKeyModal] = useState(false);
4452
const [contextMenuChatId, setContextMenuChatId] = useState<string | null>(
4553
null,
4654
);
@@ -215,16 +223,19 @@ const Sidebar: React.FC<SidebarProps> = ({ isCollapsed, onClose }) => {
215223
if (contextMenuChatId) {
216224
setContextMenuChatId(null);
217225
}
226+
if (isUserMenuOpen) {
227+
setIsUserMenuOpen(false);
228+
}
218229
};
219230

220-
if (contextMenuChatId) {
231+
if (contextMenuChatId || isUserMenuOpen) {
221232
document.addEventListener("click", handleClickOutside);
222233
}
223234

224235
return () => {
225236
document.removeEventListener("click", handleClickOutside);
226237
};
227-
}, [contextMenuChatId]);
238+
}, [contextMenuChatId, isUserMenuOpen]);
228239

229240
const handleNewChat = async () => {
230241
setIsCreatingChat(true);
@@ -330,6 +341,26 @@ const Sidebar: React.FC<SidebarProps> = ({ isCollapsed, onClose }) => {
330341
setEditingTitle("");
331342
};
332343

344+
// User menu handlers
345+
const handleChangePassphrase = () => {
346+
// Clear the current passphrase from both context and session storage
347+
setUserSecretKeySeed("");
348+
sessionStorage.removeItem("userSecretKeySeed");
349+
// Open the modal
350+
setShowSecretKeyModal(true);
351+
setIsUserMenuOpen(false);
352+
};
353+
354+
const handleViewAttestation = () => {
355+
setIsAttestationModalOpen(true);
356+
setIsUserMenuOpen(false);
357+
};
358+
359+
const handleSignOut = async () => {
360+
await signOut();
361+
setIsUserMenuOpen(false);
362+
};
363+
333364
return (
334365
<div
335366
className={`
@@ -534,15 +565,54 @@ const Sidebar: React.FC<SidebarProps> = ({ isCollapsed, onClose }) => {
534565
<AttestationModal />
535566
</Dialog>
536567

537-
<div className="border-t border-neutral-600 shrink-0 py-3 p-4 ">
568+
<div className="border-t border-neutral-600 shrink-0 py-3 p-4 relative">
538569
<div className="flex items-center space-x-3">
539-
<div className="w-8 h-8 rounded-full bg-[#FFC971] flex items-center justify-center text-sm font-medium text-black shrink-0">
570+
<button
571+
onClick={(e) => {
572+
e.stopPropagation();
573+
setIsUserMenuOpen(!isUserMenuOpen);
574+
}}
575+
className="w-8 h-8 rounded-full bg-[#FFC971] flex items-center justify-center text-sm font-medium text-black shrink-0 hover:bg-[#FFD584] transition-colors"
576+
>
540577
{initials}
541-
</div>
578+
</button>
542579
<span className="text-md text-white truncate">{userName}</span>
543580
</div>
581+
582+
{/* User Menu Dropdown */}
583+
{isUserMenuOpen && (
584+
<div className="absolute bottom-full left-4 right-4 mb-2 bg-[#2a2a2a] border border-[#444] rounded-lg shadow-lg z-50">
585+
<button
586+
onClick={handleChangePassphrase}
587+
className="w-full flex items-center gap-3 px-4 py-3 text-sm text-white hover:bg-[#333] rounded-t-lg transition-colors"
588+
>
589+
<Shield size={16} />
590+
Change Passphrase
591+
</button>
592+
<button
593+
onClick={handleViewAttestation}
594+
className="w-full flex items-center gap-3 px-4 py-3 text-sm text-white hover:bg-[#333] transition-colors"
595+
>
596+
<ShieldCheck size={16} />
597+
View Attestation
598+
</button>
599+
<button
600+
onClick={handleSignOut}
601+
className="w-full flex items-center gap-3 px-4 py-3 text-sm text-red-400 hover:bg-[#333] rounded-b-lg transition-colors"
602+
>
603+
<LogOut size={16} />
604+
Sign Out
605+
</button>
606+
</div>
607+
)}
544608
</div>
545609
</div>
610+
611+
{/* Secret Key Modal */}
612+
<SecretKeyModal
613+
isOpen={showSecretKeyModal}
614+
onClose={() => setShowSecretKeyModal(false)}
615+
/>
546616
</div>
547617
);
548618
};

src/contexts/AppContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export function AppProvider({ children }: { children: ReactNode }) {
7070
if (wasAuthenticated && !isCurrentlyAuthenticated) {
7171
setUserSecretKeySeed(null);
7272
sessionStorage.removeItem("userSecretKeySeed");
73+
setIsSidebarCollapsed(true); // Collapse sidebar on sign out
7374
}
7475

7576
// Update the previous state

0 commit comments

Comments
 (0)