diff --git a/.env.example b/.env.example index 0068853..ec333e6 100644 --- a/.env.example +++ b/.env.example @@ -101,3 +101,19 @@ STRIPE_SECRET_KEY= STRIPE_WEBHOOK_SECRET= STRIPE_PRO_PRICE_ID= DROPS_TEAM_INVITE_SECRET= + +# Managed Platform V4. These values select external adapters; setting a name never +# marks the adapter healthy by itself. Production UI waits for provider evidence. +# DROPS_MANAGED_DATA_PROVIDER accepts only "d1" or "postgres" in adapter wiring. +DROPS_MANAGED_DATA_PROVIDER= +DATABASE_URL= +DROPS_COLLABORATION_TRANSPORT_URL= + +# Generic enterprise OIDC. Values stay server-only and external login remains +# disabled until discovery, callback, nonce, PKCE, domain and group checks pass. +DROPS_ENTERPRISE_OIDC_ISSUER= +DROPS_ENTERPRISE_OIDC_CLIENT_ID= +DROPS_ENTERPRISE_OIDC_CLIENT_SECRET= + +# Separate 32+ byte material for a future durable append-only audit adapter. +DROPS_ENTERPRISE_AUDIT_SIGNING_KEY= diff --git a/app/api/platform/capabilities/route.ts b/app/api/platform/capabilities/route.ts new file mode 100644 index 0000000..0b1fb7f --- /dev/null +++ b/app/api/platform/capabilities/route.ts @@ -0,0 +1,16 @@ +import { NextResponse } from "next/server.js"; + +import { platformCapabilitySnapshot } from "@/lib/platform-capabilities"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +export async function GET() { + return NextResponse.json(platformCapabilitySnapshot(), { + status: 200, + headers: { + "cache-control": "private, no-store, max-age=0", + vary: "Cookie", + }, + }); +} diff --git a/app/backend/page.tsx b/app/backend/page.tsx new file mode 100644 index 0000000..253c060 --- /dev/null +++ b/app/backend/page.tsx @@ -0,0 +1,9 @@ +import { Database, ShieldCheck } from "lucide-react"; + +import { PlatformCapabilityConsole } from "@/components/platform/platform-capability-console"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function BackendPage() { + return
Data-plane readiness

Live server capability receipt

Reference core verified
} />
; +} diff --git a/app/enterprise/page.tsx b/app/enterprise/page.tsx new file mode 100644 index 0000000..ad2fc2f --- /dev/null +++ b/app/enterprise/page.tsx @@ -0,0 +1,9 @@ +import { ShieldCheck, UsersRound } from "lucide-react"; + +import { PlatformCapabilityConsole } from "@/components/platform/platform-capability-console"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function EnterprisePage() { + return
Control-plane evidence

Tenant-safe reference runtime

Core verified locally
} />
; +} diff --git a/app/integrations/page.tsx b/app/integrations/page.tsx new file mode 100644 index 0000000..8df4fc2 --- /dev/null +++ b/app/integrations/page.tsx @@ -0,0 +1,9 @@ +import { KeyRound, ShieldCheck } from "lucide-react"; + +import { IntegrationCatalog } from "@/components/platform/integration-catalog"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function IntegrationsPage() { + return
No credential values rendered

Only current-tab markers are inspected

Evidence required
} />
; +} diff --git a/app/layout.tsx b/app/layout.tsx index d53e25d..dd91cb0 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -26,12 +26,12 @@ const productionUrl = /^https?:\/\//i.test(configuredProductionUrl) export const metadata: Metadata = { metadataBase: new URL(productionUrl), - title: "Drops Studio — Build crypto products in minutes", + title: "Drops Studio — Build crypto apps 10x faster with AI", description: - "Build useful crypto apps with DropsTab intelligence, guided Drops Bot setup and the AI model you choose.", + "Plan, build, test and publish editable crypto applications with DropsTab intelligence, Drops Bot automation and the AI model you choose.", openGraph: { title: "Drops Studio", - description: "Turn a crypto idea into a live project in five minutes.", + description: "Build editable crypto applications with AI, DropsTab intelligence and Drops Bot automation.", type: "website", images: [ { @@ -45,7 +45,7 @@ export const metadata: Metadata = { twitter: { card: "summary_large_image", title: "Drops Studio", - description: "Turn a crypto idea into a live project in five minutes.", + description: "Build editable crypto applications with AI, DropsTab intelligence and Drops Bot automation.", images: ["/og.png"], }, }; diff --git a/app/organizations/page.tsx b/app/organizations/page.tsx new file mode 100644 index 0000000..e1e506b --- /dev/null +++ b/app/organizations/page.tsx @@ -0,0 +1,9 @@ +import { Building2, ShieldCheck } from "lucide-react"; + +import { OrganizationConsole } from "@/components/platform/organization-console"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; + +export default function OrganizationsPage() { + return
Tenant-aware control plane

Account and storage status are read live

Account dependent
} />
; +} diff --git a/app/page.tsx b/app/page.tsx index 8e87c83..af6fa8f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,21 +5,22 @@ function LandingHero() { return (
- BUILD IN 5 MINUTES + DROPS AI CRYPTO BUILDER

- Turn a crypto idea{" "} + Build crypto apps{" "}
- into a live project. + 10x faster with AI.

- Start from 12 extensible working foundations or describe any crypto - product. + Describe a product or start from 12 extensible working foundations + for category-native crypto apps. Review the plan, then build an editable + multi-file application.

- Drops Studio assembles the data, triggers, AI brain and output around - DropsTab + Drops Bot. + DropsTab supplies market intelligence. Drops Bot supplies monitoring, + alerts and approved Telegram delivery. You own the source.

diff --git a/app/platform/page.tsx b/app/platform/page.tsx new file mode 100644 index 0000000..3e59095 --- /dev/null +++ b/app/platform/page.tsx @@ -0,0 +1,13 @@ +import { Boxes, ShieldCheck } from "lucide-react"; + +import { PlatformOverview } from "@/components/platform/platform-overview"; +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; +import { platformCapabilitySnapshot } from "@/lib/platform-capabilities"; + +export const dynamic = "force-dynamic"; + +export default function PlatformPage() { + const snapshot = platformCapabilitySnapshot(); + return
Capability-aware UI

Working, local, and setup states stay distinct

Truthful states
} />
; +} diff --git a/app/projects/page.tsx b/app/projects/page.tsx new file mode 100644 index 0000000..e974c51 --- /dev/null +++ b/app/projects/page.tsx @@ -0,0 +1,9 @@ +import { FolderKanban, HardDrive } from "lucide-react"; + +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; +import { ProjectLibrary } from "@/components/platform/project-library"; + +export default function ProjectsPage() { + return
Browser-first library

Cloud sync remains account-dependent

Local source of truth
} />
; +} diff --git a/app/styles/drops-studio.responsive.css b/app/styles/drops-studio.responsive.css index 41889c6..e664923 100644 --- a/app/styles/drops-studio.responsive.css +++ b/app/styles/drops-studio.responsive.css @@ -13,6 +13,7 @@ .studio-header nav.open { display: flex; align-items: stretch; } .studio-header nav button, .studio-header nav a { border-radius: 8px; justify-content: space-between; padding: 11px; width: 100%; } .studio-header nav button:hover, .studio-header nav a:hover { background: #f3f6fb; } + .studio-header nav .mobile-nav-connections { display: flex; } .header-actions { min-width: 0; } .mobile-menu { display: flex; } .api-vault-button { display: none; } diff --git a/app/styles/drops-studio.shell.css b/app/styles/drops-studio.shell.css index d3df3dc..737c642 100644 --- a/app/styles/drops-studio.shell.css +++ b/app/styles/drops-studio.shell.css @@ -27,8 +27,8 @@ .studio-header nav button, .studio-header nav a { align-items: center; background: none; border: 0; color: #36435d; cursor: pointer; display: flex; font-size: 14px; font-weight: 560; gap: 6px; padding: 10px 0; text-decoration: none; } .studio-header nav button:hover, .studio-header nav a:hover { color: var(--blue); } .studio-header nav button span { align-items: center; background: #eaf0fb; border-radius: 99px; display: inline-flex; font-size: 12px; height: 18px; justify-content: center; min-width: 18px; } +.studio-header nav .mobile-nav-connections { display: none; } .header-actions { align-items: center; display: flex; justify-content: flex-end; min-width: 218px; } .api-vault-button { align-items: center; background: #fff; border: 1px solid #dbe3ef; border-radius: 11px; cursor: pointer; display: flex; font-size: 13px; font-weight: 650; gap: 7px; padding: 10px 14px; box-shadow: 0 5px 16px rgba(40, 60, 100, .05); } .api-vault-button:hover { border-color: #adc4ff; color: var(--blue); } .mobile-menu { background: transparent; border: 0; cursor: pointer; display: none; padding: 7px; } - diff --git a/app/styles/platform-tailwind.css b/app/styles/platform-tailwind.css new file mode 100644 index 0000000..ec105e3 --- /dev/null +++ b/app/styles/platform-tailwind.css @@ -0,0 +1,5 @@ +/* Public dashboard routes are isolated from the legacy landing cascade. Keep + these utilities unlayered so explicit component classes can override the + older unlayered h1-h6 reset without changing the approved builder shell. */ +@import "tailwindcss/theme.css"; +@import "tailwindcss/utilities.css"; diff --git a/app/templates/page.tsx b/app/templates/page.tsx new file mode 100644 index 0000000..45bc2fd --- /dev/null +++ b/app/templates/page.tsx @@ -0,0 +1,9 @@ +import { Layers3, ShieldCheck } from "lucide-react"; + +import { PlatformShell } from "@/components/platform/platform-shell"; +import { PageIntro, StatusBadge } from "@/components/platform/platform-ui"; +import { TemplateCatalog } from "@/components/platform/template-catalog"; + +export default function TemplatesPage() { + return
Current recipe catalog

Imported directly from lib/presets

12 registered
} />
; +} diff --git a/components/drops-studio.tsx b/components/drops-studio.tsx index 870e4ab..bb3fb00 100644 --- a/components/drops-studio.tsx +++ b/components/drops-studio.tsx @@ -347,10 +347,12 @@ function Brand() { function useNearViewport() { const [ready, setReady] = useState(false); - const elementRef = useRef(null); + const [element, setElement] = useState(null); + const elementRef = useCallback((next: HTMLElement | null) => { + setElement(next); + }, []); useEffect(() => { - const element = elementRef.current; if (!element) return; const observer = new IntersectionObserver( ([entry]) => { @@ -362,7 +364,7 @@ function useNearViewport() { ); observer.observe(element); return () => observer.disconnect(); - }, []); + }, [element]); return { ready, elementRef }; } @@ -476,6 +478,13 @@ export function DropsStudio({ hero }: { hero: ReactNode }) { useEffect(() => { const timer = window.setTimeout(async () => { + const params = new URLSearchParams(window.location.search); + const presetParam = params.get("preset"); + const requestedCatalogPreset = presets.find( + (preset) => preset.id === presetParam, + ); + if (requestedCatalogPreset) setSelectedId(requestedCatalogPreset.id); + let savedProjects = readProjectsFromStore(); try { @@ -612,7 +621,6 @@ export function DropsStudio({ hero }: { hero: ReactNode }) { } catch { /* The local compiler remains available when access status is offline. */ } - const params = new URLSearchParams(window.location.search); const handoff = parseStudioConnectionHandoff(window.location.search); if (handoff.connections) { const requestedProvider = providerList.find( @@ -1701,17 +1709,7 @@ export function DropsStudio({ hero }: { hero: ReactNode }) {