diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/agents/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/agents/page.tsx deleted file mode 100644 index 58997ea..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/agents/page.tsx +++ /dev/null @@ -1,383 +0,0 @@ -"use client"; - -import { Bot, Plus, FileText, Wrench, Plug } from "lucide-react"; - -export default function AgentsPage() { - return ( -
-
-

- Agents -

-

- Manage agents and users -

-
- -
-
- -
-

- Create your first agent -

-

- Agents talk to your users, work with your data, and run on schedules – - all guided by clear instructions you define in your own words. -

- -
-
- - Suggested for your app - - -
- -
- {/* Blurred out cards */} -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - {/* Create from scratch */} -
-
- - Create from scratch - -
- -
-
-

- Create your own agent, defining how it works, responds, and - integrates with your data. -

-
-
-
- -
-

- How you can use agents -

-
-
-
- - - Guidelines - -
-

- Define the agent's behavior, knowledge, and AI model. -

-
-
-
- - - Tools - -
-

- Configure what tools and data the agent can access. -

-
-
-
- - - Connectors - -
-

- Connect the agent to Gmail, Calendar & more. -

-
-
-
-
-
- ); -} - -function RefreshIcon() { - return ( - - - - - - - ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/analytics/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/analytics/page.tsx deleted file mode 100644 index 5cd3740..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/analytics/page.tsx +++ /dev/null @@ -1,75 +0,0 @@ -"use client"; - -import { BarChart2 } from "lucide-react"; - -export default function AnalyticsPage() { - return ( -
-
-

- Analytics -

-

- Track traffic, usage, and events. -

-
- -
-
- -
-

- No data available yet -

-

- Once your app is live and receiving traffic, your analytics metrics - will appear here. -

-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/api/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/api/page.tsx deleted file mode 100644 index e346e9f..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/api/page.tsx +++ /dev/null @@ -1,158 +0,0 @@ -"use client"; - -import { Copy, Key } from "lucide-react"; - -export default function ApiPage() { - return ( -
-
-

- API & Webhooks -

-

- Connect external services to your application. -

-
- -
-

- REST API Endpoint -

-
-
- - https://api.steadfast-camp-core-flow.vibn.app/v1 - -
- -
-
- -
-
-

- API Keys -

- -
- -
-
- -
-
-
- Production Key -
-
- Created 2 days ago -
-
-
- pk_live_******************* -
-
-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/automations/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/automations/page.tsx deleted file mode 100644 index df38a45..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/automations/page.tsx +++ /dev/null @@ -1,109 +0,0 @@ -"use client"; - -export default function AutomationsPage() { - return ( -
-
-

- Automations -

-

- Build and manage automations in your app. -

-
- -
-
-
- - Automations - - - Builder+ - -
-
- -
-

- Unlock automations -

-

- To run automations in your app, you need backend functions enabled. - Upgrade to enable backend functions and start using automations. -

- -
-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/code/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/code/page.tsx deleted file mode 100644 index 3084a54..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/code/page.tsx +++ /dev/null @@ -1,442 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useParams } from "next/navigation"; -import { - Loader2, - AlertCircle, - ChevronDown, - ChevronRight, - Box, - Container, - CircleDot, -} from "lucide-react"; -import { GiteaFileTree } from "@/components/project/gitea-file-tree"; -import { GiteaFileViewer } from "@/components/project/gitea-file-viewer"; -import { useAnatomy, type Anatomy } from "@/components/project/use-anatomy"; - -/** - * Product tab — everything that makes up the thing being shipped. - * - * Left rail (top → bottom): - * 1. Codebases — Gitea repos, each tile expands inline into a file - * tree; clicking a file previews it on the right. - * 2. Images — Coolify services backed by an upstream Docker image - * (Twenty CRM, n8n…). Clicking shows image meta on the right. - * - * Dev containers do not appear here — they are the AI's workshop, not - * part of the product surface. - */ - -type Selection = { type: "file"; codebaseId: string; path: string } | null; - -export default function CodeTab() { - const params = useParams(); - const projectId = params.projectId as string; - const { anatomy, loading, error } = useAnatomy(projectId); - - const codebases = anatomy?.product.codebases ?? null; - const reason = anatomy?.codebasesReason; - - const [selection, setSelection] = useState(null); - - useEffect(() => { - setSelection(null); - }, [projectId]); - - const showLoading = loading && !anatomy; - - return ( -
-
- {/* ── Left rail ── */} -
- {showLoading && ( - - Loading… - - )} - {error && !showLoading && ( - - {error} - - )} - - {anatomy && ( - <> - {/* Code Files */} - - {codebases && codebases.length === 0 && ( - - {reason === "no_repo" ? ( - <> - No codebase yet.{" "} - - Try: "Start building my app" - - - ) : ( - <> - Repo is empty — push a first commit.{" "} - - Try: "Scaffold a Next.js app" - - - )} - - )} - {codebases?.map((cb) => { - return ( -
-
- - - - -
-
{cb.label}
- {cb.hint &&
{cb.hint}
} -
-
-
- - setSelection({ - type: "file", - codebaseId: cb.id, - path: p, - }) - } - /> -
-
- ); - })} -
- - )} -
- - {/* ── Right pane ── */} - -
-
- ); -} - -// ────────────────────────────────────────────────── -// Bits -// ────────────────────────────────────────────────── - -function RailGroup({ - title, - count, - children, -}: { - title: string; - count: number; - children: React.ReactNode; -}) { - return ( -
-
- {title} - {count} -
-
{children}
-
- ); -} - -function RailEmpty({ children }: { children: React.ReactNode }) { - return
{children}
; -} - -function DetailRow({ - label, - value, - dot, - href, -}: { - label: string; - value: string; - dot?: string; - href?: string; -}) { - return ( -
- {label} - - {dot && } - {href ? ( - - {value} - - ) : ( - value - )} - -
- ); -} - -function Inline({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -function Empty({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -// ────────────────────────────────────────────────── - -function paneHeading(s: Selection): string { - if (!s) return "Preview"; - if (s.type === "file") return `Preview · ${shortPath(s.path)}`; - return "Preview"; -} -function shortPath(p: string) { - const parts = p.split("/"); - if (parts.length <= 2) return p; - return ".../" + parts.slice(-2).join("/"); -} -function statusColor(status: string) { - const s = status.toLowerCase(); - if (s.includes("running") || s.includes("healthy")) return "#2e7d32"; - if (s.includes("starting") || s.includes("deploying")) return "#d4a04a"; - if (s.includes("exit") || s.includes("fail") || s.includes("unhealthy")) - return "#c5392b"; - return "#a09a90"; -} - -// ────────────────────────────────────────────────── -// Tokens -// ────────────────────────────────────────────────── - -const INK = { - ink: "#1a1a1a", - mid: "#5f5e5a", - muted: "#a09a90", - border: "#e8e4dc", - borderSoft: "#efebe1", - cardBg: "#fff", - fontSans: '"Outfit", "Inter", ui-sans-serif, sans-serif', -} as const; - -const pageWrap: React.CSSProperties = { - padding: "28px 48px 48px", - fontFamily: INK.fontSans, - color: INK.ink, -}; -const grid: React.CSSProperties = { - display: "grid", - gridTemplateColumns: "minmax(280px, 360px) minmax(0, 1fr)", - gap: 28, - maxWidth: 1400, - margin: "0 auto", - alignItems: "stretch", -}; -const leftCol: React.CSSProperties = { - minWidth: 0, - display: "flex", - flexDirection: "column", - gap: 18, -}; -const rightCol: React.CSSProperties = { - minWidth: 0, - display: "flex", - flexDirection: "column", -}; -const heading: React.CSSProperties = { - fontSize: "0.72rem", - fontWeight: 600, - letterSpacing: "0.12em", - textTransform: "uppercase", - color: INK.muted, - margin: "0 0 14px", -}; -const railGroup: React.CSSProperties = { - display: "flex", - flexDirection: "column", -}; -const railGroupHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - padding: "0 4px 8px", -}; -const railGroupTitle: React.CSSProperties = { - fontSize: "0.68rem", - fontWeight: 600, - letterSpacing: "0.12em", - textTransform: "uppercase", - color: INK.muted, -}; -const countPill: React.CSSProperties = { - fontSize: "0.7rem", - fontWeight: 600, - color: INK.mid, - padding: "1px 7px", - borderRadius: 999, - background: "#f3eee4", -}; -const railItems: React.CSSProperties = { - display: "flex", - flexDirection: "column", - gap: 10, -}; -const railEmpty: React.CSSProperties = { - padding: "10px 12px", - fontSize: "0.74rem", - color: INK.muted, - border: `1px dashed ${INK.borderSoft}`, - borderRadius: 8, - lineHeight: 1.6, -}; -const nudge: React.CSSProperties = { - display: "block", - marginTop: 6, - fontStyle: "normal", - background: "#f3eee4", - borderRadius: 4, - padding: "3px 8px", - fontSize: "0.72rem", - color: "#7a6a50", -}; -const flatTile: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 10, - width: "100%", - padding: "12px 14px", - background: INK.cardBg, - border: `1px solid ${INK.borderSoft}`, - borderRadius: 10, - cursor: "pointer", - font: "inherit", - color: "inherit", - transition: "border-color 0.12s, background 0.12s, box-shadow 0.12s", -}; -const codebaseTile: React.CSSProperties = { - background: INK.cardBg, - border: `1px solid ${INK.borderSoft}`, - borderRadius: 10, - overflow: "hidden", -}; -const tileHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 8, - width: "100%", - padding: "12px 14px", - background: "transparent", - border: "none", - font: "inherit", - color: "inherit", -}; -const tileLabel: React.CSSProperties = { - fontSize: "0.85rem", - fontWeight: 600, - color: INK.ink, - marginBottom: 2, -}; -const tileHint: React.CSSProperties = { - fontSize: "0.74rem", - color: INK.mid, - lineHeight: 1.4, -}; -const tileBody: React.CSSProperties = { - padding: "8px 10px 12px", - borderTop: `1px solid ${INK.borderSoft}`, -}; -const chevronCell: React.CSSProperties = { - width: 14, - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - flexShrink: 0, -}; -const panel: React.CSSProperties = { - background: INK.cardBg, - border: `1px solid ${INK.border}`, - borderRadius: 10, - padding: 16, - flex: 1, - minHeight: 0, - display: "flex", - flexDirection: "column", -}; -const detailRow: React.CSSProperties = { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - padding: "12px 4px", - borderBottom: `1px solid ${INK.borderSoft}`, -}; -const detailLabel: React.CSSProperties = { - fontSize: "0.72rem", - fontWeight: 600, - letterSpacing: "0.06em", - textTransform: "uppercase", - color: INK.muted, -}; -const detailValue: React.CSSProperties = { - fontSize: "0.85rem", - color: INK.ink, - display: "inline-flex", - alignItems: "center", -}; -const detailLink: React.CSSProperties = { - color: INK.ink, - textDecoration: "underline", -}; diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/data/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/data/page.tsx deleted file mode 100644 index d03e2de..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/data/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { redirect } from "next/navigation"; - -export default async function DataPage({ - params, -}: { - params: Promise<{ workspace: string; projectId: string }>; -}) { - const { workspace, projectId } = await params; - redirect(`/${workspace}/project/${projectId}/data/tables`); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/data/tables/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/data/tables/page.tsx deleted file mode 100644 index f6c7f7d..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/data/tables/page.tsx +++ /dev/null @@ -1,372 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useParams, useSearchParams } from "next/navigation"; -import { - Loader2, - AlertCircle, - ChevronDown, - ChevronRight, - Database, - CircleDot, -} from "lucide-react"; -import { DatabaseTableTree } from "@/components/project/database-table-tree"; -import { TableViewer } from "@/components/project/table-viewer"; -import { useAnatomy } from "@/components/project/use-anatomy"; - -type Selection = { - kind: "table"; - dbUuid: string; - schema: string; - name: string; -} | null; - -export default function DataTablesPage() { - const params = useParams(); - const searchParams = useSearchParams(); - const projectId = params.projectId as string; - const targetDbId = searchParams.get("db"); - - const { anatomy, loading, error } = useAnatomy(projectId); - - const databases = anatomy?.infrastructure?.databases ?? []; - - // If targetDbId is in the URL, only show that database. - // Otherwise, default to the first database in the list if available. - const activeDbId = - targetDbId || (databases.length > 0 ? databases[0].uuid : null); - const activeDatabases = databases.filter((db) => db.uuid === activeDbId); - - const [selection, setSelection] = useState(null); - - useEffect(() => { - setSelection(null); - }, [projectId, targetDbId]); - - const showLoading = loading && !anatomy; - - return ( -
-
- {/* ── Left rail ── */} -
- {showLoading && ( - - Loading… - - )} - {error && !showLoading && ( - - {error} - - )} - - {anatomy && ( - - {activeDatabases.length === 0 && ( - - No databases yet. - - Try: "Add a Postgres database to my project" - - - )} - {activeDatabases.map((db) => { - return ( -
-
- - - - -
-
{db.name}
-
{db.type}
-
- -
-
- - setSelection({ - kind: "table", - dbUuid: db.uuid, - schema, - name, - }) - } - /> -
-
- ); - })} -
- )} -
- - {/* ── Right pane ── */} - -
-
- ); -} - -// ────────────────────────────────────────────────── -// Bits -// ────────────────────────────────────────────────── - -function RailGroup({ - title, - count, - children, -}: { - title: string; - count: number; - children: React.ReactNode; -}) { - return ( -
-
- {title} - {count} -
-
{children}
-
- ); -} - -function RailEmpty({ children }: { children: React.ReactNode }) { - return
{children}
; -} - -function Inline({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -function Empty({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -function paneHeading(s: Selection): string { - if (!s) return "Preview"; - if (s.kind === "table") - return `Preview · ${s.schema === "public" ? s.name : `${s.schema}.${s.name}`}`; - return "Preview"; -} - -function statusColor(status: string) { - const s = (status ?? "").toLowerCase(); - if (s.includes("running") || s.includes("healthy")) return "#2e7d32"; - if (s.includes("starting") || s.includes("deploying")) return "#d4a04a"; - if (s.includes("exit") || s.includes("fail") || s.includes("unhealthy")) - return "#c5392b"; - return "#a09a90"; -} - -// ────────────────────────────────────────────────── -// Tokens -// ────────────────────────────────────────────────── - -const INK = { - ink: "#1a1a1a", - mid: "#5f5e5a", - muted: "#a09a90", - border: "#e8e4dc", - borderSoft: "#efebe1", - cardBg: "#fff", - fontSans: '"Outfit", "Inter", ui-sans-serif, sans-serif', -} as const; - -const pageWrap: React.CSSProperties = { - padding: "28px 48px 48px", - fontFamily: INK.fontSans, - color: INK.ink, -}; -const grid: React.CSSProperties = { - display: "grid", - gridTemplateColumns: "minmax(280px, 360px) minmax(0, 1fr)", - gap: 28, - maxWidth: 1400, - margin: "0 auto", - alignItems: "stretch", -}; -const leftCol: React.CSSProperties = { - minWidth: 0, - display: "flex", - flexDirection: "column", - gap: 18, -}; -const rightCol: React.CSSProperties = { - minWidth: 0, - display: "flex", - flexDirection: "column", -}; -const heading: React.CSSProperties = { - fontSize: "0.72rem", - fontWeight: 600, - letterSpacing: "0.12em", - textTransform: "uppercase", - color: INK.muted, - margin: "0 0 14px", -}; -const railGroup: React.CSSProperties = { - display: "flex", - flexDirection: "column", -}; -const railGroupHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - padding: "0 4px 8px", -}; -const railGroupTitle: React.CSSProperties = { - fontSize: "0.68rem", - fontWeight: 600, - letterSpacing: "0.12em", - textTransform: "uppercase", - color: INK.muted, -}; -const countPill: React.CSSProperties = { - fontSize: "0.7rem", - fontWeight: 600, - color: INK.mid, - padding: "1px 7px", - borderRadius: 999, - background: "#f3eee4", -}; -const railItems: React.CSSProperties = { - display: "flex", - flexDirection: "column", - gap: 10, -}; -const railEmpty: React.CSSProperties = { - padding: "10px 12px", - fontSize: "0.74rem", - color: INK.muted, - border: `1px dashed ${INK.borderSoft}`, - borderRadius: 8, - lineHeight: 1.6, -}; -const nudge: React.CSSProperties = { - display: "block", - marginTop: 6, - fontStyle: "normal", - background: "#f3eee4", - borderRadius: 4, - padding: "3px 8px", - fontSize: "0.72rem", - color: "#7a6a50", -}; -const codebaseTile: React.CSSProperties = { - background: INK.cardBg, - border: `1px solid ${INK.borderSoft}`, - borderRadius: 10, - overflow: "hidden", -}; -const tileHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 8, - width: "100%", - padding: "12px 14px", - background: "transparent", - border: "none", - font: "inherit", - color: "inherit", -}; -const tileLabel: React.CSSProperties = { - fontSize: "0.85rem", - fontWeight: 600, - color: INK.ink, - marginBottom: 2, -}; -const tileHint: React.CSSProperties = { - fontSize: "0.74rem", - color: INK.mid, - lineHeight: 1.4, - textTransform: "capitalize", -}; -const tileBody: React.CSSProperties = { - padding: "8px 10px 12px", - borderTop: `1px solid ${INK.borderSoft}`, -}; -const chevronCell: React.CSSProperties = { - width: 14, - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - flexShrink: 0, -}; -const panel: React.CSSProperties = { - background: INK.cardBg, - border: `1px solid ${INK.border}`, - borderRadius: 10, - padding: 16, - flex: 1, - minHeight: 0, - display: "flex", - flexDirection: "column", -}; diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/domains/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/domains/page.tsx deleted file mode 100644 index f3fd1dd..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/domains/page.tsx +++ /dev/null @@ -1,227 +0,0 @@ -"use client"; - -import { Copy } from "lucide-react"; - -export default function DomainsPage() { - return ( -
-
-

- Domains -

-

- Buy, connect and manage your domains.{" "} - - Learn more - -

-
- -
-
-
-

- Built-in URL -

-
- -
-
-
- - steadfast-camp-core-flow - - .vibn.app -
- -
-
- -

- Custom domains -

-
-

- Want to use your domain? -

-

- Custom domains are available on our Builder plan and above. Upgrade to - continue working to this app. -

- -
- -
-
-
-
-

- Email domain -

- - Builder+ - -
-
- no-reply@notifications.vibn.app -
-
- Sender Name: App -
-
- -
-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/integrations/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/integrations/page.tsx deleted file mode 100644 index 80f0be1..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/integrations/page.tsx +++ /dev/null @@ -1,223 +0,0 @@ -"use client"; - -import { Diamond } from "lucide-react"; - -export default function IntegrationsPage() { - return ( -
-
-

- Integrations -

-
- -
-
- - -
- -
-
-
-
-
Stripe
-
- Sell products or subscriptions and get paid online. -
-
- -
-
- -

- Connectors -

-

- Connect your app to popular services. -

- -
- {[1, 2, 3, 4].map((i) => ( -
-
-
-
-
- Connector {i} -
-
- Connect with external service for app data. -
-
- ))} -
-
- -
-
-
- -
-

- Unlock this feature -

-

- This feature is only available on the Builder plan or higher. - Upgrade to continue working without limits. -

- -
-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx deleted file mode 100644 index 6b2de0e..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx +++ /dev/null @@ -1,98 +0,0 @@ -"use client"; - -import { Search } from "lucide-react"; - -export default function LogsPage() { - return ( -
-
-

- Logs -

-

- View application and server logs. -

-
- -
-
-
- - -
-
-
-
- 14:32:01 - [info] - Server started on port 3000 -
-
- 14:32:05 - [info] - Database connected successfully -
-
- 14:45:12 - [http] - GET /api/users 200 OK - 45ms -
-
-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/page.tsx deleted file mode 100644 index ec0ee46..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { redirect } from "next/navigation"; - -export default async function MarketingPage({ params }: { params: Promise<{ workspace: string; projectId: string }> }) { - const { workspace, projectId } = await params; - redirect(`/${workspace}/project/${projectId}/marketing/seo`); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/seo/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/seo/page.tsx deleted file mode 100644 index 28dc940..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/seo/page.tsx +++ /dev/null @@ -1,366 +0,0 @@ -"use client"; - -import { ListFilter } from "lucide-react"; - -export default function SeoPage() { - return ( -
-
-
-

- SEO & GEO -

-

- Improve how your app appears in search results and AI answers. -

-
-
- - Enable SEO for this app - -
-
-
-
-
- -
- - - -
- -
-
- -
-

- Run an SEO & GEO scan -

-

- Scan your app for SEO basics and GEO details. Get a prioritized - checklist to fix issues in minutes. -

- -
- -
-
-
-
- AI Assistant Discovery -
-
- Help AI search engines understand and recommend your app -
-
-
-
-
-
- -
-
-
- Generate robots.txt -
-
- Off: serve your deployed public/robots.txt if shipped, otherwise - return 404. -
-
-
-
-
-
- -
-
-
- Generate sitemap.xml -
-
- Off: serve your deployed public/sitemap.xml if shipped, otherwise - return 404. -
-
-
-
-
-
- -
-
-
- Auto-generate per-page breadcrumbs -
-
- Build a fresh BreadcrumbList for each route instead of using the - same persisted list site-wide. Turn off if you hand crafted your - breadcrumb schema and want it served verbatim. -
-
-
-
-
-
-
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/social/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/social/page.tsx deleted file mode 100644 index bce3688..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/marketing/social/page.tsx +++ /dev/null @@ -1,89 +0,0 @@ -"use client"; - -import { Share2 } from "lucide-react"; - -export default function SocialPage() { - return ( -
-
-

- Social Content -

-

- Manage social sharing campaigns and meta tags. -

-
- -
-
- -
-

- Social Campaign Manager -

-

- Automatically generate and schedule social media content across - platforms based on your app's pages. -

- -
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/overview/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/overview/page.tsx deleted file mode 100644 index 126d5cb..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/overview/page.tsx +++ /dev/null @@ -1,683 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useParams } from "next/navigation"; -import { - Loader2, - AlertCircle, - ExternalLink, - Globe, - RefreshCw, - CircleDot, - ChevronDown, - ChevronRight, - Copy, - Check, - Terminal, - Server, -} from "lucide-react"; -import { useAnatomy, type Anatomy } from "@/components/project/use-anatomy"; - -/** - * Hosting tab — user-facing: "Is my thing live? How do I reach it?" - * - * One endpoint = one card. Each card shows: - * - Live URL (open in new tab) - * - Status dot + plain-language status - * - Redeploy button - * - Domain(s) list - * - Last build (time + status) - * - Expandable recent logs - * - * No master-detail split — with 1-3 services the overhead isn't worth it. - * Previews (dev server URLs) shown below in a secondary section. - */ - -// ────────────────────────────────────────────────── -// Types -// ────────────────────────────────────────────────── - -type LiveItem = Anatomy["hosting"]["live"][number]; -type Preview = Anatomy["hosting"]["previews"][number]; - -// ────────────────────────────────────────────────── -// Main component -// ────────────────────────────────────────────────── - -export default function OverviewTab() { - const params = useParams(); - const projectId = params.projectId as string; - const { anatomy, loading, error } = useAnatomy(projectId, { pollMs: 8000 }); - const showLoading = loading && !anatomy; - - return ( -
- {showLoading && ( -
- - - Loading… - -
- )} - {error && !showLoading && ( -
- - {error} -
- )} - - {anatomy && ( - <> - {/* ── Live endpoints ── */} -
- - {anatomy.hosting.live.length === 0 ? ( - } - title="Nothing deployed yet" - hint="Ask the AI to deploy your app and it will appear here." - promptSuggestion="Deploy my app to production" - /> - ) : ( -
- {anatomy.hosting.live.map((item) => ( - - ))} -
- )} -
- - {/* ── Previews ── */} - {anatomy.hosting.previews.length > 0 && ( -
- -
- {anatomy.hosting.previews.map((p) => ( - - ))} -
-
- )} - - )} -
- ); -} - -// ────────────────────────────────────────────────── -// Live card -// ────────────────────────────────────────────────── - -function LiveCard({ item, projectId }: { item: LiveItem; projectId: string }) { - const [deploying, setDeploying] = useState(false); - const [logsOpen, setLogsOpen] = useState(false); - const [logs, setLogs] = useState(null); - const [logsLoading, setLogsLoading] = useState(false); - const [copied, setCopied] = useState(false); - - const primaryUrl = item.fqdn ? `https://${item.fqdn}` : null; - const phase = classifyPhase(item.status); - const { color: statusColor, label: statusLabel } = phaseDisplay(phase, item); - - const redeploy = async () => { - if (deploying) return; - setDeploying(true); - try { - await fetch(`/api/mcp`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - action: "apps.deploy", - params: { uuid: item.uuid, projectId }, - }), - }); - } finally { - setTimeout(() => setDeploying(false), 3000); - } - }; - - const openLogs = async () => { - if (!logsOpen) { - setLogsOpen(true); - setLogsLoading(true); - try { - const r = await fetch(`/api/mcp`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - action: "apps.logs", - params: { uuid: item.uuid, lines: 60 }, - }), - }); - const d = await r.json(); - setLogs( - typeof d.result === "string" - ? d.result - : JSON.stringify(d.result ?? d.error, null, 2), - ); - } catch { - setLogs("Failed to load logs."); - } finally { - setLogsLoading(false); - } - } else { - setLogsOpen(false); - } - }; - - const copyUrl = () => { - if (!primaryUrl) return; - navigator.clipboard.writeText(primaryUrl); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - return ( -
- {/* ── Card header ── */} -
-
- - {item.name} - - {item.source === "repo" ? "built" : "image"} - -
-
- -
-
- - {/* ── Status line ── */} -
- - {statusLabel} - - {item.lastBuild && ( - - · Last build {item.lastBuild.status}{" "} - {formatRelative(item.lastBuild.finishedAt)} - - )} -
- - {/* ── Live URL ── */} - {primaryUrl ? ( -
- - - {primaryUrl} - - - -
- ) : ( -
- - - No domain attached — ask the AI to add one. - -
- )} - - {/* ── Extra domains ── */} - {item.domains.length > 1 && ( -
- {item.domains.slice(1).map((d) => ( - - {d}{" "} - - - ))} -
- )} - - {/* ── Logs toggle ── */} -
- - - {logsOpen && ( -
- {logsLoading ? ( - - Loading… - - ) : ( -
{logs || "(no logs)"}
- )} -
- )} -
-
- ); -} - -// ────────────────────────────────────────────────── -// Preview row -// ────────────────────────────────────────────────── - -function PreviewRow({ preview }: { preview: Preview }) { - const running = preview.state === "running"; - return ( -
-
- - - {preview.name} - - - port {preview.port} - - {preview.url && running && ( - - )} -
-
- ); -} - -// ────────────────────────────────────────────────── -// Helpers -// ────────────────────────────────────────────────── - -type Phase = "up" | "deploying" | "down" | "unknown"; - -function classifyPhase(status: string | undefined): Phase { - const s = (status ?? "").toLowerCase(); - if (!s || s === "unknown") return "unknown"; - if (/^(running|healthy)/.test(s)) return "up"; - if ( - /^(starting|restarting|created|deploying|building|in_progress|queued|paused)/.test( - s, - ) - ) - return "deploying"; - if (/^(exited|dead|failed|stopped|unhealthy|error)/.test(s)) return "down"; - return "unknown"; -} - -function phaseDisplay( - phase: Phase, - item: LiveItem, -): { color: string; label: string } { - if (item.inFlightBuild) - return { - color: AMBER, - label: `Deploying (${item.inFlightBuild.status ?? "in progress"})`, - }; - switch (phase) { - case "up": - return { color: GREEN, label: "Live" }; - case "deploying": - return { color: AMBER, label: "Starting…" }; - case "down": - return { color: DANGER, label: "Down" }; - default: - return { color: INK.muted, label: "Unknown" }; - } -} - -function formatRelative(iso: string | undefined) { - if (!iso) return ""; - const ms = Date.now() - new Date(iso).getTime(); - if (Number.isNaN(ms)) return ""; - const min = Math.floor(ms / 60_000); - if (min < 1) return "just now"; - if (min < 60) return `${min}m ago`; - const hr = Math.floor(min / 60); - if (hr < 24) return `${hr}h ago`; - return `${Math.floor(hr / 24)}d ago`; -} - -// ────────────────────────────────────────────────── -// Sub-components -// ────────────────────────────────────────────────── - -function SectionHeader({ title, count }: { title: string; count: number }) { - return ( -
- {title} - {count} -
- ); -} - -function EmptySection({ - icon, - title, - hint, - promptSuggestion, -}: { - icon: React.ReactNode; - title: string; - hint: string; - promptSuggestion?: string; -}) { - return ( -
-
{icon}
-
- {title} -
-
- {hint} -
- {promptSuggestion && ( -
- - Try asking: - - - "{promptSuggestion}" - -
- )} -
- ); -} - -// ────────────────────────────────────────────────── -// Tokens -// ────────────────────────────────────────────────── - -const INK = { - ink: "#1a1a1a", - mid: "#5f5e5a", - muted: "#a09a90", - border: "#e8e4dc", - borderSoft: "#efebe1", - cardBg: "#fff", - fontSans: '"Outfit", "Inter", ui-sans-serif, sans-serif', -} as const; -const GREEN = "#10b981"; -const AMBER = "#f59e0b"; -const DANGER = "#ef4444"; - -// ────────────────────────────────────────────────── -// Styles -// ────────────────────────────────────────────────── - -const pageWrap: React.CSSProperties = { - padding: "28px 48px 64px", - fontFamily: INK.fontSans, - color: INK.ink, - maxWidth: 860, -}; -const centeredMsg: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 10, - padding: "24px 0", -}; -const sectionHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 8, - marginBottom: 14, -}; -const sectionTitle: React.CSSProperties = { - fontSize: "0.68rem", - fontWeight: 700, - letterSpacing: "0.12em", - textTransform: "uppercase", - color: INK.muted, -}; -const countPill: React.CSSProperties = { - fontSize: "0.7rem", - fontWeight: 600, - color: INK.mid, - padding: "1px 7px", - borderRadius: 999, - background: "#f3eee4", -}; -const card: React.CSSProperties = { - background: INK.cardBg, - border: `1px solid ${INK.border}`, - borderRadius: 10, - padding: "18px 20px", -}; -const cardHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - gap: 12, - marginBottom: 6, -}; -const cardTitle: React.CSSProperties = { - fontSize: "0.95rem", - fontWeight: 700, - color: INK.ink, -}; -const statusLine: React.CSSProperties = { - fontSize: "0.8rem", - color: INK.mid, - marginBottom: 12, - display: "flex", - alignItems: "center", - gap: 6, - flexWrap: "wrap", -}; -const urlRow: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 8, - background: "#f8f5f0", - borderRadius: 6, - padding: "8px 12px", - marginBottom: 2, -}; -const urlLink: React.CSSProperties = { - fontSize: "0.85rem", - color: INK.ink, - textDecoration: "none", - flex: 1, - minWidth: 0, - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - display: "inline-flex", - alignItems: "center", - gap: 4, -}; -const actionBtn: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - gap: 6, - padding: "6px 12px", - border: `1px solid ${INK.border}`, - borderRadius: 6, - background: "#fff", - cursor: "pointer", - font: "inherit", - fontSize: "0.78rem", - fontWeight: 600, - color: INK.mid, - transition: "background 0.1s, border-color 0.1s", -}; -const iconBtn: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - width: 26, - height: 26, - border: "none", - background: "transparent", - cursor: "pointer", - color: INK.muted, - borderRadius: 4, - flexShrink: 0, -}; -const logsToggleBtn: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - gap: 6, - fontSize: "0.75rem", - fontWeight: 600, - color: INK.mid, - background: "none", - border: "none", - cursor: "pointer", - font: "inherit", - padding: 0, -}; -const logsBox: React.CSSProperties = { - marginTop: 10, - background: "#1a1a1a", - borderRadius: 6, - padding: "12px 14px", - maxHeight: 320, - overflowY: "auto", -}; -const logsPre: React.CSSProperties = { - margin: 0, - fontFamily: "ui-monospace, monospace", - fontSize: "0.72rem", - color: "#d4d0c8", - lineHeight: 1.6, - whiteSpace: "pre-wrap", - wordBreak: "break-all", -}; - -const emptyBox: React.CSSProperties = { - border: `1px dashed ${INK.border}`, - borderRadius: 10, - padding: "36px 28px", - textAlign: "center", - display: "flex", - flexDirection: "column", - alignItems: "center", -}; -const promptChip: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - background: "#f3eee4", - borderRadius: 6, - padding: "6px 12px", - fontSize: "0.8rem", -}; - -function sourcePill(source: "repo" | "image"): React.CSSProperties { - const isRepo = source === "repo"; - return { - fontSize: "0.62rem", - fontWeight: 700, - letterSpacing: "0.08em", - textTransform: "uppercase", - color: isRepo ? "#2e6d2e" : "#3b5a78", - background: isRepo ? "#eaf3e8" : "#e9eff5", - padding: "1px 6px", - borderRadius: 4, - flexShrink: 0, - }; -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/plan/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/plan/page.tsx index 094b446..43941b9 100644 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/plan/page.tsx +++ b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/plan/page.tsx @@ -121,7 +121,7 @@ export default function PlanTab() {
} selectedId={selectedId} onClick={setSelectedId} @@ -282,7 +282,7 @@ function ObjectivePanel({
-

Product Brief

+

Project Objective

The high-level business case and elevator pitch.

diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/security/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/security/page.tsx deleted file mode 100644 index a76f5e7..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/security/page.tsx +++ /dev/null @@ -1,119 +0,0 @@ -"use client"; - -import { Shield, Settings } from "lucide-react"; - -export default function SecurityPage() { - return ( -
-
-
-

- Security -

-

- Manage your permissions and security rules.{" "} - - Learn more - -

-
- -
- -
-
- -
-

- Check the security of your app -

-

- Review your configuration, identify potential risks, and learn how to - strengthen your app's protection -

- -
-
- ); -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/services/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/services/page.tsx deleted file mode 100644 index ab25c71..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/services/page.tsx +++ /dev/null @@ -1,696 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useParams } from "next/navigation"; -import { - Loader2, - AlertCircle, - ExternalLink, - Globe, - RefreshCw, - CircleDot, - ChevronDown, - ChevronRight, - Copy, - Check, - Terminal, - Server, -} from "lucide-react"; -import { useAnatomy, type Anatomy } from "@/components/project/use-anatomy"; - -/** - * Hosting tab — user-facing: "Is my thing live? How do I reach it?" - * - * One endpoint = one card. Each card shows: - * - Live URL (open in new tab) - * - Status dot + plain-language status - * - Redeploy button - * - Domain(s) list - * - Last build (time + status) - * - Expandable recent logs - * - * No master-detail split — with 1-3 services the overhead isn't worth it. - * Previews (dev server URLs) shown below in a secondary section. - */ - -// ────────────────────────────────────────────────── -// Types -// ────────────────────────────────────────────────── - -type LiveItem = Anatomy["hosting"]["live"][number]; -type Preview = Anatomy["hosting"]["previews"][number]; - -// ────────────────────────────────────────────────── -// Main component -// ────────────────────────────────────────────────── - -export default function ServicesPage() { - const params = useParams(); - const projectId = params.projectId as string; - const { anatomy, loading, error } = useAnatomy(projectId, { pollMs: 8000 }); - const showLoading = loading && !anatomy; - - return ( -
- {showLoading && ( -
- - - Loading… - -
- )} - {error && !showLoading && ( -
- - {error} -
- )} - - {anatomy && ( - <> - {/* ── Live endpoints ── */} -
- - {anatomy.hosting.live.length === 0 ? ( - } - title="Nothing deployed yet" - hint="Ask the AI to deploy your app and it will appear here." - promptSuggestion="Deploy my app to production" - /> - ) : ( -
- {anatomy.hosting.live.map((item) => ( - - ))} -
- )} -
- - {/* ── Previews ── */} - {anatomy.hosting.previews.length > 0 && ( -
- -
- {anatomy.hosting.previews.map((p) => ( - - ))} -
-
- )} - - )} -
- ); -} - -// ────────────────────────────────────────────────── -// Live card -// ────────────────────────────────────────────────── - -function LiveCard({ item, projectId }: { item: LiveItem; projectId: string }) { - const [deploying, setDeploying] = useState(false); - const [logsOpen, setLogsOpen] = useState(false); - const [logs, setLogs] = useState(null); - const [logsLoading, setLogsLoading] = useState(false); - const [copied, setCopied] = useState(false); - - const primaryUrl = item.fqdn ? `https://${item.fqdn}` : null; - const phase = classifyPhase(item.status); - const { color: statusColor, label: statusLabel } = phaseDisplay(phase, item); - - const redeploy = async () => { - if (deploying) return; - setDeploying(true); - try { - await fetch(`/api/mcp`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - action: "apps.deploy", - params: { uuid: item.uuid, projectId }, - }), - }); - } finally { - setTimeout(() => setDeploying(false), 3000); - } - }; - - const openLogs = async () => { - if (!logsOpen) { - setLogsOpen(true); - setLogsLoading(true); - try { - const r = await fetch(`/api/mcp`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - action: "apps.logs", - params: { uuid: item.uuid, lines: 60 }, - }), - }); - const d = await r.json(); - setLogs( - typeof d.result === "string" - ? d.result - : JSON.stringify(d.result ?? d.error, null, 2), - ); - } catch { - setLogs("Failed to load logs."); - } finally { - setLogsLoading(false); - } - } else { - setLogsOpen(false); - } - }; - - const copyUrl = () => { - if (!primaryUrl) return; - navigator.clipboard.writeText(primaryUrl); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - return ( -
- {/* ── Card header ── */} -
-
- - {item.name} - - {item.source === "repo" ? "built" : "image"} - -
-
- -
-
- - {/* ── Status line ── */} -
- - {statusLabel} - - {item.lastBuild && ( - - · Last build {item.lastBuild.status}{" "} - {formatRelative(item.lastBuild.finishedAt)} - - )} -
- - {/* ── Live URL ── */} - {primaryUrl ? ( -
- - - {primaryUrl} - - - -
- ) : ( -
- - - No domain attached — ask the AI to add one. - -
- )} - - {/* ── Extra domains ── */} - {item.domains.length > 1 && ( -
- {item.domains.slice(1).map((d) => ( - - {d}{" "} - - - ))} -
- )} - - {/* ── Logs toggle ── */} -
- - - {logsOpen && ( -
- {logsLoading ? ( - - Loading… - - ) : ( -
{logs || "(no logs)"}
- )} -
- )} -
-
- ); -} - -// ────────────────────────────────────────────────── -// Preview row -// ────────────────────────────────────────────────── - -function PreviewRow({ preview }: { preview: Preview }) { - const running = preview.state === "running"; - return ( -
-
- - - {preview.name} - - - port {preview.port} - - - {preview.state} - - {preview.url && running && ( - - )} -
-
- ); -} - -// ────────────────────────────────────────────────── -// Helpers -// ────────────────────────────────────────────────── - -type Phase = "up" | "deploying" | "down" | "unknown"; - -function classifyPhase(status: string | undefined): Phase { - const s = (status ?? "").toLowerCase(); - if (!s || s === "unknown") return "unknown"; - if (/^(running|healthy)/.test(s)) return "up"; - if ( - /^(starting|restarting|created|deploying|building|in_progress|queued|paused)/.test( - s, - ) - ) - return "deploying"; - if (/^(exited|dead|failed|stopped|unhealthy|error)/.test(s)) return "down"; - return "unknown"; -} - -function phaseDisplay( - phase: Phase, - item: LiveItem, -): { color: string; label: string } { - if (item.inFlightBuild) - return { - color: AMBER, - label: `Deploying (${item.inFlightBuild.status ?? "in progress"})`, - }; - switch (phase) { - case "up": - return { color: GREEN, label: "Live" }; - case "deploying": - return { color: AMBER, label: "Starting…" }; - case "down": - return { color: DANGER, label: "Down" }; - default: - return { color: INK.muted, label: "Unknown" }; - } -} - -function formatRelative(iso: string | undefined) { - if (!iso) return ""; - const ms = Date.now() - new Date(iso).getTime(); - if (Number.isNaN(ms)) return ""; - const min = Math.floor(ms / 60_000); - if (min < 1) return "just now"; - if (min < 60) return `${min}m ago`; - const hr = Math.floor(min / 60); - if (hr < 24) return `${hr}h ago`; - return `${Math.floor(hr / 24)}d ago`; -} - -// ────────────────────────────────────────────────── -// Sub-components -// ────────────────────────────────────────────────── - -function SectionHeader({ title, count }: { title: string; count: number }) { - return ( -
- {title} - {count} -
- ); -} - -function EmptySection({ - icon, - title, - hint, - promptSuggestion, -}: { - icon: React.ReactNode; - title: string; - hint: string; - promptSuggestion?: string; -}) { - return ( -
-
{icon}
-
- {title} -
-
- {hint} -
- {promptSuggestion && ( -
- - Try asking: - - - "{promptSuggestion}" - -
- )} -
- ); -} - -// ────────────────────────────────────────────────── -// Tokens -// ────────────────────────────────────────────────── - -const INK = { - ink: "#1a1a1a", - mid: "#5f5e5a", - muted: "#a09a90", - border: "#e8e4dc", - borderSoft: "#efebe1", - cardBg: "#fff", - fontSans: '"Outfit", "Inter", ui-sans-serif, sans-serif', -} as const; -const GREEN = "#10b981"; -const AMBER = "#f59e0b"; -const DANGER = "#ef4444"; - -// ────────────────────────────────────────────────── -// Styles -// ────────────────────────────────────────────────── - -const pageWrap: React.CSSProperties = { - padding: "28px 48px 64px", - fontFamily: INK.fontSans, - color: INK.ink, - maxWidth: 860, -}; -const centeredMsg: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 10, - padding: "24px 0", -}; -const sectionHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 8, - marginBottom: 14, -}; -const sectionTitle: React.CSSProperties = { - fontSize: "0.68rem", - fontWeight: 700, - letterSpacing: "0.12em", - textTransform: "uppercase", - color: INK.muted, -}; -const countPill: React.CSSProperties = { - fontSize: "0.7rem", - fontWeight: 600, - color: INK.mid, - padding: "1px 7px", - borderRadius: 999, - background: "#f3eee4", -}; -const card: React.CSSProperties = { - background: INK.cardBg, - border: `1px solid ${INK.border}`, - borderRadius: 10, - padding: "18px 20px", -}; -const cardHeader: React.CSSProperties = { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - gap: 12, - marginBottom: 6, -}; -const cardTitle: React.CSSProperties = { - fontSize: "0.95rem", - fontWeight: 700, - color: INK.ink, -}; -const statusLine: React.CSSProperties = { - fontSize: "0.8rem", - color: INK.mid, - marginBottom: 12, - display: "flex", - alignItems: "center", - gap: 6, - flexWrap: "wrap", -}; -const urlRow: React.CSSProperties = { - display: "flex", - alignItems: "center", - gap: 8, - background: "#f8f5f0", - borderRadius: 6, - padding: "8px 12px", - marginBottom: 2, -}; -const urlLink: React.CSSProperties = { - fontSize: "0.85rem", - color: INK.ink, - textDecoration: "none", - flex: 1, - minWidth: 0, - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - display: "inline-flex", - alignItems: "center", - gap: 4, -}; -const actionBtn: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - gap: 6, - padding: "6px 12px", - border: `1px solid ${INK.border}`, - borderRadius: 6, - background: "#fff", - cursor: "pointer", - font: "inherit", - fontSize: "0.78rem", - fontWeight: 600, - color: INK.mid, - transition: "background 0.1s, border-color 0.1s", -}; -const iconBtn: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - width: 26, - height: 26, - border: "none", - background: "transparent", - cursor: "pointer", - color: INK.muted, - borderRadius: 4, - flexShrink: 0, -}; -const logsToggleBtn: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - gap: 6, - fontSize: "0.75rem", - fontWeight: 600, - color: INK.mid, - background: "none", - border: "none", - cursor: "pointer", - font: "inherit", - padding: 0, -}; -const logsBox: React.CSSProperties = { - marginTop: 10, - background: "#1a1a1a", - borderRadius: 6, - padding: "12px 14px", - maxHeight: 320, - overflowY: "auto", -}; -const logsPre: React.CSSProperties = { - margin: 0, - fontFamily: "ui-monospace, monospace", - fontSize: "0.72rem", - color: "#d4d0c8", - lineHeight: 1.6, - whiteSpace: "pre-wrap", - wordBreak: "break-all", -}; - -const emptyBox: React.CSSProperties = { - border: `1px dashed ${INK.border}`, - borderRadius: 10, - padding: "36px 28px", - textAlign: "center", - display: "flex", - flexDirection: "column", - alignItems: "center", -}; -const promptChip: React.CSSProperties = { - display: "inline-flex", - alignItems: "center", - background: "#f3eee4", - borderRadius: 6, - padding: "6px 12px", - fontSize: "0.8rem", -}; - -function sourcePill(source: "repo" | "image"): React.CSSProperties { - const isRepo = source === "repo"; - return { - fontSize: "0.62rem", - fontWeight: 700, - letterSpacing: "0.08em", - textTransform: "uppercase", - color: isRepo ? "#2e6d2e" : "#3b5a78", - background: isRepo ? "#eaf3e8" : "#e9eff5", - padding: "1px 6px", - borderRadius: 4, - flexShrink: 0, - }; -} diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/settings/app/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/settings/app/page.tsx deleted file mode 100644 index 438e8be..0000000 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/settings/app/page.tsx +++ /dev/null @@ -1,101 +0,0 @@ -"use client"; - -export default function AppSettingsPage() { - return ( -
-
-

- App Settings -

-

- General configuration for your application. -

-
- -
-
- - -
-
- -