diff --git a/package-lock.json b/package-lock.json
index 59921e1..8860fa6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,11 +10,14 @@
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "lightweight-charts": "^5.1.0",
+ "lightweight-charts-react-components": "^1.4.0",
"lucide-react": "^0.563.0",
"next": "16.1.6",
"radix-ui": "^1.4.3",
"react": "19.2.3",
"react-dom": "19.2.3",
+ "react-resizable-panels": "^4.6.4",
"tailwind-animations": "^1.0.1",
"tailwind-merge": "^3.4.0",
"tailwind-motlib": "^1.0.4"
@@ -4599,6 +4602,12 @@
"express": ">= 4.11"
}
},
+ "node_modules/fancy-canvas": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-2.1.0.tgz",
+ "integrity": "sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==",
+ "license": "MIT"
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -5715,6 +5724,26 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/lightweight-charts": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-5.1.0.tgz",
+ "integrity": "sha512-jEAYR4ODYeyNZcWUigsoLTl52rbPmgXnvd5FLIv/ZoA/2sSDw63YKnef8n4yhzum7W926yHeFwlm7ididKb7YQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fancy-canvas": "2.1.0"
+ }
+ },
+ "node_modules/lightweight-charts-react-components": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/lightweight-charts-react-components/-/lightweight-charts-react-components-1.4.0.tgz",
+ "integrity": "sha512-FWzEiQDhQaQ04l6kV2fzsODK6jCrzbLOai+r6oEvCqC3tniXjKUAFHbTUlemII7PfiWYwyVAVgl1XRv72Q8Phw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "lightweight-charts": ">=5 < 6",
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -6753,6 +6782,16 @@
}
}
},
+ "node_modules/react-resizable-panels": {
+ "version": "4.6.4",
+ "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.6.4.tgz",
+ "integrity": "sha512-E7Szs1xyaMZ7xOI2gG4TECNz4r/gmpV1AsXyZRnER6OQnfFf9uclFmrHHZR3h/iF8vQS+nQ1LKyZv9bzwGxPSg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
diff --git a/package.json b/package.json
index 8b3d99b..cbe478e 100644
--- a/package.json
+++ b/package.json
@@ -10,11 +10,14 @@
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "lightweight-charts": "^5.1.0",
+ "lightweight-charts-react-components": "^1.4.0",
"lucide-react": "^0.563.0",
"next": "16.1.6",
"radix-ui": "^1.4.3",
"react": "19.2.3",
"react-dom": "19.2.3",
+ "react-resizable-panels": "^4.6.4",
"tailwind-animations": "^1.0.1",
"tailwind-merge": "^3.4.0",
"tailwind-motlib": "^1.0.4"
diff --git a/src/app/globals.css b/src/app/globals.css
index 5b22053..16bfb74 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -48,6 +48,9 @@
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
+
+ --color-bullish: var(--bullish);
+ --color-bearish: var(--bearish);
}
:root {
@@ -57,7 +60,7 @@
--background: oklch(0.13 0.01 260); /* deep charcoal */
--foreground: oklch(0.98 0 0);
- --card: oklch(0.18 0.015 260);
+ --card: oklch(15.349% 0.00726 258.438);
--card-foreground: oklch(0.98 0 0);
--popover: oklch(0.18 0.015 260);
@@ -106,6 +109,12 @@
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.6 0.12 285);
+
+ /* Bullish - vibrant SOL mint */
+ --bullish: oklch(0.72 0.18 165);
+
+ /* Bearish - destructive/coral red */
+ --bearish: oklch(63.35% 0.2171 4.38);
}
@layer base {
diff --git a/src/app/market/[mint]/components/market-sidebar/BuySell.tsx b/src/app/market/[mint]/components/market-sidebar/BuySell.tsx
new file mode 100644
index 0000000..d8209aa
--- /dev/null
+++ b/src/app/market/[mint]/components/market-sidebar/BuySell.tsx
@@ -0,0 +1,55 @@
+import { Button } from "@/components/ui/button";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { ChevronDown, ChevronUp } from "lucide-react";
+import { useState } from "react";
+
+export default function BuySell() {
+ const [isCollapsed, setIsCollapsed] = useState(false);
+
+ return (
+
+
+
+
+ Buy
+
+
+ Sell
+
+
+
+ {/* The Toggle Button */}
+
+
+
+ {/* Conditional Rendering or Height Hiding */}
+ {!isCollapsed && (
+ <>
+
+ buy placeholder
+
+
+ sell placeholder
+
+ >
+ )}
+
+ );
+}
diff --git a/src/app/market/[mint]/components/market-sidebar/Chart.tsx b/src/app/market/[mint]/components/market-sidebar/Chart.tsx
new file mode 100644
index 0000000..5d6bde0
--- /dev/null
+++ b/src/app/market/[mint]/components/market-sidebar/Chart.tsx
@@ -0,0 +1,96 @@
+import { CandlestickSeries, Chart } from "lightweight-charts-react-components";
+
+export default function MarketChart() {
+ return (
+
+
+
+ );
+}
diff --git a/src/app/market/[mint]/components/market-sidebar/MarketSidebar.tsx b/src/app/market/[mint]/components/market-sidebar/MarketSidebar.tsx
new file mode 100644
index 0000000..3e2f192
--- /dev/null
+++ b/src/app/market/[mint]/components/market-sidebar/MarketSidebar.tsx
@@ -0,0 +1,12 @@
+import BuySell from "./BuySell";
+import TokenInfo from "./TokenInfo";
+
+export default function MarketSidebar() {
+ return (
+
+ );
+}
diff --git a/src/app/market/[mint]/components/market-sidebar/TokenInfo.tsx b/src/app/market/[mint]/components/market-sidebar/TokenInfo.tsx
new file mode 100644
index 0000000..b8e1fb9
--- /dev/null
+++ b/src/app/market/[mint]/components/market-sidebar/TokenInfo.tsx
@@ -0,0 +1,126 @@
+import { cn } from "@/lib/utils";
+import { Token, TokenLiveData } from "@/modules/tokens/types";
+import {
+ BoxesIcon,
+ ChartCandlestick,
+ ChefHatIcon,
+ CrosshairIcon,
+ FlameIcon,
+ GhostIcon,
+ LucideIcon,
+ ShieldIcon,
+ UsersIcon,
+ UserStarIcon,
+} from "lucide-react";
+
+export default function TokenInfo() {
+ const info: Token = {
+ mint: "",
+ symbol: "",
+ name: "",
+ };
+
+ const liveData: TokenLiveData = {
+ topHolder: 67,
+ devHold: 20,
+ snipersHold: 30,
+ insidersHold: 10,
+ bundleHold: 3,
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CA and DA placeholder
+
+
+ );
+}
+
+function DataCard({
+ label,
+ value,
+ className,
+ icon: Icon,
+}: {
+ icon: LucideIcon;
+ label: string;
+ value: string;
+ className: string;
+}) {
+ return (
+
+
+
+ {value}
+
+
{label}
+
+ );
+}
diff --git a/src/app/market/[mint]/page.tsx b/src/app/market/[mint]/page.tsx
new file mode 100644
index 0000000..bb40490
--- /dev/null
+++ b/src/app/market/[mint]/page.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+import Navbar from "@/components/layout/Navbar";
+import Statusbar from "@/components/layout/Statusbar";
+import MarketSidebar from "./components/market-sidebar/MarketSidebar";
+import Chart from "./components/market-sidebar/Chart";
+
+export default function Market() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/app/markets/components/TokenCard.tsx b/src/app/markets/components/TokenCard.tsx
index 51e0fdd..bf0c96a 100644
--- a/src/app/markets/components/TokenCard.tsx
+++ b/src/app/markets/components/TokenCard.tsx
@@ -40,13 +40,13 @@ export default function TokenCard({ token }: { token: Token }) {
"
onClick={(e) => {
if (e.ctrlKey || e.metaKey) {
- window.open(`/token/${token.mint}`, "_blank", "noopener,noreferrer");
+ window.open(`/market/${token.mint}`, "_blank", "noopener,noreferrer");
} else {
- router.push(`/token/${token.mint}`);
+ router.push(`/market/${token.mint}`);
}
}}
onContextMenu={() =>
- window.open(`/token/${token.mint}`, "_blank", "noopener,noreferrer")
+ window.open(`/market/${token.mint}`, "_blank", "noopener,noreferrer")
}
>
{/* Left: logo */}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index fc98459..950006a 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -22,38 +22,38 @@ export default function Landing() {
{/* Hero */}
-
-
-
+
+
+
Your copilot for
memecoin trading
-
- Catch rugs before they happen, execute trades at Solana
- speed, and automate the research pro traders do.
+
+ Catch rugs before they happen, execute trades at Solana speed, and
+ automate the research pro traders do.
-
+
-
-
-
-
@@ -61,7 +61,7 @@ export default function Landing() {
{/* Features */}
-
+
{[
{
icon: Shield,
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..8916905
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,21 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Input({ className, type, ...props }: React.ComponentProps<"input">) {
+ return (
+
+ )
+}
+
+export { Input }
diff --git a/src/components/ui/resizable.tsx b/src/components/ui/resizable.tsx
new file mode 100644
index 0000000..bbd25cc
--- /dev/null
+++ b/src/components/ui/resizable.tsx
@@ -0,0 +1,53 @@
+"use client"
+
+import { GripVerticalIcon } from "lucide-react"
+import * as ResizablePrimitive from "react-resizable-panels"
+
+import { cn } from "@/lib/utils"
+
+function ResizablePanelGroup({
+ className,
+ ...props
+}: ResizablePrimitive.GroupProps) {
+ return (
+
+ )
+}
+
+function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
+ return
+}
+
+function ResizableHandle({
+ withHandle,
+ className,
+ ...props
+}: ResizablePrimitive.SeparatorProps & {
+ withHandle?: boolean
+}) {
+ return (
+ div]:rotate-90",
+ className
+ )}
+ {...props}
+ >
+ {withHandle && (
+
+
+
+ )}
+
+ )
+}
+
+export { ResizableHandle, ResizablePanel, ResizablePanelGroup }
diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx
new file mode 100644
index 0000000..4c24b2a
--- /dev/null
+++ b/src/components/ui/separator.tsx
@@ -0,0 +1,28 @@
+"use client"
+
+import * as React from "react"
+import { Separator as SeparatorPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Separator({
+ className,
+ orientation = "horizontal",
+ decorative = true,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Separator }
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
new file mode 100644
index 0000000..7f73dcd
--- /dev/null
+++ b/src/components/ui/tabs.tsx
@@ -0,0 +1,91 @@
+"use client"
+
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { Tabs as TabsPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Tabs({
+ className,
+ orientation = "horizontal",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+const tabsListVariants = cva(
+ "rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
+ {
+ variants: {
+ variant: {
+ default: "bg-muted",
+ line: "gap-1 bg-transparent",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function TabsList({
+ className,
+ variant = "default",
+ ...props
+}: React.ComponentProps &
+ VariantProps) {
+ return (
+
+ )
+}
+
+function TabsTrigger({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function TabsContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
diff --git a/src/modules/tokens/types.ts b/src/modules/tokens/types.ts
index 8a3f3fb..3ef2e0d 100644
--- a/src/modules/tokens/types.ts
+++ b/src/modules/tokens/types.ts
@@ -3,7 +3,7 @@ export interface Token {
symbol: string;
name: string;
logoURI?: string;
- twitterPost: {
+ twitterPost?: {
link: string;
author: {
username: string;