Skip to content

Commit 06a1f64

Browse files
authored
Merge pull request #22 from Pollux-Studio/main
main to develop
2 parents c265016 + e642af6 commit 06a1f64

7 files changed

Lines changed: 318 additions & 51 deletions

File tree

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { Navbar } from "@/components/landing/navbar";
2+
import { Footer } from "@/components/landing/footer";
3+
import fs from "node:fs/promises";
4+
import path from "node:path";
5+
6+
type InlinePart = {
7+
type: "text" | "bold" | "code" | "link";
8+
value: string;
9+
href?: string;
10+
};
11+
12+
const parseInline = (text: string) => {
13+
const parts: InlinePart[] = [];
14+
let remaining = text;
15+
const pattern = /(\*\*[^*]+\*\*|`[^`]+`|\[[^\]]+\]\([^)]+\))/;
16+
17+
while (remaining.length > 0) {
18+
const match = remaining.match(pattern);
19+
if (!match || match.index === undefined) {
20+
parts.push({ type: "text", value: remaining });
21+
break;
22+
}
23+
24+
const index = match.index;
25+
if (index > 0) {
26+
parts.push({ type: "text", value: remaining.slice(0, index) });
27+
}
28+
29+
const token = match[0];
30+
if (token.startsWith("**")) {
31+
parts.push({ type: "bold", value: token.slice(2, -2) });
32+
} else if (token.startsWith("`")) {
33+
parts.push({ type: "code", value: token.slice(1, -1) });
34+
} else if (token.startsWith("[")) {
35+
const labelEnd = token.indexOf("]");
36+
const urlStart = token.indexOf("(") + 1;
37+
const label = token.slice(1, labelEnd);
38+
const href = token.slice(urlStart, -1);
39+
parts.push({ type: "link", value: label, href });
40+
}
41+
42+
remaining = remaining.slice(index + token.length);
43+
}
44+
45+
return parts;
46+
};
47+
48+
const renderInline = (text: string, keyPrefix: string) => {
49+
const parts = parseInline(text);
50+
return parts.map((part, index) => {
51+
const key = `${keyPrefix}-${index}`;
52+
if (part.type === "bold") {
53+
return (
54+
<strong key={key} className="text-white">
55+
{part.value}
56+
</strong>
57+
);
58+
}
59+
if (part.type === "code") {
60+
return (
61+
<code
62+
key={key}
63+
className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs text-white/80"
64+
>
65+
{part.value}
66+
</code>
67+
);
68+
}
69+
if (part.type === "link" && part.href) {
70+
return (
71+
<a
72+
key={key}
73+
href={part.href}
74+
className="text-white underline decoration-white/40 hover:decoration-white"
75+
target="_blank"
76+
rel="noreferrer"
77+
>
78+
{part.value}
79+
</a>
80+
);
81+
}
82+
return <span key={key}>{part.value}</span>;
83+
});
84+
};
85+
86+
const renderMarkdown = (content: string) => {
87+
const lines = content.split(/\r?\n/);
88+
const elements: React.ReactNode[] = [];
89+
let listItems: string[] = [];
90+
91+
const flushList = () => {
92+
if (listItems.length === 0) return;
93+
const items = listItems;
94+
listItems = [];
95+
elements.push(
96+
<ul key={`list-${elements.length}`} className="space-y-2 pl-5 text-white/80">
97+
{items.map((item, index) => (
98+
<li key={`list-item-${index}`} className="list-disc">
99+
{renderInline(item, `list-${elements.length}-${index}`)}
100+
</li>
101+
))}
102+
</ul>
103+
);
104+
};
105+
106+
lines.forEach((line, index) => {
107+
const trimmed = line.trim();
108+
109+
if (!trimmed) {
110+
flushList();
111+
return;
112+
}
113+
114+
if (trimmed === "---") {
115+
flushList();
116+
elements.push(<hr key={`hr-${index}`} className="border-white/10" />);
117+
return;
118+
}
119+
120+
if (trimmed.startsWith("### ")) {
121+
flushList();
122+
elements.push(
123+
<h3 key={`h3-${index}`} className="text-lg font-semibold text-white">
124+
{renderInline(trimmed.slice(4), `h3-${index}`)}
125+
</h3>
126+
);
127+
return;
128+
}
129+
130+
if (trimmed.startsWith("## ")) {
131+
flushList();
132+
elements.push(
133+
<h2 key={`h2-${index}`} className="text-2xl font-semibold text-white mt-6">
134+
{renderInline(trimmed.slice(3), `h2-${index}`)}
135+
</h2>
136+
);
137+
return;
138+
}
139+
140+
if (trimmed.startsWith("# ")) {
141+
flushList();
142+
elements.push(
143+
<h1 key={`h1-${index}`} className="text-3xl font-bold text-white">
144+
{renderInline(trimmed.slice(2), `h1-${index}`)}
145+
</h1>
146+
);
147+
return;
148+
}
149+
150+
if (trimmed.startsWith("- ")) {
151+
listItems.push(trimmed.slice(2));
152+
return;
153+
}
154+
155+
flushList();
156+
elements.push(
157+
<p key={`p-${index}`} className="text-white/70">
158+
{renderInline(trimmed, `p-${index}`)}
159+
</p>
160+
);
161+
});
162+
163+
flushList();
164+
return elements;
165+
};
166+
167+
const loadChangelog = async () => {
168+
const candidates = [
169+
path.join(process.cwd(), "CHANGELOG.md"),
170+
path.resolve(process.cwd(), "..", "CHANGELOG.md"),
171+
];
172+
173+
for (const candidate of candidates) {
174+
try {
175+
return await fs.readFile(candidate, "utf8");
176+
} catch {
177+
continue;
178+
}
179+
}
180+
181+
return "# Changelog\n\nChangelog file not found.";
182+
};
183+
184+
export default async function ChangelogPage() {
185+
const changelog = await loadChangelog();
186+
187+
return (
188+
<main className="min-h-screen bg-[var(--background)] text-foreground">
189+
<Navbar />
190+
191+
<section className="wrapper wrapper--ticks border-t border-nickel px-6 sm:px-10 py-14 sm:py-20">
192+
<div className="flex flex-col gap-6">{renderMarkdown(changelog)}</div>
193+
</section>
194+
195+
<Footer />
196+
</main>
197+
);
198+
}

maxc-landing-page/app/downloads/page.tsx

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Image from "next/image";
21
import { Navbar } from "@/components/landing/navbar";
32
import { Footer } from "@/components/landing/footer";
43

@@ -98,9 +97,10 @@ export default async function DownloadsPage() {
9897
}))
9998
);
10099

101-
const windowsAssets = assets.filter((asset) => asset.platformKey.startsWith("windows"));
102-
const macAssets = assets.filter((asset) => asset.platformKey.startsWith("darwin"));
103-
const linuxAssets = assets.filter((asset) => asset.platformKey.startsWith("linux"));
100+
const versionToken = latest.version.replace(/^v/, "");
101+
const windowsAssets = assets.filter(
102+
(asset) => asset.platformKey.startsWith("windows") && asset.name.includes(versionToken)
103+
);
104104

105105
return (
106106
<main className="min-h-screen bg-[var(--background)] text-foreground">
@@ -122,7 +122,7 @@ export default async function DownloadsPage() {
122122
</section>
123123

124124
<section className="wrapper wrapper--ticks border-t border-nickel px-6 sm:px-10 py-10">
125-
<div className="grid gap-6 lg:grid-cols-3">
125+
<div className="grid gap-6 lg:grid-cols-1">
126126
<div className="rounded-xl border border-[rgba(255,255,255,0.08)] bg-white/[0.02] p-5">
127127
<div className="flex items-center gap-2">
128128
<WindowsLogo className="h-5 w-5 text-white/70" />
@@ -135,44 +135,6 @@ export default async function DownloadsPage() {
135135
))}
136136
</div>
137137
</div>
138-
139-
<div className="rounded-xl border border-[rgba(255,255,255,0.08)] bg-white/[0.02] p-5">
140-
<div className="flex items-center gap-2">
141-
<Image
142-
src="/apple-logo-svgrepo-com.svg"
143-
alt="Apple"
144-
width={20}
145-
height={20}
146-
className="h-5 w-5 object-contain invert opacity-70"
147-
/>
148-
<div className="text-white font-semibold text-lg">macOS</div>
149-
</div>
150-
<div className="text-white/40 text-xs mt-1">Apple Silicon</div>
151-
<div className="mt-5 space-y-3">
152-
{macAssets.map((asset) => (
153-
<AssetRow key={asset.url} asset={asset} />
154-
))}
155-
</div>
156-
</div>
157-
158-
<div className="rounded-xl border border-[rgba(255,255,255,0.08)] bg-white/[0.02] p-5">
159-
<div className="flex items-center gap-2">
160-
<Image
161-
src="/linux-svgrepo-com.svg"
162-
alt="Linux"
163-
width={20}
164-
height={20}
165-
className="h-5 w-5 object-contain grayscale invert opacity-70"
166-
/>
167-
<div className="text-white font-semibold text-lg">Linux</div>
168-
</div>
169-
<div className="text-white/40 text-xs mt-1">AppImage, DEB, RPM</div>
170-
<div className="mt-5 space-y-3">
171-
{linuxAssets.map((asset) => (
172-
<AssetRow key={asset.url} asset={asset} />
173-
))}
174-
</div>
175-
</div>
176138
</div>
177139
</section>
178140

maxc-landing-page/app/layout.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ const jetbrainsMono = JetBrains_Mono({
1818

1919
export const metadata: Metadata = {
2020
metadataBase: new URL("https://maxc.polluxstudio.in"),
21-
title: "maxc - Workspace for AI Coding Agents",
21+
title: {
22+
default: "maxc - Workspace for AI Coding Agents",
23+
template: "%s - maxc",
24+
},
2225
description:
2326
"maxc is a programmable workspace for AI coding agents. Run terminals, automate browsers, and orchestrate AI agents from one developer environment.",
2427
keywords: [
@@ -30,15 +33,23 @@ export const metadata: Metadata = {
3033
"agent orchestration",
3134
"developer productivity tools",
3235
],
36+
applicationName: "maxc",
37+
alternates: {
38+
canonical: "/",
39+
},
40+
robots: {
41+
index: true,
42+
follow: true,
43+
},
3344
openGraph: {
3445
title: "maxc - Control Center for AI Coding Agents",
3546
description:
3647
"Run terminals, control browsers, and orchestrate AI agents from one programmable workspace.",
37-
url: "https://maxc.polluxstudio.in",
48+
url: "/",
3849
siteName: "maxc",
3950
images: [
4051
{
41-
url: "https://maxc.polluxstudio.in/og-image.png",
52+
url: "/og.png",
4253
width: 1200,
4354
height: 630,
4455
alt: "maxc - Control Center for AI Coding Agents",
@@ -50,10 +61,10 @@ export const metadata: Metadata = {
5061
card: "summary_large_image",
5162
title: "maxc - AI Coding Agent Workspace",
5263
description: "Run terminals, browsers, and AI agents in one programmable workspace.",
53-
images: ["https://maxc.polluxstudio.in/og-image.png"],
64+
images: ["/og.png"],
5465
},
5566
icons: {
56-
icon: "/maxc_logo_full_white_single.svg",
67+
icon: [{ url: "/maxc_logo_full_white_single.svg" }],
5768
},
5869
};
5970

maxc-landing-page/components/landing/footer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const columns = [
55
{
66
title: "Product",
77
links: [
8-
{ label: "Documentation", href: "/docs" },
8+
{ label: "Changelogs", href: "/changelogs" },
99
],
1010
},
1111
// {
@@ -19,7 +19,7 @@ const columns = [
1919
title: "Social",
2020
links: [
2121
{ label: "GitHub", href: "https://github.com/Pollux-Studio/maxc" },
22-
{ label: "LinkedIn", href: "https://www.linkedin.com" },
22+
{ label: "LinkedIn", href: "https://www.linkedin.com/in/rjprasanth/" },
2323
],
2424
},
2525
];

0 commit comments

Comments
 (0)