Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file added frontend/public/badge-qwen-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 38 additions & 39 deletions frontend/src/components/ComparisonChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ interface ComparisonData {
lumo: string;
duckAI: string;
chatGPT: string;
claude: string;
grok: string;
}

const comparisonData: ComparisonData[] = [
Expand All @@ -16,101 +14,104 @@ const comparisonData: ComparisonData[] = [
maple: "Yes",
lumo: "No",
duckAI: "No",
chatGPT: "No",
claude: "No",
grok: "No"
chatGPT: "No"
},
{
feature: "Open-Source Code",
maple: "Yes",
lumo: "Partial",
duckAI: "No",
chatGPT: "No",
claude: "No",
grok: "No"
chatGPT: "No"
},
{
feature: "Open Models",
maple: "Yes",
lumo: "Yes",
duckAI: "Partial",
chatGPT: "No",
claude: "No",
grok: "No"
chatGPT: "No"
},
{
feature: "Never uses your data to train AI",
maple: "Yes",
lumo: "?",
duckAI: "?",
chatGPT: "No",
claude: "No",
grok: "No"
chatGPT: "No"
},
{
feature: "Doesn't log your chats",
maple: "Yes",
lumo: "?",
duckAI: "?",
chatGPT: "No",
claude: "No",
grok: "No"
chatGPT: "No"
},
{
feature: "Zero Data Retention",
maple: "Yes",
lumo: "?",
duckAI: "?",
chatGPT: "$",
claude: "$",
grok: "$"
chatGPT: "$"
},
{
feature: "Document Upload",
maple: "Yes",
lumo: "Yes",
duckAI: "No",
chatGPT: "Yes"
},
{
feature: "Image Analysis",
maple: "Yes",
lumo: "No",
duckAI: "Yes",
chatGPT: "Yes"
},
{
feature: "Voice Mode",
maple: "Yes",
lumo: "No",
duckAI: "No",
chatGPT: "Yes"
},
{
feature: "Integration With Coding IDE",
maple: "Yes",
lumo: "No",
duckAI: "No",
chatGPT: "Yes",
claude: "Yes",
grok: "Yes"
chatGPT: "Yes"
},
{
feature: "Teams Plan",
maple: "Yes",
lumo: "No",
duckAI: "No",
chatGPT: "Yes",
claude: "Yes",
grok: "Yes"
chatGPT: "Yes"
},
{
feature: "Developer API",
maple: "Yes",
lumo: "No",
duckAI: "No",
chatGPT: "Yes",
claude: "Yes",
grok: "Yes"
chatGPT: "Yes"
}
];

const ValueIcon = ({ value }: { value: string }) => {
switch (value) {
case "Yes":
return (
<div className="flex items-center justify-center w-6 h-6 bg-green-100 dark:bg-green-900/30 rounded-full">
<div className="flex items-center justify-center w-6 h-6 bg-green-300 dark:bg-green-900/30 rounded-full">
<Check className="w-4 h-4 text-green-600 dark:text-green-400" aria-label="Available" />
</div>
);
case "No":
return (
<div className="flex items-center justify-center w-6 h-6 bg-red-100 dark:bg-red-900/30 rounded-full">
<div className="flex items-center justify-center w-6 h-6 bg-red-300 dark:bg-red-900/30 rounded-full">
<X className="w-4 h-4 text-red-600 dark:text-red-400" aria-label="Not Available" />
</div>
);
case "?":
return (
<div className="flex items-center justify-center w-6 h-6 bg-yellow-100 dark:bg-yellow-900/30 rounded-full">
<div className="flex items-center justify-center w-6 h-6 bg-yellow-300 dark:bg-yellow-900/30 rounded-full">
<HelpCircle
className="w-4 h-4 text-yellow-600 dark:text-yellow-400"
aria-label="Unknown or Unclear"
Expand All @@ -119,7 +120,7 @@ const ValueIcon = ({ value }: { value: string }) => {
);
case "$":
return (
<div className="flex items-center justify-center w-6 h-6 bg-blue-100 dark:bg-blue-900/30 rounded-full">
<div className="flex items-center justify-center w-6 h-6 bg-blue-300 dark:bg-blue-900/30 rounded-full">
<DollarSign
className="w-4 h-4 text-blue-600 dark:text-blue-400"
aria-label="Paid Feature"
Expand All @@ -128,7 +129,7 @@ const ValueIcon = ({ value }: { value: string }) => {
);
case "Partial":
return (
<div className="flex items-center justify-center w-6 h-6 bg-orange-100 dark:bg-orange-900/30 rounded-full">
<div className="flex items-center justify-center w-6 h-6 bg-orange-300 dark:bg-orange-900/30 rounded-full">
<MinusCircle
className="w-4 h-4 text-orange-600 dark:text-orange-400"
aria-label="Partially Available"
Expand All @@ -145,9 +146,7 @@ export function ComparisonChart() {
{ key: "maple", name: "Maple", highlight: true },
{ key: "lumo", name: "Lumo", highlight: false },
{ key: "duckAI", name: "Duck AI", highlight: false },
{ key: "chatGPT", name: "ChatGPT", highlight: false },
{ key: "claude", name: "Claude", highlight: false },
{ key: "grok", name: "Grok", highlight: false }
{ key: "chatGPT", name: "ChatGPT, Gemini, Claude, Grok", highlight: false }
];

return (
Expand All @@ -170,7 +169,7 @@ export function ComparisonChart() {
<div
className="grid border-b border-[hsl(var(--marketing-card-border))]"
style={{
gridTemplateColumns: "minmax(200px, 1.5fr) repeat(6, minmax(80px, 1fr))"
gridTemplateColumns: "minmax(200px, 1.5fr) repeat(4, minmax(80px, 1fr))"
}}
>
<div className="p-3 font-medium text-foreground bg-[hsl(var(--marketing-card-highlight))]/30 text-sm"></div>
Expand All @@ -196,7 +195,7 @@ export function ComparisonChart() {
index % 2 === 0 ? "bg-[hsl(var(--marketing-card-highlight))]/20" : ""
}`}
style={{
gridTemplateColumns: "minmax(200px, 1.5fr) repeat(6, minmax(80px, 1fr))"
gridTemplateColumns: "minmax(200px, 1.5fr) repeat(4, minmax(80px, 1fr))"
}}
>
<div className="p-3 font-medium text-foreground bg-[hsl(var(--marketing-card-highlight))]/30">
Expand Down
60 changes: 55 additions & 5 deletions frontend/src/components/Marketing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import { ComparisonChart } from "./ComparisonChart";
import type { DiscountResponse } from "@/billing/billingApi";
import { Badge } from "@/components/ui/badge";

const AI_MODELS = [
{ src: "/badge-openai-logo.png", alt: "OpenAI", labels: ["OpenAI GPT-OSS"] },
{ src: "/badge-google-logo.png", alt: "Google", labels: ["Google Gemma"] },
{ src: "/badge-deepseek-logo.png", alt: "DeepSeek", labels: ["DeepSeek R1", "DeepSeek V3.1 Terminus"] },
{ src: "/badge-qwen-logo.png", alt: "Qwen", labels: ["Qwen3 Coder", "Qwen3-VL"] },
{ src: "/badge-meta-logo.png", alt: "Meta", labels: ["Meta Llama"] }
];

function CTAButton({
children,
to,
Expand Down Expand Up @@ -279,7 +287,7 @@ export function Marketing() {
<div className="flex gap-4 flex-col sm:flex-row">
<CTAButton to="/signup" primary>
<Sparkles className="h-5 w-5" />
Start Secure Chat
Start Chatting
</CTAButton>
<CTAButton to="/login">Log In</CTAButton>
</div>
Expand Down Expand Up @@ -369,9 +377,8 @@ export function Marketing() {
<Link
to="/downloads"
className="inline-flex items-center gap-2 h-10 px-6 rounded-lg text-center font-medium transition-all duration-300
dark:bg-white/90 dark:text-black dark:hover:bg-white dark:active:bg-white/80
bg-black text-white hover:bg-black/90 active:bg-black/80
border border-[hsl(var(--marketing-card-border))]"
border border-[hsl(var(--marketing-text-muted))]"
>
<Laptop className="h-5 w-5" />
<span>Desktop</span>
Expand Down Expand Up @@ -405,8 +412,51 @@ export function Marketing() {
</div>
</section>

{/* AI Models Section */}
<section className="w-full py-16 dark:bg-[hsl(var(--section-alt))] bg-[hsl(var(--section-alt))]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-4xl font-light mb-4">
Powerful AI models.{" "}
<span className="text-[hsl(var(--purple))] font-medium">No data sharing.</span>
</h2>
<p className="text-xl text-[hsl(var(--marketing-text-muted))] max-w-2xl mx-auto">
We use full-size open models from the biggest providers.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-8">
{AI_MODELS.map((model) => (
<div key={model.alt} className="flex flex-col items-center">
<img
src={model.src}
alt={model.alt}
loading="lazy"
decoding="async"
className="max-w-full h-24 object-contain mb-4"
/>
<div className="flex flex-col items-center">
{model.labels.map((label, index) => (
<span key={index} className="text-lg font-medium text-foreground">
{label}
</span>
))}
</div>
</div>
))}
</div>
<div className="text-center">
<p className="text-xl text-[hsl(var(--marketing-text-muted))] max-w-2xl mx-auto">
<br />
None of your data is transmitted to these companies.
<br />
Get the best without the mess.
</p>
</div>
</div>
</section>

{/* Features Section */}
<section className="w-full py-20 dark:bg-[hsl(var(--section-alt))] bg-[hsl(var(--section-alt))]">
<section className="w-full py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-4xl font-light mb-4">
Expand Down Expand Up @@ -442,7 +492,7 @@ export function Marketing() {
</section>

{/* Proof/Verification Section */}
<section className="w-full py-20">
<section className="w-full py-20 dark:bg-[hsl(var(--section-alt))] bg-[hsl(var(--section-alt))]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
<div>
Expand Down
Loading