chore: clean up root directory, move docs to /docs and legacy plans to /docs_archive
This commit is contained in:
510
docs_archive/AGENT_EXECUTION_ARCHITECTURE.md
Normal file
510
docs_archive/AGENT_EXECUTION_ARCHITECTURE.md
Normal file
@@ -0,0 +1,510 @@
|
||||
# VIBN Agent Execution Architecture
|
||||
|
||||
## Goal
|
||||
|
||||
Give every product builder a personal AI that thinks like a COO — one that understands their product, monitors what's happening, and can direct specialists to get work done on their behalf. The user talks to one AI. That AI figures out what needs to happen and delegates accordingly.
|
||||
|
||||
---
|
||||
|
||||
## The Mental Model
|
||||
|
||||
**The user has one AI.** It happens to have specialists behind it.
|
||||
|
||||
The user does not manage or navigate to Code agents, Growth agents, or Analytics agents. They talk to their Assist AI — their personal product COO — and that COO routes work to the right specialist internally. The module tabs (Code, Growth, Analytics) show the *output and status* of delegated work, not separate AI interfaces the user has to navigate between.
|
||||
|
||||
This is the difference between a founder who has a COO (talks to one person who directs the team) vs. a founder who manages every department directly (exhausting, requires expertise they don't have).
|
||||
|
||||
---
|
||||
|
||||
## Core AI Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ USER (non-technical founder) │
|
||||
│ │
|
||||
│ "I want more signups" │
|
||||
│ "Why did revenue drop last week?" │
|
||||
│ "Add a referral program" │
|
||||
│ "What should I build next?" │
|
||||
└───────────────────────────┬─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ASSIST — Personal AI / COO (Tier B: Claude Sonnet) │
|
||||
│ │
|
||||
│ The user's only AI interface. Reasons across the entire │
|
||||
│ business. Surfaces insights proactively. Delegates all work │
|
||||
│ to the right specialist. Never asks the user to choose which │
|
||||
│ module or agent to use — it figures that out. │
|
||||
│ │
|
||||
│ Full context available: │
|
||||
│ - PRD + product vision │
|
||||
│ - Everything that's been built (Gitea) │
|
||||
│ - All past agent sessions and their outcomes │
|
||||
│ - Live analytics and deployment status │
|
||||
│ - Persistent memory: decisions made, patterns, preferences │
|
||||
│ - Web search: competitors, market trends, growth tactics │
|
||||
│ │
|
||||
│ Proactive behaviors: │
|
||||
│ - Surfaces anomalies before being asked │
|
||||
│ - Tracks open questions and follows up │
|
||||
│ - Monitors deploy outcomes and flags regressions │
|
||||
│ - Briefs the founder on what happened while they were away │
|
||||
└──────────────┬───────────────┬───────────────┬──────────────────────┘
|
||||
│ │ │
|
||||
delegates │ delegates │ delegates │
|
||||
▼ ▼ ▼
|
||||
┌────────────────┐ ┌──────────────────┐ ┌────────────────────┐
|
||||
│ CODE ADVISOR │ │ GROWTH ADVISOR │ │ ANALYTICS ADVISOR │
|
||||
│ (Tier B) │ │ (Tier B) │ │ (Tier B) │
|
||||
│ │ │ │ │ │
|
||||
│ Technical │ │ Acquisition, │ │ Data queries, │
|
||||
│ scoping. │ │ activation, │ │ anomaly detection, │
|
||||
│ Reads codebase │ │ retention. │ │ correlates data │
|
||||
│ before writing │ │ Researches what │ │ with deploys and │
|
||||
│ a single line. │ │ works for this │ │ events. │
|
||||
│ │ │ product type. │ │ │
|
||||
│ NOT user- │ │ NOT user- │ │ NOT user- │
|
||||
│ facing. │ │ facing. │ │ facing. │
|
||||
└───────┬────────┘ └────────┬─────────┘ └──────────┬─────────┘
|
||||
│ │ │
|
||||
└───────────────────┴───────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ORCHESTRATOR (Tier A: Gemini Flash) │
|
||||
│ │
|
||||
│ Receives a structured plan from whichever Advisor handled the │
|
||||
│ work. Breaks it into discrete tasks. Assigns tiers based on │
|
||||
│ complexity. Runs tasks in parallel where possible. │
|
||||
└──────────┬───────────────┬───────────────┬──────────────────────────┘
|
||||
↓ ↓ ↓
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ TIER A │ │ TIER B │ │ TIER C │
|
||||
│Gemini Flash │ │Claude Sonnet│ │Claude Sonnet│
|
||||
│ │ │ │ │ (→ Opus) │
|
||||
│Simple edits │ │Feature work │ │Complex arch │
|
||||
│Copy, rename │ │New components│ │Hard debugs │
|
||||
│Config tweaks│ │API routes │ │Refactors │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
↓ ↓ ↓
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ EXECUTION RUNTIME (vibn-agent-runner) │
|
||||
│ │
|
||||
│ - Persistent dev environment: Node, Python, npm, git │
|
||||
│ - Agent executes commands, writes files, runs builds │
|
||||
│ - Self-corrects on errors (re-prompts with error context) │
|
||||
│ - Auto-commits to Gitea on completion │
|
||||
│ - Triggers Coolify redeploy automatically │
|
||||
│ - Session persists even if browser is closed │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**The confirmation moment is critical UX.** Before any work is delegated, Assist always surfaces a plain-language summary card:
|
||||
> *"Here's my plan: I'm going to add a referral program. That means building a referral code system, an invite email, and a dashboard for your users. This will take ~8 minutes. Want me to start?"*
|
||||
|
||||
The user never sees task tiers, agent assignments, or technical scope. They see what's happening and confirm.
|
||||
|
||||
---
|
||||
|
||||
## Assist — Personal AI / COO
|
||||
|
||||
The user's primary and only AI interface. All other specialists work behind it.
|
||||
|
||||
### What it does
|
||||
|
||||
**Reactive** — answers anything the user asks:
|
||||
- "What should I build next?"
|
||||
- "Why did signups drop?"
|
||||
- "How does our product compare to [competitor]?"
|
||||
- "Add a referral program"
|
||||
- "Fix the checkout bug"
|
||||
|
||||
**Proactive** — surfaces things without being asked:
|
||||
- Detects when a deploy caused an error spike
|
||||
- Flags when a key metric moves significantly
|
||||
- Follows up on decisions made in past conversations ("You said you wanted to revisit the onboarding flow this week")
|
||||
- Delivers a brief when the founder returns after time away
|
||||
|
||||
### Full example
|
||||
|
||||
```
|
||||
User: "I want more signups but I don't know what's blocking it"
|
||||
↓
|
||||
Assist COO
|
||||
- Pulls current signup funnel from analytics
|
||||
- Reads landing page + onboarding flow from Gitea
|
||||
- Checks recent deploys for regressions
|
||||
- Searches web for conversion benchmarks for this product category
|
||||
- Cross-references PRD: what was the original promise to users?
|
||||
- Asks: "Are people finding you organically or are you mostly getting
|
||||
referrals? That changes what I'd recommend."
|
||||
- User: "Mostly referrals from friends"
|
||||
- COO: "Then your acquisition isn't the problem — your activation is.
|
||||
People aren't converting after they land. Your onboarding skips the
|
||||
'aha moment'. Here's what I'd do..."
|
||||
- Presents plan: "1. Rewrite the onboarding welcome email.
|
||||
2. Add an empty-state prompt on the dashboard.
|
||||
3. Test a 30-day trial instead of 14.
|
||||
Shall I start?"
|
||||
↓
|
||||
On confirm → delegates to Growth Advisor (items 1–2) + Code Advisor (item 3)
|
||||
↓
|
||||
Orchestrator breaks into tasks → Execution agents build and deploy
|
||||
↓
|
||||
Assist follows up: "Done. The onboarding email and dashboard prompt are live.
|
||||
The trial length change needs your input on pricing — want to talk through it?"
|
||||
```
|
||||
|
||||
### What Assist does NOT do itself
|
||||
- Write or execute code → delegates to Code Advisor
|
||||
- Design and run growth experiments → delegates to Growth Advisor
|
||||
- Run raw data queries → delegates to Analytics Advisor
|
||||
|
||||
The user never has to know this routing is happening.
|
||||
|
||||
### Persistent Memory
|
||||
|
||||
Assist maintains a memory of the product that grows over time:
|
||||
|
||||
```
|
||||
"User prefers simple, fast solutions over architecturally correct ones"
|
||||
"Decided against social login in Jan 2026 — too complex for current stage"
|
||||
"Trial length has been discussed 3x — founder is nervous about revenue impact"
|
||||
"Mobile conversion has been an open problem since Nov 2025"
|
||||
```
|
||||
|
||||
This is what makes it feel like a COO rather than a chatbot. It remembers context across sessions so the user never has to repeat themselves.
|
||||
|
||||
---
|
||||
|
||||
## Specialist Advisors (Delegated, Not User-Facing)
|
||||
|
||||
These three Advisors are invoked by Assist, not by the user directly. Their module tabs (Code, Growth, Analytics) in the sidebar show **status and output** — not chat interfaces.
|
||||
|
||||
### Code Advisor
|
||||
|
||||
Handles anything that requires changes to the codebase. Scopes work technically before a single line is written.
|
||||
|
||||
```
|
||||
[Invoked by Assist with]: "Add Stripe checkout to the admin app"
|
||||
↓
|
||||
Code Advisor
|
||||
- Reads package.json — Stripe not yet installed
|
||||
- Reads existing payment-related files (none found)
|
||||
- Searches: current Stripe Next.js best practices
|
||||
- Scopes: "3 tasks: install SDK, create checkout API route,
|
||||
add checkout button to pricing page"
|
||||
- Returns structured plan to Orchestrator
|
||||
↓
|
||||
Orchestrator:
|
||||
Task 1 (Tier A): npm install stripe
|
||||
Task 2 (Tier B): POST /api/checkout route
|
||||
Task 3 (Tier B): CheckoutButton component on pricing page
|
||||
↓
|
||||
Execution → commit → redeploy
|
||||
↓
|
||||
Assist reports back to user: "Stripe checkout is live."
|
||||
```
|
||||
|
||||
**Context it uses:**
|
||||
- Full codebase via Gitea
|
||||
- Past agent sessions and what they changed
|
||||
- Build logs and Coolify deploy history
|
||||
- Web search: API docs, package versions, error solutions
|
||||
|
||||
### Growth Advisor
|
||||
|
||||
Handles acquisition, activation, and retention work. Researches what's proven to work before proposing anything.
|
||||
|
||||
```
|
||||
[Invoked by Assist with]: "Build a referral program"
|
||||
↓
|
||||
Growth Advisor
|
||||
- Checks current user count + retention from analytics
|
||||
- Reviews existing onboarding flow from codebase
|
||||
- Searches: referral mechanics that work for this product type + stage
|
||||
- Proposes: "Double-sided reward — referrer gets 1 month free,
|
||||
referee gets 14-day trial. This pattern converts at ~18% for
|
||||
B2B SaaS at your stage."
|
||||
- Returns scoped plan
|
||||
↓
|
||||
Orchestrator → 4 tasks → Execution
|
||||
```
|
||||
|
||||
**Context it uses:**
|
||||
- User base metrics from Analytics
|
||||
- Current user flow from Gitea
|
||||
- Past growth experiments and outcomes
|
||||
- Web search: growth playbooks, conversion benchmarks, competitor tactics
|
||||
|
||||
### Analytics Advisor
|
||||
|
||||
Handles all data interpretation. Correlates numbers with events to find the actual story behind a metric.
|
||||
|
||||
```
|
||||
[Invoked by Assist with]: "Why did signups drop last week?"
|
||||
↓
|
||||
Analytics Advisor
|
||||
- Queries signup data: 30-day trend, last week specifically
|
||||
- Checks Gitea: any deploys last week? Yes — Tuesday at 2pm
|
||||
- Checks error logs: mobile error rate went from 0.2% to 8.4% post-deploy
|
||||
- Finds: specific commit that changed signup form validation
|
||||
- Returns: "Tuesday's deploy broke mobile signup. Here's the commit."
|
||||
↓
|
||||
Assist reports to user + asks: "Want me to fix it?"
|
||||
If yes → delegates to Code Advisor
|
||||
```
|
||||
|
||||
**Context it uses:**
|
||||
- Product event data (signups, activations, churns, feature usage)
|
||||
- Deployment history (Gitea commits + Coolify deploy timestamps)
|
||||
- Error logs and performance metrics
|
||||
- Web search: category benchmarks for comparison
|
||||
|
||||
---
|
||||
|
||||
## Shared Execution Infrastructure
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ vibn-agent-runner (Node.js/TypeScript) │
|
||||
│ │
|
||||
│ - Receives task + tier assignment from Orchestrator │
|
||||
│ - Instantiates the correct LLM client (Tier A/B/C) │
|
||||
│ - Executes tools: write_file, execute_command, read_file, etc. │
|
||||
│ - Self-corrects on errors (re-prompts with error output) │
|
||||
│ - Writes step-by-step output to Postgres after every step │
|
||||
│ - Auto-commits to Gitea on completion │
|
||||
│ - Triggers Coolify redeploy automatically │
|
||||
│ - Session persists even if browser tab is closed │
|
||||
└──────────────────────┬────────────────────────────────────────────┘
|
||||
│ git push (auto on completion)
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ Gitea (git.vibnai.com) │
|
||||
│ - Source of truth for all committed code │
|
||||
│ - Webhook → triggers Coolify auto-deploy │
|
||||
└──────────────────────┬────────────────────────────────────────────┘
|
||||
│ auto-deploy
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ Coolify (coolify.vibnai.com) │
|
||||
│ - Builds, deploys, manages domains + SSL │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Model Tier Reference
|
||||
|
||||
| Tier | Model | Used For | Cost Profile |
|
||||
|------|-------|----------|--------------|
|
||||
| A | Gemini 2.5 Flash | Orchestration, simple tasks, routing, summaries | Very low |
|
||||
| B | Claude Sonnet 4.6 | Assist COO, specialist advisors, feature-level tasks | Medium |
|
||||
| C | Claude Sonnet 4.6 (→ Opus) | Hard debugging, architecture changes, complex reasoning | High |
|
||||
|
||||
All tiers overridable via env vars: `TIER_A_MODEL`, `TIER_B_MODEL`, `TIER_C_MODEL`.
|
||||
|
||||
---
|
||||
|
||||
## Data Model
|
||||
|
||||
### `agent_sessions` table (Postgres) — exists
|
||||
|
||||
```sql
|
||||
CREATE TABLE agent_sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id TEXT NOT NULL,
|
||||
module TEXT NOT NULL DEFAULT 'code',
|
||||
-- code | growth | analytics (delegated modules)
|
||||
app_name TEXT NOT NULL,
|
||||
app_path TEXT NOT NULL,
|
||||
task TEXT NOT NULL,
|
||||
plan JSONB,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
-- pending | running | done | approved | failed | stopped
|
||||
output JSONB NOT NULL DEFAULT '[]',
|
||||
changed_files JSONB NOT NULL DEFAULT '[]',
|
||||
error TEXT,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
```
|
||||
|
||||
### `advisor_conversations` table (Postgres) — to be built
|
||||
|
||||
Stores every conversation the COO has had with the user. Each row is one session (may span many turns).
|
||||
|
||||
```sql
|
||||
CREATE TABLE advisor_conversations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id TEXT NOT NULL,
|
||||
|
||||
-- 'assist' for user-facing COO conversations.
|
||||
-- 'code' | 'growth' | 'analytics' for internal specialist invocations.
|
||||
module TEXT NOT NULL DEFAULT 'assist',
|
||||
|
||||
messages JSONB NOT NULL DEFAULT '[]',
|
||||
-- Full message history: [{role, content, timestamp}]
|
||||
|
||||
context_snapshot JSONB,
|
||||
-- State of PRD, codebase summary, analytics at conversation start.
|
||||
-- Used for debugging ("what did it know when it said that?")
|
||||
|
||||
outcome TEXT,
|
||||
-- null | 'tasked' | 'declined' | 'deferred' | 'monitoring'
|
||||
|
||||
-- If this conversation resulted in execution, link to the session(s)
|
||||
session_ids UUID[] DEFAULT '{}',
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
```
|
||||
|
||||
### `advisor_memory` table (Postgres) — to be built
|
||||
|
||||
Long-term memory for the COO. Persists facts, decisions, and patterns across conversations. This is what makes the AI feel like a real assistant rather than a stateless chatbot.
|
||||
|
||||
```sql
|
||||
CREATE TABLE advisor_memory (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id TEXT NOT NULL,
|
||||
|
||||
-- Category of memory
|
||||
category TEXT NOT NULL,
|
||||
-- 'decision' — "Decided not to add social login — too complex for now"
|
||||
-- 'preference' — "Founder prefers speed over architectural correctness"
|
||||
-- 'open' — "Mobile conversion problem still unresolved"
|
||||
-- 'context' — "Target user is non-technical indie founders"
|
||||
-- 'experiment' — "Tried 30-day trial in Feb — didn't impact conversion"
|
||||
|
||||
key TEXT NOT NULL, -- short label for retrieval
|
||||
value TEXT NOT NULL, -- full memory content
|
||||
confidence REAL DEFAULT 1.0, -- 0–1, decays if contradicted
|
||||
|
||||
-- Where this memory came from
|
||||
source_conversation_id UUID REFERENCES advisor_conversations(id),
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Current End-to-End Status (March 2026)
|
||||
|
||||
### What works today
|
||||
|
||||
| Step | Status |
|
||||
|------|--------|
|
||||
| User types task → session created in Postgres | ✅ |
|
||||
| Agent runner receives task, calls Claude Sonnet 4.6 via Vertex AI | ✅ |
|
||||
| Agent executes commands and writes files in runner workspace | ✅ |
|
||||
| Step output streamed to Postgres in real time | ✅ |
|
||||
| Frontend polls and shows live output log | ✅ |
|
||||
| Auto-commit + push to Gitea on completion | ✅ |
|
||||
| Gitea webhook triggers Coolify auto-deploy | ✅ |
|
||||
| Browse tab shows latest committed files | ✅ |
|
||||
| Project tabs (Atlas/PRD/Build/Growth/Assist/Analytics) in sidebar | ✅ |
|
||||
|
||||
### In Progress
|
||||
|
||||
| Step | Status |
|
||||
|------|--------|
|
||||
| Assist COO + specialist advisor architecture | 🔶 Designing |
|
||||
|
||||
### Not Yet Started
|
||||
|
||||
| Step | Status |
|
||||
|------|--------|
|
||||
| `advisor_conversations` + `advisor_memory` DB tables | ⬜ |
|
||||
| Assist COO (user-facing personal AI) | ⬜ |
|
||||
| Code Advisor (delegated specialist) | ⬜ |
|
||||
| Growth Advisor (delegated specialist) | ⬜ |
|
||||
| Analytics Advisor (delegated specialist) | ⬜ |
|
||||
| Orchestrator (task decomposition + tier routing) | ⬜ |
|
||||
| Proactive monitoring (anomaly detection, briefings) | ⬜ |
|
||||
| Parallel task execution | ⬜ |
|
||||
| WebSocket streaming (replace polling) | ⬜ |
|
||||
| Terminal tab (xterm.js → live container PTY) | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## Build Phases
|
||||
|
||||
### Phase 1 — Execution Foundation ✅
|
||||
- [x] `agent_sessions` DB table + REST API routes
|
||||
- [x] Agent runner with Claude Sonnet (Tier B)
|
||||
- [x] Three-tier LLM clients (Gemini Flash, Claude via Vertex)
|
||||
- [x] Session output streamed to Postgres per step
|
||||
- [x] Auto-commit + Coolify deploy on completion
|
||||
- [x] Frontend: Browse / Agent / Terminal tabs
|
||||
- [x] Adaptive polling, auto-select active session
|
||||
- [x] Context-aware task input (locked while running)
|
||||
- [x] Project tabs moved to sidebar (Atlas/PRD/Build/Growth/Assist/Analytics)
|
||||
|
||||
### Phase 2 — Per-project Sandboxed Workspaces
|
||||
- [ ] Per-project ephemeral container (cold tier — wakes on demand)
|
||||
- [ ] Agent `execute_command` routes through the project workspace container
|
||||
- [ ] Persistent volume per project for caches / installed deps
|
||||
- [ ] In-browser file viewer reflects live agent workspace
|
||||
|
||||
### Phase 3 — Assist COO + Specialist Advisors
|
||||
- [ ] `advisor_conversations` + `advisor_memory` DB tables + API
|
||||
- [ ] Assist COO: stateful multi-turn conversation with full project context
|
||||
- [ ] Assist COO: persistent memory across conversations (advisor_memory)
|
||||
- [ ] Assist COO: web search tool (competitor research, growth tactics, docs)
|
||||
- [ ] Code Advisor: reads codebase, scopes tasks before execution
|
||||
- [ ] Growth Advisor: researches what works, proposes specific experiments
|
||||
- [ ] Analytics Advisor: queries data, correlates with deploys, finds stories
|
||||
- [ ] Assist delegates to specialists internally — user sees one AI
|
||||
- [ ] Confirmation card UX: plain-language plan before any execution begins
|
||||
|
||||
### Phase 4 — Orchestrator
|
||||
- [ ] Orchestrator: decomposes advisor plans into tiered tasks
|
||||
- [ ] Parallel task execution (multiple agents simultaneously)
|
||||
- [ ] Task dependency graph (task B waits for task A's output)
|
||||
- [ ] Cross-module task routing (one confirm → Code + Growth tasks in parallel)
|
||||
|
||||
### Phase 5 — Streaming & Persistence
|
||||
- [ ] WebSocket replaces polling for live output
|
||||
- [ ] Browser reconnect: full log replay from Postgres + live tail
|
||||
- [ ] Background notifications (in-app + email) on completion/failure
|
||||
- [ ] Terminal tab: xterm.js connected to project workspace PTY
|
||||
|
||||
### Phase 6 — Proactive Intelligence
|
||||
- [ ] Assist monitors for anomalies and surfaces them without being asked
|
||||
- [ ] Morning briefing: digest of what happened since last session
|
||||
- [ ] Memory improves over time: learns founder preferences and product patterns
|
||||
- [ ] Orchestrator estimates task time + complexity before confirming
|
||||
- [ ] Agents validate their own output (tests, type-checks, linting)
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
**Why one AI interface (the COO), not four module AIs?**
|
||||
Non-technical founders don't want to manage a team of AI tools. They want to talk to one AI that knows their business and gets things done. Routing work to specialists is the COO's job, not the founder's. Hiding that complexity is the product.
|
||||
|
||||
**Why is Assist Tier B (Claude Sonnet) and not the fastest model?**
|
||||
Assist is the only AI the user ever talks to. They'll judge the entire product by how well it understands them, how good its advice is, and how natural the conversation feels. This warrants the strongest reasoning model available. The savings come from using cheaper tiers for execution, not from cutting corners on the user-facing intelligence.
|
||||
|
||||
**Why does Assist have persistent memory (advisor_memory)?**
|
||||
Without memory, every conversation starts from zero. The COO has to re-learn that the founder doesn't want to use social login, that mobile conversion is an ongoing problem, that the trial length decision was deliberate. Memory is what transforms a chatbot into an assistant that actually knows you.
|
||||
|
||||
**Why are the specialist advisors not user-facing?**
|
||||
Users shouldn't have to decide "is this a Code question or a Growth question?" That's the COO's job. The module tabs exist to show what's been delegated and what the status is — not as separate AI chat interfaces. The user's relationship is with Assist, not with the individual specialists.
|
||||
|
||||
**Why is the Orchestrator Tier A (cheap)?**
|
||||
Once a specialist Advisor has produced a clear, structured plan, decomposing it into tasks is mechanical. It doesn't require deep reasoning — it requires fast, reliable routing. Gemini Flash is ideal: low latency, high quota, very low cost.
|
||||
|
||||
**Why auto-commit by default?**
|
||||
The target user is a non-technical founder. Requiring approval on every task creates friction and undermines the "describe it, it ships" value proposition. Gitea + Coolify already provide a rollback path if something goes wrong.
|
||||
|
||||
**Why store everything in Postgres?**
|
||||
Browser sessions end. Postgres is the source of truth. Every conversation turn, every memory item, every execution step, every outcome is written immediately. The WebSocket stream (Phase 5) is a convenience layer on top of the database, not a replacement.
|
||||
299
docs_archive/Google_Cloud_Product_OS.md
Normal file
299
docs_archive/Google_Cloud_Product_OS.md
Normal file
@@ -0,0 +1,299 @@
|
||||
Google Cloud Product OS
|
||||
Product-Centric IDE + SaaS Autopilot Platform (Requirements & Architecture)
|
||||
Vision
|
||||
|
||||
Build a Product-Centric IDE and Automation Platform dedicated exclusively to:
|
||||
|
||||
Launching, growing, and operating SaaS products on Google Cloud
|
||||
|
||||
This is NOT a general-purpose IDE.
|
||||
This is a Product Operating System (Product OS) designed to unify:
|
||||
|
||||
Code
|
||||
|
||||
Marketing
|
||||
|
||||
Analytics
|
||||
|
||||
Growth
|
||||
|
||||
Support
|
||||
|
||||
Experiments
|
||||
|
||||
Infrastructure
|
||||
|
||||
AI-driven automation
|
||||
|
||||
into one coherent platform.
|
||||
|
||||
It delivers:
|
||||
|
||||
A Cursor-like experience
|
||||
|
||||
Without Cursor cost
|
||||
|
||||
Powered by Gemini (Vertex AI)
|
||||
|
||||
Optimized specifically for Google Cloud
|
||||
|
||||
Focused exclusively on building & automating products
|
||||
|
||||
Core Product Principles
|
||||
1. Product-Centric, Not Code-Centric
|
||||
|
||||
This platform optimizes for:
|
||||
|
||||
Shipping, launching, growing, and optimizing products, not just writing code.
|
||||
|
||||
2. Opinionated for Google Cloud
|
||||
|
||||
This system is:
|
||||
|
||||
Cloud Run-first
|
||||
|
||||
Firestore / Cloud SQL-native
|
||||
|
||||
BigQuery-native
|
||||
|
||||
Cloud Build-native
|
||||
|
||||
Gemini-native
|
||||
|
||||
No AWS, no Azure, no multi-cloud abstraction.
|
||||
|
||||
3. Automation First
|
||||
|
||||
Everything is:
|
||||
|
||||
Automatable
|
||||
|
||||
Observable
|
||||
|
||||
Auditable
|
||||
|
||||
Optimizable
|
||||
|
||||
4. AI as a Product Operator
|
||||
|
||||
The AI is not just a coding assistant.
|
||||
It is a:
|
||||
|
||||
Product Operator AI
|
||||
capable of coordinating marketing, growth, support, analytics, and code.
|
||||
|
||||
IDE Structure: Product-Centric Layout
|
||||
|
||||
Instead of a traditional IDE layout, the system must expose:
|
||||
|
||||
Product OS
|
||||
├── Code
|
||||
├── Marketing
|
||||
├── Analytics
|
||||
├── Growth
|
||||
├── Support
|
||||
├── Experiments
|
||||
└── Infrastructure
|
||||
|
||||
|
||||
Each section is first-class and AI-assisted.
|
||||
|
||||
Section Requirements
|
||||
1. Code Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Build and deploy product services
|
||||
|
||||
Must support:
|
||||
|
||||
Cloud Run services
|
||||
|
||||
Cloud SQL / Firestore integration
|
||||
|
||||
Secrets management
|
||||
|
||||
Logs & traces
|
||||
|
||||
Rollbacks
|
||||
|
||||
Service templates
|
||||
|
||||
Not required:
|
||||
|
||||
Arbitrary framework support
|
||||
|
||||
Every programming language
|
||||
|
||||
Optimized languages:
|
||||
|
||||
TypeScript / Node
|
||||
|
||||
Python
|
||||
|
||||
2. Marketing Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Automate go-to-market and content execution
|
||||
|
||||
Must support:
|
||||
|
||||
Campaign generation
|
||||
|
||||
Social scheduling (Missinglettr)
|
||||
|
||||
Blog generation & updates
|
||||
|
||||
Landing page updates
|
||||
|
||||
Brand voice control
|
||||
|
||||
Product update → campaign pipeline
|
||||
|
||||
AI must:
|
||||
|
||||
Convert product changes into launch content
|
||||
|
||||
Adapt content to brand style
|
||||
|
||||
3. Analytics Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Understand product performance and causality
|
||||
|
||||
Must support:
|
||||
|
||||
Funnels
|
||||
|
||||
Retention
|
||||
|
||||
Activation
|
||||
|
||||
Cohorts
|
||||
|
||||
LTV
|
||||
|
||||
Causal drivers
|
||||
|
||||
Experiment results
|
||||
|
||||
NOT a SQL editor.
|
||||
This is a Product Intelligence Interface.
|
||||
|
||||
AI must answer:
|
||||
|
||||
"Why did conversion change?"
|
||||
"What caused activation to drop?"
|
||||
"What should we test next?"
|
||||
|
||||
4. Growth Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Optimize onboarding and conversion
|
||||
|
||||
Must support:
|
||||
|
||||
Funnel definitions
|
||||
|
||||
Onboarding flows
|
||||
|
||||
Growth experiments
|
||||
|
||||
A/B tests
|
||||
|
||||
Nudge systems
|
||||
|
||||
Conversion optimization
|
||||
|
||||
AI must:
|
||||
|
||||
Detect drop-offs
|
||||
|
||||
Recommend experiments
|
||||
|
||||
Evaluate uplift
|
||||
|
||||
5. Support Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Integrate customer feedback and product health
|
||||
|
||||
Must support:
|
||||
|
||||
Ticket ingestion
|
||||
|
||||
AI-assisted replies
|
||||
|
||||
Knowledge base generation
|
||||
|
||||
Product issue detection
|
||||
|
||||
Issue → fix pipeline
|
||||
|
||||
AI must:
|
||||
|
||||
Generate replies
|
||||
|
||||
Detect recurring issues
|
||||
|
||||
Recommend fixes
|
||||
|
||||
6. Experiments Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Coordinate A/B tests and product experiments
|
||||
|
||||
Must support:
|
||||
|
||||
Experiment definitions
|
||||
|
||||
Targeting
|
||||
|
||||
Metrics tracking
|
||||
|
||||
Statistical significance
|
||||
|
||||
Rollout controls
|
||||
|
||||
AI must:
|
||||
|
||||
Suggest experiments
|
||||
|
||||
Analyze results
|
||||
|
||||
Recommend actions
|
||||
|
||||
7. Infrastructure Section
|
||||
|
||||
Purpose:
|
||||
|
||||
Manage and monitor production systems
|
||||
|
||||
Must support:
|
||||
|
||||
Cloud Run deployments
|
||||
|
||||
Firestore / Cloud SQL management
|
||||
|
||||
Secrets
|
||||
|
||||
Logs
|
||||
|
||||
Traces
|
||||
|
||||
Alerts
|
||||
|
||||
Cost monitoring
|
||||
|
||||
AI must:
|
||||
|
||||
Detect anomalies
|
||||
|
||||
Recommend optimizations
|
||||
|
||||
Automate fixes
|
||||
209
docs_archive/TURBOREPO_MIGRATION_PLAN.md
Normal file
209
docs_archive/TURBOREPO_MIGRATION_PLAN.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Turborepo Monorepo Per-Project Migration Plan
|
||||
|
||||
## Why We Are Making This Change
|
||||
|
||||
The core thesis of this platform is that **one AI controls everything in one project**. For that to work, the AI needs a complete mental model of the project — all apps, all shared code, all dependencies — in a single coherent context.
|
||||
|
||||
The current architecture creates a single Gitea repo per project with no enforced internal structure. The AI has no reliable way to know where apps live, what shares code with what, or how to trigger a targeted build for one part of the project.
|
||||
|
||||
By adopting **Turborepo monorepo per project**, every project repo gets a standardised structure containing all of its apps (`product`, `website`, `admin`, `storybook`) and shared packages (`ui`, `tokens`, `types`, `config`). The AI operates across the entire project simultaneously. Build orchestration, deployment, and shared code all become coherent automatically.
|
||||
|
||||
**The structure every user project repo will have:**
|
||||
|
||||
```
|
||||
{project-slug}/ ← one Gitea repo per project
|
||||
apps/
|
||||
product/ ← core user-facing app (Next.js)
|
||||
website/ ← marketing / landing site (Next.js)
|
||||
admin/ ← internal admin tool (Next.js)
|
||||
storybook/ ← component browser and design system
|
||||
packages/
|
||||
ui/ ← shared React component library
|
||||
tokens/ ← design tokens (colors, spacing, typography)
|
||||
types/ ← shared TypeScript types
|
||||
config/ ← shared eslint, tsconfig
|
||||
turbo.json
|
||||
package.json ← pnpm workspace root
|
||||
.gitignore
|
||||
README.md
|
||||
```
|
||||
|
||||
Turborepo is MIT-licensed, runs anywhere, and costs nothing. No Vercel dependency.
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure Context
|
||||
|
||||
Everything runs on a single GCP VM (`34.19.250.135`, Montreal) via Docker + Traefik:
|
||||
|
||||
| Service | URL | Repo |
|
||||
|---|---|---|
|
||||
| Platform frontend | `vibnai.com` | `git.vibnai.com/mark/vibn-frontend` |
|
||||
| Gitea | `git.vibnai.com` | — |
|
||||
| Coolify | `coolify.vibnai.com` | — |
|
||||
| PostgreSQL | internal | — |
|
||||
|
||||
**All platform logic lives in `vibn-frontend`** (Next.js). There is no separate control plane service. The backend is Next.js API routes in `app/api/`. Storage is PostgreSQL via raw SQL queries (no ORM layer in use for project data).
|
||||
|
||||
**Integrations that already exist and should not be replaced:**
|
||||
- `lib/gitea.ts` — full Gitea API client (create repo, webhooks, signature verification)
|
||||
- `lib/coolify.ts` — full Coolify API client (projects, databases, applications, deployments)
|
||||
- `app/api/projects/create/route.ts` — project creation flow (creates Gitea repo)
|
||||
- `app/api/webhooks/gitea/route.ts` — receives Gitea push/PR events
|
||||
- `app/api/webhooks/coolify/route.ts` — receives Coolify deployment events
|
||||
- `app/api/ai/chat/route.ts` — AI chat with Gemini
|
||||
- `lib/auth/authOptions.ts` — NextAuth v4 with Prisma adapter
|
||||
|
||||
---
|
||||
|
||||
## Scope of Changes
|
||||
|
||||
### 1. Scaffold Templates
|
||||
|
||||
**What:** A set of template files written into the user's Gitea repo when a project is created, giving every project the standard Turborepo monorepo structure.
|
||||
|
||||
**Where:** `vibn-frontend/lib/scaffold/turborepo/`
|
||||
|
||||
**Files to create:**
|
||||
- `turbo.json` — pipeline: `build`, `dev`, `lint`, `type-check`, `test`
|
||||
- `package.json` — pnpm workspace root pointing to `apps/*` and `packages/*`
|
||||
- `.gitignore`
|
||||
- `README.md` — project-specific (name injected at scaffold time)
|
||||
- `apps/product/` — Next.js 15, references shared `ui`, `tokens`, `types`
|
||||
- `apps/website/` — Next.js 15
|
||||
- `apps/admin/` — Next.js 15
|
||||
- `apps/storybook/` — Storybook 8
|
||||
- `packages/ui/` — Button, Card, Input, Badge components using CSS token vars
|
||||
- `packages/tokens/` — design tokens as TS + CSS custom properties
|
||||
- `packages/types/` — shared `User`, `ApiResponse`, `PaginatedResponse` types
|
||||
- `packages/config/` — `tsconfig.base.json` and `eslint.config.js`
|
||||
|
||||
**Status:** Templates were written and are ready. Need to be moved to `vibn-frontend/lib/scaffold/turborepo/`.
|
||||
|
||||
---
|
||||
|
||||
### 2. Project Creation Route — Add Scaffold Push
|
||||
|
||||
**What:** The existing `app/api/projects/create/route.ts` already creates a Gitea repo. It needs one additional step: push the Turborepo scaffold as the initial commit.
|
||||
|
||||
**File to update:** `vibn-frontend/app/api/projects/create/route.ts`
|
||||
|
||||
**Current flow:**
|
||||
1. Create Gitea repo (`auto_init: true` — creates empty repo with README)
|
||||
2. Register webhook
|
||||
3. Save project record to PostgreSQL
|
||||
|
||||
**New step to add after repo creation:**
|
||||
- Read scaffold template files from `lib/scaffold/turborepo/`
|
||||
- Replace `{{project-slug}}` and `{{project-name}}` placeholders
|
||||
- Push each file to the Gitea repo via the contents API
|
||||
- This replaces the default empty `auto_init` commit
|
||||
|
||||
**Note:** Change `auto_init: true` to `auto_init: false` since we are pushing the scaffold ourselves.
|
||||
|
||||
---
|
||||
|
||||
### 3. Project Data Model — Add App Tracking
|
||||
|
||||
**What:** The `fs_projects` table stores project data as a JSONB `data` column. The `data` object needs two new fields to track the monorepo apps and their Coolify services.
|
||||
|
||||
**Fields to add to the project `data` JSONB:**
|
||||
|
||||
```typescript
|
||||
apps: Array<{
|
||||
name: string; // "product" | "website" | "admin" | "storybook"
|
||||
path: string; // "apps/product"
|
||||
coolifyServiceUuid?: string;
|
||||
domain?: string;
|
||||
}>
|
||||
turboVersion: string; // e.g. "2.3.3"
|
||||
```
|
||||
|
||||
No schema migration needed — it's JSONB, just include these fields when inserting/updating.
|
||||
|
||||
---
|
||||
|
||||
### 4. Coolify — Per-App Service Provisioning
|
||||
|
||||
**What:** When a project is created, each app in the monorepo gets its own Coolify service with the correct Turbo build filter. This extends the existing `lib/coolify.ts`.
|
||||
|
||||
**File to update:** `vibn-frontend/lib/coolify.ts`
|
||||
|
||||
**Add function:**
|
||||
|
||||
```typescript
|
||||
createMonorepoAppService(opts: {
|
||||
projectUuid: string;
|
||||
appName: string; // e.g. "product"
|
||||
gitRepo: string; // the project's Gitea clone URL
|
||||
domain: string; // e.g. "product-taskmaster.vibnai.com"
|
||||
}): Promise<CoolifyApplication>
|
||||
```
|
||||
|
||||
Build command: `pnpm install && turbo run build --filter={appName}`
|
||||
|
||||
**Wire into project creation:** After Gitea repo is created and scaffold is pushed, create one Coolify service per app and store the `coolifyServiceUuid` in the project's `apps` array.
|
||||
|
||||
---
|
||||
|
||||
### 5. Deploy API Route
|
||||
|
||||
**What:** A new API route that triggers a Coolify deployment for a specific app within a project.
|
||||
|
||||
**File to create:** `vibn-frontend/app/api/projects/[projectId]/deploy/route.ts`
|
||||
|
||||
```
|
||||
POST /api/projects/{projectId}/deploy
|
||||
Body: { app_name: "product" | "website" | "admin" | "storybook" }
|
||||
```
|
||||
|
||||
Flow:
|
||||
1. Load project from PostgreSQL
|
||||
2. Find the app's `coolifyServiceUuid`
|
||||
3. Call `deployApplication(uuid)` from `lib/coolify.ts`
|
||||
4. Return deployment UUID
|
||||
|
||||
---
|
||||
|
||||
### 6. AI Chat — Project Context Injection
|
||||
|
||||
**What:** The existing `app/api/ai/chat/route.ts` handles Gemini chat. It needs to inject monorepo structure context when a `projectId` is present in the request.
|
||||
|
||||
**File to update:** `vibn-frontend/app/api/ai/chat/route.ts`
|
||||
|
||||
**Add to chat request handling:**
|
||||
- Accept optional `projectId`
|
||||
- When present, load the project from PostgreSQL
|
||||
- Inject into the system prompt:
|
||||
- Project name, slug, repo URL
|
||||
- List of apps and their domains
|
||||
- Shared packages available
|
||||
- Turbo version and build command pattern
|
||||
- Add two new Gemini tools:
|
||||
- `deploy_app` — triggers `POST /api/projects/{projectId}/deploy`
|
||||
- `scaffold_app` — adds a new app folder to the monorepo via Gitea contents API
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
| Step | Task | File | Depends On |
|
||||
|------|------|------|-----------|
|
||||
| 1 | Move scaffold templates into `vibn-frontend/lib/scaffold/` | `lib/scaffold/turborepo/**` | — |
|
||||
| 2 | Update project creation to push scaffold | `app/api/projects/create/route.ts` | Step 1 |
|
||||
| 3 | Add app tracking fields to project data | `app/api/projects/create/route.ts` | Step 2 |
|
||||
| 4 | Add `createMonorepoAppService` to Coolify lib | `lib/coolify.ts` | — |
|
||||
| 5 | Wire Coolify per-app provisioning into project creation | `app/api/projects/create/route.ts` | Steps 3, 4 |
|
||||
| 6 | Add deploy route | `app/api/projects/[projectId]/deploy/route.ts` | Step 4 |
|
||||
| 7 | Inject monorepo context into AI chat | `app/api/ai/chat/route.ts` | Step 3 |
|
||||
|
||||
---
|
||||
|
||||
## What Does Not Change
|
||||
|
||||
- Gitea as source control — same, one repo per project (already the case)
|
||||
- Coolify as deployment host — same, extended with per-app services
|
||||
- NextAuth for auth — unchanged
|
||||
- PostgreSQL + JSONB for project storage — unchanged
|
||||
- `lib/gitea.ts` and `lib/coolify.ts` — extended, not replaced
|
||||
- No Vercel dependency anywhere
|
||||
501
docs_archive/VIBN_PRD.md
Normal file
501
docs_archive/VIBN_PRD.md
Normal file
@@ -0,0 +1,501 @@
|
||||
# vibn — Product Requirements Document
|
||||
|
||||
**Version:** 1.0
|
||||
**Date:** March 2026
|
||||
**Author:** Mark Henderson / Atlas AI
|
||||
**Status:** Draft
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
vibn is a template-first SaaS product builder for non-technical founders. It turns a product idea into a fully deployed, live web application — without writing code. Users describe their idea through a guided 6-phase wizard (Discover → Architect → Design → Market → Build), and vibn's AI agents scaffold, build, and deploy the product onto the user's own self-hosted infrastructure (Gitea + Coolify). vibn is positioned as "Shopify for building software": opinionated, template-driven, and designed to dramatically reduce failure rates compared to blank-page AI coding tools. The target customer is a non-technical or low-technical founder who has a validated idea and wants to get to a live product and first paying user in under 72 hours.
|
||||
|
||||
---
|
||||
|
||||
## 2. Problem Statement
|
||||
|
||||
**The problem:** Non-technical founders cannot build software products without hiring developers or becoming one themselves. Existing AI coding tools (Cursor, Replit, v0) assume technical literacy. General-purpose AI (ChatGPT) produces code snippets that can't be deployed. Developer agencies cost $50–200k and take 6–12 months. The gap between "I have a great idea" and "I have a live product" remains enormous.
|
||||
|
||||
**Who experiences it:** Solo founders, domain experts (lawyers, trainers, consultants, operators) who want to productize a service, career changers, and micro-agencies wanting to scale client delivery without headcount.
|
||||
|
||||
**What they do today instead:**
|
||||
- Hire a freelance developer (slow, expensive, dependency risk)
|
||||
- Use no-code tools like Bubble or Webflow (limited, technical ceiling, hard to customize)
|
||||
- Try to learn to code (fails 90%+ of the time for non-native coders)
|
||||
- Sit on the idea indefinitely
|
||||
|
||||
**Why current alternatives fall short:**
|
||||
- Bubble/Webflow: Hit a wall as soon as real backend logic is needed; proprietary and not portable
|
||||
- AI coding tools: Require knowing what to ask, how to debug, how to deploy — the hard parts remain
|
||||
- Agencies: Take too long, cost too much, and the founder loses control
|
||||
- Hiring: Creates single-point-of-failure dependency
|
||||
|
||||
---
|
||||
|
||||
## 3. Vision & Success Metrics
|
||||
|
||||
**Vision:** vibn is the fastest path from idea to live product for anyone who can describe what they want. It removes every technical barrier between a non-technical founder and a running SaaS — planning, building, deploying, and marketing — while keeping the user in control and the infrastructure on their own servers.
|
||||
|
||||
**Success metrics (v1, 6-month targets):**
|
||||
|
||||
| Metric | Target |
|
||||
|---|---|
|
||||
| Time from signup to deployed app | < 72 hours (median) |
|
||||
| % of builds that deploy successfully on first attempt | > 85% |
|
||||
| Monthly active builders | 500 |
|
||||
| Projects reaching "live" status | 200 |
|
||||
| Net Revenue Retention (NRR) | > 100% |
|
||||
| Gross margin | > 65% |
|
||||
| Paying customers at 6 months | 150 |
|
||||
|
||||
**Key milestones:**
|
||||
- Month 1: Private beta with 10 hand-selected founders
|
||||
- Month 2: 50 projects initiated, first 20 live
|
||||
- Month 3: Public waitlist open, payment enabled
|
||||
- Month 6: Self-serve onboarding, 150 paying customers
|
||||
|
||||
---
|
||||
|
||||
## 4. Target Users & Personas
|
||||
|
||||
### Persona A — The Non-Technical Founder ("The Builder")
|
||||
- **Who:** A domain expert (ex: fitness coach, lawyer, ops manager) who has identified a software problem in their industry. No coding background. Has validated the idea informally with peers.
|
||||
- **Primary goal:** Go from idea to a working product they can show to real users and start charging for.
|
||||
- **Pain points:** Doesn't know where to start technically; has been burned by developers before; doesn't trust no-code tools for "real" products; overwhelmed by choices.
|
||||
- **Happy path:** Describes idea in the Discover phase → reviews and approves architecture → picks a visual style → sets brand voice → hits "Build" → shares a live URL within 48 hours.
|
||||
- **What they value:** Speed, control, clarity. They want to see something real, not a mock.
|
||||
|
||||
### Persona B — The Micro-Agency Operator ("The Producer")
|
||||
- **Who:** A freelancer or small agency (1–5 people) that builds web products for clients. Currently using developers or outsourcing. Wants to deliver faster and at higher margin.
|
||||
- **Primary goal:** Build client products in days, not months. Manage multiple projects from one dashboard. Bill clients for AI compute costs with markup.
|
||||
- **Pain points:** Hiring developers is expensive and slow. Coordinating freelancers is painful. Margins are thin. Can't take on more work without more headcount.
|
||||
- **Happy path:** Creates a new client project → walks through wizard on behalf of client → client reviews and approves → vibn builds and deploys → operator bills client with AI cost markup shown.
|
||||
- **What they value:** Speed, multi-project management, billing visibility, client-presentable output.
|
||||
|
||||
### Permissions Matrix
|
||||
|
||||
| Capability | Builder (own project) | Producer (client project) |
|
||||
|---|---|---|
|
||||
| Create project | ✓ | ✓ |
|
||||
| Run wizard phases | ✓ | ✓ |
|
||||
| Trigger build | ✓ | ✓ |
|
||||
| View live app URL | ✓ | ✓ |
|
||||
| View cost breakdown | Own costs only | Full client cost breakdown |
|
||||
| Bill client | — | ✓ |
|
||||
| Manage custom domain | ✓ | ✓ |
|
||||
| Access Gitea repo | ✓ | ✓ |
|
||||
| Request changes post-launch | ✓ | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## 5. User Flows & Journeys
|
||||
|
||||
### Primary Flow — New Builder (Non-Technical Founder)
|
||||
|
||||
1. Lands on vibn marketing site (`vibn.app`)
|
||||
2. Clicks "Get started free" → enters email
|
||||
3. Completes **Welcome phase**: sees 5-step overview of what vibn does, clicks "Let's build it"
|
||||
4. **Discover phase**: guided 6-question chat conversation — idea, problem, users, value, revenue, features. Sees live PRD panel filling in as they answer. Continues when all 6 answered.
|
||||
5. **Architect phase**: Reviews AI-generated architecture (frontend, backend, auth, payments, email, hosting). Each block shows the chosen option and why. Can edit any block. Confirms with "Plan looks good — next: Design".
|
||||
6. **Design phase**: Picks visual feel from 6 presets (Clean, Bold, Warm, Fresh, Electric, Luxury). Sees live mock of their app updating in real time.
|
||||
7. **Market phase**: Sets brand voice (sliders for tone, style, personality). Reviews and edits 3 AI-generated content topics. Previews their marketing website style.
|
||||
8. **Build phase**: Reviews full summary (auth, payments, email, style, website, topics, pages). Clicks "Build my MVP". Watches 12-step live build progress. Receives live URL + Gitea repo link.
|
||||
9. Redirected to **Dashboard** — sees project as "Live" with URL, stats, and action buttons.
|
||||
|
||||
### Secondary Flow — Returning User (Dashboard → Change Request)
|
||||
|
||||
1. Logs in → lands on Dashboard (projects screen)
|
||||
2. Selects an existing project → clicks "Build" or "Grow"
|
||||
3. Enters the relevant phase of the wizard in edit mode
|
||||
4. Makes changes → re-triggers partial build
|
||||
5. Returns to Dashboard, sees updated deployment
|
||||
|
||||
### Secondary Flow — Agency Producer (Client Project)
|
||||
|
||||
1. Logs in → clicks "+ New project"
|
||||
2. Tags project as "Client" and enters client name
|
||||
3. Walks through wizard as normal (can be done with client present or on their behalf)
|
||||
4. After build: sees project card with "Client" tag, cost breakdown, and "Bill →" button
|
||||
5. Clicks "Bill →" → generates itemized invoice (LLM costs + compute + markup)
|
||||
6. Views unbilled total across all clients in Billing screen
|
||||
|
||||
### Onboarding Flow
|
||||
|
||||
1. Email signup → verify email
|
||||
2. Welcome wizard (Welcome phase of builder)
|
||||
3. First project created automatically — user is never left on an empty dashboard
|
||||
4. If user exits mid-wizard, project is saved as draft and resumed on next login
|
||||
|
||||
### Error / Recovery Flows
|
||||
|
||||
- **Build fails mid-way:** User sees which step failed, error plain-English explanation, and "Retry" button. Failed build does not charge full credits.
|
||||
- **Payment setup missing:** If user chose Stripe billing in Architect but hasn't connected Stripe, they're prompted before Build is triggered.
|
||||
- **Custom domain fails DNS:** In-app guide walks through DNS setup; app is still live on vibn subdomain in the meantime.
|
||||
- **User exits mid-wizard:** Progress is auto-saved per phase. Resumable from Dashboard.
|
||||
|
||||
---
|
||||
|
||||
## 6. Feature Requirements
|
||||
|
||||
### 6.1 Must Have (v1 Launch)
|
||||
|
||||
**Builder Wizard — 6-Phase Flow**
|
||||
- *Description:* The core product experience. A sequential, guided wizard that takes a user from idea to deployed product.
|
||||
- *User story:* As a non-technical founder, I want to answer plain-English questions and have AI figure out the architecture, code, and deployment — so I never have to think about technical choices.
|
||||
- *Acceptance criteria:* All 6 phases completable end-to-end. Progress saved between sessions. Each phase produces a visible artifact (PRD, architecture plan, design preview, etc.).
|
||||
|
||||
**Discover Phase — Conversational PRD Builder**
|
||||
- *Description:* 6-question guided chat. Each answer populates a live PRD panel. AI synthesizes answers into a structured product plan.
|
||||
- *Acceptance criteria:* All 6 questions answered before proceeding. PRD panel shows structured output per question. "Plan looks good" CTA advances to next phase.
|
||||
|
||||
**Architect Phase — Architecture Selection**
|
||||
- *Description:* AI proposes 6 architecture blocks (Frontend, Backend, Auth, Payments, Email, Hosting). Each block is explainable in plain English and editable.
|
||||
- *Acceptance criteria:* All 6 blocks shown with default selection and rationale. User can change any block via dropdown/modal. Hosting block is locked to self-hosted (Coolify + Gitea). Pages list shown.
|
||||
|
||||
**Design Phase — Visual Feel Picker**
|
||||
- *Description:* 6 visual presets. Selecting a preset updates a live app mock in real time.
|
||||
- *Acceptance criteria:* 6 presets rendered correctly. Live mock updates within 300ms of selection. Continue CTA available once selection made.
|
||||
|
||||
**Market Phase — Voice + Topics + Website**
|
||||
- *Description:* Brand voice sliders (tone, style, personality). AI-generated content topics (add/edit/remove). Website style picker with live preview.
|
||||
- *Acceptance criteria:* Voice sliders affect AI content generation downstream. Topics editable with add/remove. Website preview updates with style selection.
|
||||
|
||||
**Build Phase — Review + Deploy**
|
||||
- *Description:* Full summary of all decisions. "Build my MVP" button triggers 12-step build pipeline. Live progress shown. On completion: app URL + Gitea link.
|
||||
- *Acceptance criteria:* All decisions shown accurately from prior phases. Build progress shows step-by-step status. On success: live URL displayed and functional. On failure: clear error + retry option.
|
||||
|
||||
**Dashboard — Projects View**
|
||||
- *Description:* Home screen after login. Shows all projects with status, basic stats, and actions.
|
||||
- *Acceptance criteria:* Projects shown as cards with status (Live/Building), URL, and key stats (visitors, signups, MRR). "Continue building" for in-progress builds. "+ New project" creates a new wizard session.
|
||||
|
||||
**Dashboard — Billing View (Agency)**
|
||||
- *Description:* Client billing tab showing unbilled costs by client, LLM/compute/other breakdown, invoice generation.
|
||||
- *Acceptance criteria:* Unbilled totals accurate. "Bill →" generates invoice. Cost log shows itemized charges.
|
||||
|
||||
**Authentication**
|
||||
- *Description:* Email-based signup/login for the vibn platform itself.
|
||||
- *Acceptance criteria:* Email + password signup. Email verification required. Forgot password flow. Session persists across browser restarts.
|
||||
|
||||
**Deployment Integration (Coolify + Gitea)**
|
||||
- *Description:* Every built project is pushed to user's Gitea repo and deployed via Coolify automatically.
|
||||
- *Acceptance criteria:* Gitea repo created on build start. Code committed on completion. Coolify deploy triggered automatically. App live on `[project].vibn.app` subdomain.
|
||||
|
||||
**Floating AI Chat (Assist)**
|
||||
- *Description:* Phase-aware chat assistant available throughout the builder wizard. Persists across phase navigation.
|
||||
- *Acceptance criteria:* Chat available from Discover through Build phases. Phase-specific starter suggestions. Chat history persists across phase changes. Does not reset on navigation.
|
||||
|
||||
---
|
||||
|
||||
### 6.2 Should Have (Fast Follow — Months 2–3)
|
||||
|
||||
**Custom Domain Support**
|
||||
- Users can connect their own domain to a deployed project.
|
||||
- In-app DNS setup guide. SSL auto-provisioned via Coolify.
|
||||
|
||||
**Post-Build Change Requests**
|
||||
- Users can request changes to their live product in plain English.
|
||||
- AI interprets, diffs the codebase, applies change, redeploys.
|
||||
|
||||
**Marketing Autopilot**
|
||||
- AI generates and schedules blog posts, email newsletters, and social content based on topics defined in Market phase.
|
||||
- Initial manual approval required; can be set to auto-publish.
|
||||
|
||||
**Credit Usage Display**
|
||||
- Show real-time credit consumption during builds.
|
||||
- Warn before triggering tasks estimated to cost > X credits.
|
||||
- User-configurable spending cap per project.
|
||||
|
||||
**Template Marketplace Access**
|
||||
- Starter templates browsable before creating a project.
|
||||
- Template selection sets pre-configured architecture defaults.
|
||||
|
||||
---
|
||||
|
||||
### 6.3 Could Have (Future — Months 4–6)
|
||||
|
||||
**Client-Facing Project Portal**
|
||||
- Agency clients can log in to review progress, approve phases, and view their live app — without accessing the vibn dashboard directly.
|
||||
|
||||
**Stripe Connect for Invoice Payment**
|
||||
- Agency operators can receive payment from clients directly via vibn.
|
||||
|
||||
**Analytics Dashboard (per project)**
|
||||
- Built-in lightweight analytics (page views, signups, MRR) sourced from the deployed app's database.
|
||||
|
||||
**Invite Team Members**
|
||||
- Multiple vibn users can collaborate on a single project.
|
||||
|
||||
**Mobile App (iOS/Android)**
|
||||
- Native app for monitoring live projects and approving content scheduled by marketing autopilot.
|
||||
|
||||
**Template Marketplace (Sell/Buy)**
|
||||
- Third-party developers can submit templates; users can purchase premium templates.
|
||||
|
||||
---
|
||||
|
||||
### 6.4 Explicitly Out of Scope (v1)
|
||||
|
||||
| Feature | Reason excluded |
|
||||
|---|---|
|
||||
| Mobile app (iOS/Android) builder output | All v1 builds are web apps; native app generation is a later capability |
|
||||
| Real-time multi-user collaboration on wizard | Single-user flow only in v1; collaboration is v2 |
|
||||
| Self-hosting vibn itself (white-label) | Not offered in v1; Enterprise tier future consideration |
|
||||
| AI voice/video generation | Out of scope; vibn generates text and code only |
|
||||
| Direct Stripe Connect marketplace | Invoice workflow is manual export only in v1 |
|
||||
| Custom AI model selection by users | Model routing is automatic; users do not choose models |
|
||||
| Offline/desktop app | Web-only |
|
||||
| HIPAA / SOC2 compliance | Out of scope for v1; required before any healthcare customers |
|
||||
|
||||
---
|
||||
|
||||
## 7. Screen-by-Screen Specification
|
||||
|
||||
### 7.1 Marketing Website (`vibn.app`)
|
||||
- **Purpose:** Acquire non-technical founders. Convert to "Get started free" or "Log in".
|
||||
- **Key elements:** Hero headline ("You have the idea. We handle everything else."), 5-step how-it-works, pull quotes from 3 founders, stats bar (280+ launched, 72h avg, 4.9 rating), empathy section, final CTA.
|
||||
- **Actions:** Get started free → Welcome wizard. Log in → Dashboard.
|
||||
- **Notes:** Lora serif + Inter sans, ink/parchment palette. No color accents.
|
||||
|
||||
### 7.2 Welcome Phase
|
||||
- **Purpose:** Orient the user, set expectations, build confidence.
|
||||
- **Key elements:** 5-step overview of the vibn process. "Let's build it →" CTA. Tagline: "From idea to live product. No code needed."
|
||||
- **Actions:** "Let's build it" → Discover phase.
|
||||
|
||||
### 7.3 Builder Sidebar (phases 2–6)
|
||||
- **Purpose:** Persistent navigation and progress tracking during the wizard.
|
||||
- **Key elements:** vibn logo. Progress checklist (Product plan, Architecture, Product design, Marketing). Phase nav (Discover, Architect, Design, Market, Build MVP). User avatar + name + plan at bottom.
|
||||
- **Notes:** Sidebar is hidden on Welcome and Website screens. Always visible during builder phases.
|
||||
|
||||
### 7.4 Discover Phase
|
||||
- **Purpose:** Capture the product idea as structured data. Output: PRD.
|
||||
- **Key elements (left panel):** Phase header, progress bar across 6 questions, AI message bubble per question, user input field.
|
||||
- **Key elements (right panel):** "Your Product Plan" — live-updating sections: Idea, Problem, Users, Value, Revenue, Features. Each fills in as answered.
|
||||
- **Actions:** User types answers. AI asks follow-up. After 6 questions: "Plan looks good — next: Architect →" CTA.
|
||||
|
||||
### 7.5 Architect Phase
|
||||
- **Purpose:** Let user review and confirm the technical architecture in plain English.
|
||||
- **Key elements (center):** Phase header. 6 architecture blocks as horizontal-scrollable cards (Frontend, Backend, Auth, Payments, Email, Hosting). Each card shows: icon, chosen option, plain-English explanation, "Change →" button. "Why?" expandable for each block. Infra note (Coolify + Gitea).
|
||||
- **Key elements (right panel):** "Pages to Build" — grouped by Public, Auth, App, Payments.
|
||||
- **Actions:** "Change →" opens selection modal with 2–4 alternatives per block. "Confirm — next: Design →" CTA.
|
||||
|
||||
### 7.6 Design Phase
|
||||
- **Purpose:** Choose a visual style for the product.
|
||||
- **Key elements (left):** 6 feel cards (Clean, Bold, Warm, Fresh, Electric, Luxury) — each with label, reference product, and color/style preview.
|
||||
- **Key elements (right):** Live app mock that updates to reflect selected feel. Shows a plausible dashboard UI in that style.
|
||||
- **Actions:** Click a feel card → mock updates. "Next: Market →" CTA.
|
||||
|
||||
### 7.7 Market Phase — Voice Tab
|
||||
- **Purpose:** Set the brand voice for AI-generated content.
|
||||
- **Key elements:** 3 slider pairs: Tone (Friendly ↔ Professional), Style (Conversational ↔ Precise), Personality (Warm ↔ Direct). "Voice preview" section shows how the brand would introduce itself.
|
||||
- **Actions:** Sliders adjust in real time. Tab switches to Topics or Website.
|
||||
|
||||
### 7.8 Market Phase — Topics Tab
|
||||
- **Purpose:** Define the content topics AI will generate and publish.
|
||||
- **Key elements:** 3 pre-generated topic cards (title, angle, channels). Each editable. "Add topic" button. Remove button per card.
|
||||
- **Actions:** Edit, add, remove topics. "Next: Website →" tab.
|
||||
|
||||
### 7.9 Market Phase — Website Tab
|
||||
- **Purpose:** Choose the marketing website visual style.
|
||||
- **Key elements:** 4 website style options (Editorial, Startup Energy, Ultra Minimal, Warm & Human). Live website preview panel updates on selection.
|
||||
- **Actions:** Click style → preview updates. "Plan looks good — next: Build →" CTA.
|
||||
|
||||
### 7.10 Build Phase — Review Screen
|
||||
- **Purpose:** Final review before triggering the build.
|
||||
- **Key elements:** Summary grid (Auth, Payments, Email, Product Style, Website Style, Campaign Topics). Pages list (by group). Infra deployment note. "▲ Build my MVP" button. Disclaimer: ~15 minutes, refinable after launch.
|
||||
- **Actions:** "Build my MVP" → transitions to Build Progress screen.
|
||||
|
||||
### 7.11 Build Phase — Progress Screen
|
||||
- **Purpose:** Show real-time build progress.
|
||||
- **Key elements:** 12-step checklist with: completed steps (green checkmark), active step (animated indicator), pending steps (grey). Step label + detail line. Progress header showing step count.
|
||||
- **On completion:** "Your MVP is live" screen — app URL ("Open my app ↗"), Gitea link ("View in Gitea ↗"), "Your next 3 actions" card.
|
||||
|
||||
### 7.12 Dashboard — Projects Screen
|
||||
- **Purpose:** Manage all projects from one place.
|
||||
- **Key elements:** "Your projects" header with count. Unbilled total button (if agency projects exist). "+ New project" button. Project cards (2-column grid): status thumbnail, project identity (name, URL, client if applicable), status pill (Live/Building), cost strip (client projects), stats (visitors, signups, MRR), action buttons (Build, Grow, ↗). New project CTA card (dashed border, "+" icon).
|
||||
- **Activity feed:** Recent events across all projects (content published, new signups, build events).
|
||||
|
||||
### 7.13 Dashboard — Billing Screen (Client Billing tab)
|
||||
- **Purpose:** Manage invoicing for agency operators.
|
||||
- **Key elements:** Summary stats (total unbilled, LLM costs, compute, other). Billing table (by client, by month). Each row: project, LLM, compute, other, total, status pill. "Invoice" button per unbilled row. "Generate invoice" button (global).
|
||||
|
||||
### 7.14 Dashboard — Billing Screen (Cost Tracker tab)
|
||||
- **Purpose:** Understand AI and infrastructure cost breakdown.
|
||||
- **Key elements:** LLM usage breakdown (code gen, content, chat assist) with bar charts. Infrastructure breakdown (hosting, database, email, domain). Recent charges log (time, description, project, cost).
|
||||
|
||||
### 7.15 Floating AI Chat (Assist)
|
||||
- **Purpose:** On-demand AI help throughout the wizard.
|
||||
- **Key elements:** Dark header with "Assist · [phase]" + live green dot. Message thread (user + assistant bubbles). Phase-specific starter suggestions (3 clickable). Input field + send button.
|
||||
- **Behavior:** Persists open/closed state and message history across phase changes. Accessible via 💬 bubble button at bottom right.
|
||||
|
||||
---
|
||||
|
||||
## 8. Business Model & Pricing
|
||||
|
||||
### Revenue Model
|
||||
**Subscription + Credits** (not unlimited AI)
|
||||
|
||||
The subscription covers fixed platform value (infrastructure orchestration, templates, UX, dashboard, Gitea/Coolify integration, team ops). Credits cover variable AI compute costs (LLM calls across Tier A/B/C, build pipelines, content generation).
|
||||
|
||||
### Pricing Tiers
|
||||
|
||||
| Tier | Price | Templates | Projects | Credits included | Target |
|
||||
|---|---|---|---|---|---|
|
||||
| **Free** | $0/mo | Starter only | 1 active | 50 credits/mo | Evaluators |
|
||||
| **Builder** | $49/mo | Starter + Builder | 3 active | 500 credits/mo | Solo founders |
|
||||
| **Pro** | $149/mo | All templates | Unlimited | 2,000 credits/mo | Active builders + agencies |
|
||||
| **Enterprise** | Custom | Custom + private | Unlimited | Custom | Teams, compliance needs |
|
||||
|
||||
**Credit top-ups:** Available at $0.10/credit (10 credits = $1). Minimum top-up: $10.
|
||||
|
||||
### AI Cost Structure (Internal)
|
||||
|
||||
Three-tier model routing:
|
||||
- **Tier A (40% of calls):** Gemini Flash-class — orchestration, summaries, routing, log parsing. ~$0.0001/1k tokens.
|
||||
- **Tier B (45% of calls):** Mid-tier coding model (GLM-5 or Qwen Coder via Vertex) — code gen, feature building, refactors. ~$0.002/1k tokens.
|
||||
- **Tier C (15% of calls):** Premium escalation (Claude Sonnet or Gemini Pro) — architecture decisions, high-risk changes, repeated failures. ~$0.015/1k tokens.
|
||||
|
||||
**Credit pricing:** Each credit = approximately $0.10 of platform value (AI + margin). Exact credit cost per action surfaced to user before triggering high-cost tasks.
|
||||
|
||||
### Cost Estimate Per Build (v1 template-based app)
|
||||
| Item | Estimated cost |
|
||||
|---|---|
|
||||
| Discover/Architect/Design/Market phases (Tier A/B) | ~$0.80 |
|
||||
| Full code generation (Tier B, ~8,000 LOC) | ~$2.40 |
|
||||
| Deployment orchestration | ~$0.20 |
|
||||
| **Total per build** | **~$3.40** |
|
||||
| **Charged at markup** | **~40 credits ($4.00)** |
|
||||
|
||||
At $49/mo (500 credits), a Builder subscriber can complete ~12 full builds per month within plan.
|
||||
|
||||
---
|
||||
|
||||
## 9. Integrations & External Dependencies
|
||||
|
||||
| Integration | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| **Gitea (self-hosted)** | Code storage and version control for every built project | Required. All repos pushed here on build completion. |
|
||||
| **Coolify (self-hosted)** | Build pipeline, deployment, container orchestration | Required. Auto-deploys on Gitea push. |
|
||||
| **Google Vertex AI** | Tier A/B/C model calls | Primary AI provider. Gemini Flash (A), mid-tier MaaS (B), Claude/Gemini Pro (C). |
|
||||
| **Stripe** | Subscription billing for vibn platform fees | Customers pay vibn via Stripe. Stripe not required in built apps unless user selects it in Architect. |
|
||||
| **Resend / Postmark** | Transactional emails (signup, password reset, notifications) | For vibn platform emails. Built apps may use same if email selected in Architect. |
|
||||
| **PostgreSQL** | Platform database (conversations, project state, tasks, billing) | Self-hosted in hot tier. |
|
||||
| **Redis** | Job queue, pubsub for build pipeline events | Optional but recommended for build reliability. |
|
||||
|
||||
**No external data import requirements in v1.** Built apps start fresh; no migration tooling in scope.
|
||||
|
||||
---
|
||||
|
||||
## 10. Non-Functional Requirements
|
||||
|
||||
### Performance
|
||||
- Wizard phase transitions: < 200ms
|
||||
- Live design mock updates: < 300ms after style selection
|
||||
- Build pipeline: Median < 15 minutes for a template-based app
|
||||
- Dashboard load: < 1 second (projects list)
|
||||
- AI chat response: First token within 1 second
|
||||
|
||||
### Platform
|
||||
- **Primary:** Web (desktop browser) — Chrome, Safari, Firefox, Edge
|
||||
- **Secondary:** Responsive mobile web for dashboard viewing (not wizard)
|
||||
- **Not in scope v1:** Native iOS/Android apps
|
||||
|
||||
### Accessibility
|
||||
- WCAG 2.1 AA compliance for all interactive elements
|
||||
- Keyboard navigable wizard phases
|
||||
- Sufficient color contrast across all design tokens (ink on paper palette passes AA)
|
||||
|
||||
### Compliance & Regulatory
|
||||
- **GDPR:** Data processing agreements available for EU users. User data deletable on request.
|
||||
- **PCI DSS:** vibn does not store card data; handled entirely by Stripe.
|
||||
- **HIPAA:** Out of scope for v1. No healthcare data processed.
|
||||
- **SOC 2:** Target for Enterprise tier; not required at launch.
|
||||
|
||||
### Data Privacy & Security
|
||||
- All user project code stored in user's own Gitea instance (user owns their data)
|
||||
- vibn platform database stores: conversation history, project metadata, billing records
|
||||
- AI conversations not used for model training (Vertex API terms)
|
||||
- Secrets (API keys, Stripe keys) stored encrypted, never logged
|
||||
- Build logs retained for 30 days, then purged
|
||||
|
||||
### Scalability Assumptions (v1)
|
||||
- Designed for 500 MAU at launch
|
||||
- Build pipeline: 20 concurrent builds supported
|
||||
- Horizontal scaling of worker pool via Coolify
|
||||
|
||||
---
|
||||
|
||||
## 11. Risks & Mitigations
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|---|---|---|---|
|
||||
| Build success rate < 85% due to AI code quality | Medium | High | Template-first architecture dramatically reduces open-ended generation. Fallback retry mechanism. Tiered escalation to better model on repeated failure. |
|
||||
| LLM costs exceed credit pricing margins | Medium | High | 3-tier routing keeps 85% of calls on cheap models. Per-step token limits. Aggressive context summarization. Max retries cap (3). |
|
||||
| Users don't understand "credits" model | High | Medium | In-app cost estimation before every build. Plain-English explanations. "This build will use ~40 credits." Spending caps user-configurable. |
|
||||
| Coolify/Gitea self-hosted infra reliability | Low | High | Hot tier always-on. Healthcheck monitoring. Auto-restart policies. Graceful failure messaging in build UI. |
|
||||
| Non-technical users abandon wizard mid-way | High | Medium | Progress auto-saved per phase. Resume from dashboard. Floating AI chat for unblocking. Encourage "good enough" answers — no wrong answers in Discover. |
|
||||
| Scope creep in wizard phases | Medium | Medium | Each phase has a strict set of decisions. No free-form architecture input. Locked hosting block prevents deviation. |
|
||||
| Competition from Replit, Bolt, v0 | High | Medium | Differentiator is self-hosted infra (user owns everything), template-first (higher success rate), and the end-to-end wizard (no coding literacy required). |
|
||||
| Agency use case underperforms | Low | Low | Agency (Producer persona) is v1 secondary target. Builder persona is primary. Billing screen can be iterated post-launch. |
|
||||
|
||||
---
|
||||
|
||||
## 12. Open Questions & Assumptions
|
||||
|
||||
### Open Questions
|
||||
|
||||
1. **Template library scope at launch:** How many starter templates exist at v1 launch? What are they? (Minimum: SaaS CRUD + landing page. What else?)
|
||||
2. **Subdomain structure:** Are projects deployed to `[project-name].vibn.app` or `[user-slug]-[project].vibn.app`? (Collision risk if single namespace.)
|
||||
3. **Build pipeline timing:** Is 15-minute median build time achievable for first template? What's the P95?
|
||||
4. **Gitea/Coolify provisioning:** Is each user getting their own Gitea org? How are Coolify environments namespaced per user?
|
||||
5. **Free tier limits:** Should free tier require a credit card? (Conversion vs. abuse risk tradeoff.)
|
||||
6. **Change requests post-launch:** How are iterative changes billed? Per-change credit cost, or separate workflow?
|
||||
7. **Marketing autopilot publishing:** In v1, does AI content require manual approval before publishing, or is auto-publish available?
|
||||
8. **Wizard re-entry:** Can a user go back and redo an earlier phase after completing Build? Does this trigger a rebuild?
|
||||
|
||||
### Assumptions Made
|
||||
|
||||
- vibn's Gitea and Coolify infrastructure are already operational and stable before v1 user onboarding begins.
|
||||
- Template-based builds (vs. blank-page builds) keep success rates above 85%.
|
||||
- Non-technical founders are willing to pay $49–$149/month for a solution that reliably delivers a live product.
|
||||
- The 6-phase wizard is completable in one sitting (~20–30 minutes) for a user with a clear idea.
|
||||
- Vertex AI API access and model availability (Gemini Flash, mid-tier MaaS) is stable and within budget.
|
||||
- Users do not need to understand or manage their Gitea/Coolify infrastructure directly — vibn abstracts it entirely.
|
||||
- The primary acquisition channel for v1 is content marketing and founder communities (not paid ads).
|
||||
|
||||
---
|
||||
|
||||
## 13. Appendix
|
||||
|
||||
### Glossary
|
||||
|
||||
| Term | Definition |
|
||||
|---|---|
|
||||
| **Build** | The automated process of AI generating code, committing to Gitea, and deploying via Coolify |
|
||||
| **Wizard** | The 6-phase guided flow: Discover → Architect → Design → Market → Build |
|
||||
| **Phase** | A single stage of the wizard, each producing a specific artifact |
|
||||
| **Template** | A pre-built starter codebase that vibn AI builds upon instead of generating from scratch |
|
||||
| **Credits** | vibn's unit of AI compute consumption; consumed during builds, content generation, and chat |
|
||||
| **Hot tier** | Always-running shared infrastructure (API gateway, orchestrator, Postgres, Redis, Gitea, Coolify) |
|
||||
| **Cold tier** | Per-user on-demand containers (agent workspace instances, hibernated when inactive) |
|
||||
| **Tier A/B/C** | Three levels of AI model quality/cost, automatically routed by the orchestrator based on task complexity |
|
||||
| **Producer** | A vibn user building products for clients (agency use case) |
|
||||
| **Builder** | A vibn user building a product for themselves (founder use case) |
|
||||
| **PRD** | Product Requirements Document — the structured output of the Discover phase |
|
||||
| **Gitea** | Self-hosted open-source Git service; stores all project codebases |
|
||||
| **Coolify** | Self-hosted deployment platform; builds and runs all deployed apps |
|
||||
|
||||
### Reference Materials
|
||||
- Product strategy document: `product-idea-a.md`
|
||||
- Builder wizard UI prototype: `preview-assist-ui/src/App.jsx`
|
||||
- Marketing website prototype: `preview-assist-ui/src/Website.jsx`
|
||||
- Dashboard prototype: `preview-assist-ui/src/Dashboard.jsx`
|
||||
- PRD agent system prompt: `prd-agent-prompt.pdf`
|
||||
|
||||
### Competitor Reference
|
||||
- **Bolt.new / Lovable:** AI coding from scratch; no deployment, no templates, requires iteration by user
|
||||
- **Replit:** Strong coding environment; technical literacy required; no guided wizard
|
||||
- **Webflow:** No-code UI builder; no real backend; visual but limited
|
||||
- **Bubble:** No-code with backend; steep learning curve; proprietary lock-in
|
||||
- **v0 (Vercel):** UI generation only; no deployment, no product planning
|
||||
- **Agencies:** Custom development; 6–12 month timelines; $50k–$200k budgets
|
||||
BIN
docs_archive/prd-agent-prompt.pdf
Normal file
BIN
docs_archive/prd-agent-prompt.pdf
Normal file
Binary file not shown.
409
docs_archive/product-idea-a.md
Normal file
409
docs_archive/product-idea-a.md
Normal file
@@ -0,0 +1,409 @@
|
||||
VibnAI Plan Summary — “Shopify Template Model” + Your Infra + Model Routing + Pricing
|
||||
|
||||
Below is the consolidated plan we’ve converged on: VibnAI as a template-first product builder (Shopify-style), with your own hosted infra, and usage-based AI credits powered by Vertex marketplace models with smart routing.
|
||||
|
||||
1) Product Strategy: VibnAI Is Shopify for Building Software
|
||||
Core positioning
|
||||
|
||||
VibnAI is not “blank page AI coding.”
|
||||
|
||||
VibnAI is:
|
||||
|
||||
Build production-ready apps from elite starter templates
|
||||
then customize via guided AI workflows.
|
||||
|
||||
This reduces:
|
||||
|
||||
token burn
|
||||
|
||||
failure loops
|
||||
|
||||
architectural ambiguity
|
||||
|
||||
debugging chaos
|
||||
|
||||
And increases:
|
||||
|
||||
predictability
|
||||
|
||||
success rate
|
||||
|
||||
margins
|
||||
|
||||
retention
|
||||
|
||||
Template-first rule
|
||||
|
||||
No project starts from an empty repo by default.
|
||||
|
||||
Users must choose:
|
||||
|
||||
a starter template, or
|
||||
|
||||
“Advanced: Custom Build” (explicitly warned as costlier)
|
||||
|
||||
2) Platform Architecture: Your Infra + Event-Driven AI
|
||||
High-level architecture decisions
|
||||
|
||||
You host the infrastructure layer yourself (Hot + Cold tiers). AI compute is purchased via credits.
|
||||
|
||||
Hot tier (shared, always running)
|
||||
|
||||
API Gateway (auth, WebSockets, rate limits)
|
||||
|
||||
Orchestrator service (task routing + state machine)
|
||||
|
||||
Job queue + worker pool
|
||||
|
||||
Postgres (conversations, tasks, state)
|
||||
|
||||
Redis (optional: queue/pubsub)
|
||||
|
||||
Gitea (code/content source-of-truth)
|
||||
|
||||
Coolify (deploys, logs, runtime orchestration)
|
||||
|
||||
Key rule: The hot tier is always on, but it should be cheap to run because it is mostly event-driven and does not constantly call expensive models.
|
||||
|
||||
Cold tier (per-user, on-demand)
|
||||
|
||||
Agent workspace containers
|
||||
|
||||
Hibernate / wake-on-access
|
||||
|
||||
Persistent storage volumes
|
||||
|
||||
“Master Orchestrator” behavior change (critical cost control)
|
||||
|
||||
Even if it’s “always running,” it should behave like:
|
||||
|
||||
event-driven
|
||||
|
||||
stateless compute
|
||||
|
||||
minimal model calls
|
||||
|
||||
structured memory, not replaying chat history
|
||||
|
||||
Structured memory > conversation replay
|
||||
Instead of resending entire conversation history, persist and inject:
|
||||
|
||||
project summary
|
||||
|
||||
architecture summary
|
||||
|
||||
repo map summary
|
||||
|
||||
deploy state
|
||||
|
||||
open tasks
|
||||
|
||||
known bugs
|
||||
|
||||
This is a major cost reducer.
|
||||
|
||||
3) AI Model Strategy: 3-Tier Routing (Cost-Efficient Orchestration)
|
||||
|
||||
You’re building your own agents, but the principle applies: choose models per tool/task.
|
||||
|
||||
Tier A / Tier B / Tier C (the blend)
|
||||
|
||||
We landed on this operational blend:
|
||||
|
||||
40% Tier A (cheap)
|
||||
|
||||
45% Tier B (mid / workhorse coder)
|
||||
|
||||
15% Tier C (premium escalation)
|
||||
|
||||
This is not arbitrary—it aligns with tool/task reality:
|
||||
|
||||
most actions are parsing, routing, search, summarizing (cheap)
|
||||
|
||||
most code edits and implementations are workhorse coding (mid)
|
||||
|
||||
only a small fraction require deep reasoning / high-stakes decisions (premium)
|
||||
|
||||
Tier purpose
|
||||
Tier A — Cheap “Utility / Router”
|
||||
|
||||
Use for:
|
||||
|
||||
routing decisions
|
||||
|
||||
summarizing logs, errors, context
|
||||
|
||||
file discovery + search interpretation
|
||||
|
||||
command suggestion drafts
|
||||
|
||||
task context updates
|
||||
|
||||
chat summaries / naming
|
||||
|
||||
monitoring analysis
|
||||
|
||||
This tier should handle the majority of orchestration.
|
||||
|
||||
Tier B — Workhorse Coding Model
|
||||
|
||||
Use for:
|
||||
|
||||
generating diffs
|
||||
|
||||
writing/refactoring code
|
||||
|
||||
tests
|
||||
|
||||
standard bug fixes
|
||||
|
||||
“agent mode” loops when tasks are scoped
|
||||
|
||||
iterating on features inside templates
|
||||
|
||||
This tier should handle most coding.
|
||||
|
||||
Tier C — Premium Escalation Model
|
||||
|
||||
Use only when:
|
||||
|
||||
architecture decisions
|
||||
|
||||
high-risk changes (deploy, infra, migrations)
|
||||
|
||||
cross-service debugging
|
||||
|
||||
persistent failures (2 failed iterations)
|
||||
|
||||
very large diffs / multi-file refactors
|
||||
|
||||
security-sensitive changes
|
||||
|
||||
This tier should be rare by design.
|
||||
|
||||
4) Vertex Models: What to Use in Each Tier
|
||||
|
||||
You wanted to stay on Google infra and Vertex marketplace/API models.
|
||||
|
||||
Recommended mapping (Vertex-first)
|
||||
Tier A (cheap)
|
||||
|
||||
Gemini Flash-class model (fast, low cost)
|
||||
Use for orchestration, summaries, extraction, routing, log parsing.
|
||||
|
||||
Tier B (mid / coding workhorse)
|
||||
|
||||
Pick one:
|
||||
|
||||
GLM-5 MaaS (Vertex) — strong reasoning + cost-effective
|
||||
|
||||
Qwen coder MaaS (Vertex) — strong coding, predictable cost
|
||||
|
||||
This model does the heavy lifting for code edits and feature building.
|
||||
|
||||
Tier C (premium escalation)
|
||||
|
||||
Pick one:
|
||||
|
||||
Claude Sonnet 4.6 on Vertex (reliability + long-chain coding)
|
||||
|
||||
or Gemini 3.1 Pro Preview (if it proves better for your workflows)
|
||||
|
||||
This is your “expert brain” used sparingly.
|
||||
|
||||
5) Routing Policy: How the System Chooses Models
|
||||
|
||||
You’re not letting users pick models manually. The orchestrator routes based on task complexity and risk.
|
||||
|
||||
Default rules
|
||||
|
||||
All “read/search/list/summarize” → Tier A
|
||||
|
||||
Most code edits/refactors/tests → Tier B
|
||||
|
||||
High-risk or repeated failure → Tier C
|
||||
|
||||
Escalation triggers (simple + effective)
|
||||
|
||||
Escalate Tier B → Tier C when any of these happen:
|
||||
|
||||
2 failed iterations (tests still failing, same error persists)
|
||||
|
||||
Touching >5 files
|
||||
|
||||
Diff size exceeds ~400 LOC changed
|
||||
|
||||
Deployment / infra / secrets / migration steps involved
|
||||
|
||||
Context pressure (approaching model limits)
|
||||
|
||||
De-escalation rule
|
||||
|
||||
Once the hard part is resolved (cause found / plan decided), drop back to Tier B for implementation.
|
||||
|
||||
6) Business Model: Subscription + Credits (Not “Unlimited AI”)
|
||||
|
||||
You clarified the intended split:
|
||||
|
||||
Subscription covers your fixed costs
|
||||
|
||||
Subscription pays for:
|
||||
|
||||
your hosted infrastructure (hot tier + shared services)
|
||||
|
||||
Agent workspace orchestration (cold tier)
|
||||
|
||||
your people costs (support, ops, ongoing development)
|
||||
|
||||
product value (templates, UX, dashboards, workflows)
|
||||
|
||||
baseline included usage / small AI overhead
|
||||
|
||||
Credits cover variable compute
|
||||
|
||||
Credits pay for:
|
||||
|
||||
model calls (Tier A/B/C)
|
||||
|
||||
heavy tasks (builds, refactors, debugging loops)
|
||||
|
||||
long chain tasks
|
||||
|
||||
autonomous agent execution
|
||||
|
||||
This protects you from heavy users and keeps margins predictable.
|
||||
|
||||
7) Template Access as a Tiered Product (Shopify-style)
|
||||
Templates are the moat
|
||||
|
||||
Templates reduce:
|
||||
|
||||
architecture planning cost
|
||||
|
||||
retry loops
|
||||
|
||||
token burn
|
||||
|
||||
complexity and failure rates
|
||||
|
||||
Templates also create:
|
||||
|
||||
differentiation
|
||||
|
||||
a marketplace opportunity later
|
||||
|
||||
compounding margins
|
||||
|
||||
Tiering via template access
|
||||
|
||||
Instead of just “more AI,” higher tiers unlock better starter systems.
|
||||
|
||||
Example approach:
|
||||
|
||||
Starter tier
|
||||
|
||||
landing page template
|
||||
|
||||
simple SaaS CRUD template
|
||||
|
||||
basic auth + Stripe
|
||||
|
||||
limited integrations
|
||||
|
||||
Builder tier
|
||||
|
||||
multi-tenant SaaS template
|
||||
|
||||
marketplace template
|
||||
|
||||
analytics dashboard template
|
||||
|
||||
stronger RBAC patterns
|
||||
|
||||
more integrations
|
||||
|
||||
Pro tier
|
||||
|
||||
“OpsOS / analytics warehouse” template
|
||||
|
||||
monitoring + alerting template
|
||||
|
||||
ML-ready pipeline template
|
||||
|
||||
advanced data model scaffolds
|
||||
|
||||
Enterprise
|
||||
|
||||
custom templates
|
||||
|
||||
compliance add-ons
|
||||
|
||||
private deployments
|
||||
|
||||
dedicated support / SLAs
|
||||
|
||||
8) Credit Pricing: Fixed Markup per Model
|
||||
|
||||
You said you want:
|
||||
|
||||
credits based on user actions, with fixed markup on every model
|
||||
|
||||
This implies:
|
||||
|
||||
Each model has an internal “true cost”
|
||||
|
||||
You charge credits at a consistent markup multiplier
|
||||
|
||||
Premium models may have a higher markup (optional), but you can keep it fixed if you prefer simplicity
|
||||
|
||||
How it should feel to the user
|
||||
|
||||
“This action will cost ~X credits”
|
||||
|
||||
“Set a spending cap per day/project”
|
||||
|
||||
“Require approval if a task is estimated > Y credits”
|
||||
|
||||
This prevents runaway spending and builds trust.
|
||||
|
||||
9) Key Risk Controls We Agreed Are Necessary
|
||||
|
||||
To make this sellable and safe:
|
||||
|
||||
Token and autonomy guardrails
|
||||
|
||||
max tokens per step
|
||||
|
||||
max retries per task
|
||||
|
||||
auto-summarize context aggressively
|
||||
|
||||
store structured memory, not chat replay
|
||||
|
||||
only send diffs / minimal file slices
|
||||
|
||||
caching where possible (especially for repeated prefixes)
|
||||
|
||||
UX controls
|
||||
|
||||
show credit burn in real time
|
||||
|
||||
warn/approve for high-cost tasks
|
||||
|
||||
allow user-set budgets
|
||||
|
||||
explain why escalation happened (briefly)
|
||||
|
||||
10) The End State
|
||||
|
||||
VibnAI becomes:
|
||||
|
||||
A template-first “product builder OS”
|
||||
|
||||
powered by multi-model orchestration
|
||||
|
||||
hosted on your infra
|
||||
|
||||
with predictable economics via subscription + credits
|
||||
|
||||
and a defensible moat via templates + routing intelligence
|
||||
991
docs_archive/remixed-9edec9e9.tsx
Normal file
991
docs_archive/remixed-9edec9e9.tsx
Normal file
@@ -0,0 +1,991 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
const FontLoader = () => (
|
||||
<style>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400&family=Outfit:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
body { background: #f6f4f0; }
|
||||
@keyframes enter { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
|
||||
@keyframes blink { 0%,100%{opacity:.2} 50%{opacity:.8} }
|
||||
@keyframes breathe { 0%,100%{transform:scale(1)} 50%{transform:scale(1.15)} }
|
||||
::selection { background: #1a1a1a; color: #fff; }
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: #d0ccc4; border-radius: 10px; }
|
||||
input::placeholder { color: #b5b0a6; }
|
||||
input:focus, textarea:focus { outline: none; }
|
||||
button { font-family: 'Outfit', sans-serif; cursor: pointer; }
|
||||
textarea { font-family: 'Outfit', sans-serif; resize: vertical; }
|
||||
`}</style>
|
||||
);
|
||||
|
||||
/* ─── DATA ─── */
|
||||
const projects = [
|
||||
{ id: 1, name: "Meridian", desc: "Client portal for boutique agencies", status: "building", progress: 68, features: 12, phase: "Frontend Gen", lastActive: "2h ago", color: "#3d5afe", domain: "meridian-app.stackless.dev", repo: "stackless/meridian-build", created: "Jan 12, 2026" },
|
||||
{ id: 2, name: "Tidepool", desc: "Marine research data platform", status: "prd", progress: 45, features: 7, phase: "Features", lastActive: "20m ago", color: "#00897b", domain: null, repo: null, created: "Feb 3, 2026" },
|
||||
{ id: 3, name: "Canopy", desc: "Internal team knowledge base", status: "live", progress: 100, features: 18, phase: "Deployed", lastActive: "1d ago", color: "#2e7d32", domain: "canopy.stackless.dev", customDomain: "kb.acmecorp.com", repo: "stackless/canopy-build", created: "Nov 28, 2025" },
|
||||
{ id: 4, name: "Foxglove", desc: "Prescription mgmt for pharmacies", status: "prd", progress: 20, features: 3, phase: "Discovery", lastActive: "now", color: "#e65100", domain: null, repo: null, created: "Feb 27, 2026" },
|
||||
];
|
||||
|
||||
const activityFeed = [
|
||||
{ time: "2 min ago", project: "Foxglove", action: "Atlas completed Users & Personas phase", type: "atlas" },
|
||||
{ time: "18 min ago", project: "Foxglove", action: "You described the core prescription workflow", type: "user" },
|
||||
{ time: "1h ago", project: "Meridian", action: "Build: Dashboard UI component generated", type: "build" },
|
||||
{ time: "2h ago", project: "Meridian", action: "Build: Authentication system passed all tests", type: "build" },
|
||||
{ time: "3h ago", project: "Tidepool", action: "Atlas captured 7 features in MoSCoW framework", type: "atlas" },
|
||||
{ time: "5h ago", project: "Tidepool", action: "You approved Problem Statement section", type: "user" },
|
||||
{ time: "8h ago", project: "Meridian", action: "Build: Database schema deployed", type: "build" },
|
||||
{ time: "1d ago", project: "Canopy", action: "Custom domain kb.acmecorp.com verified and active", type: "deploy" },
|
||||
{ time: "1d ago", project: "Canopy", action: "v1.2 deployed — added search filters", type: "deploy" },
|
||||
{ time: "2d ago", project: "Meridian", action: "PRD approved — build pipeline started", type: "deploy" },
|
||||
{ time: "2d ago", project: "Tidepool", action: "Project created", type: "user" },
|
||||
{ time: "3d ago", project: "Foxglove", action: "Project created", type: "user" },
|
||||
];
|
||||
|
||||
const chatHistory = [
|
||||
{ from: "atlas", text: "I see you're building Foxglove — prescription management for small pharmacies. We've locked in the problem statement and identified your primary user.\n\nNow let's map the core workflow. When a pharmacist opens Foxglove first thing in the morning, what do they need to see?" },
|
||||
{ from: "user", text: "They need to see incoming prescriptions from doctors. The current systems are super clunky. They want to see new scripts, verify them, and mark them as filled." },
|
||||
{ from: "atlas", text: "Clean workflow. Three stages:\n\n1. Receive — new scripts appear in a queue\n2. Verify — check dosage, interactions, patient history\n3. Fill — mark dispensed, update stock\n\nTwo things I want to nail down: does the pharmacist need to message the prescribing doctor back through Foxglove if there's a dosage flag? And are we building for single-location pharmacies or chains with 2–3 stores?" },
|
||||
];
|
||||
|
||||
const prdData = [
|
||||
{ name: "Executive Summary", status: "done", pct: 100 },
|
||||
{ name: "Problem Statement", status: "done", pct: 100 },
|
||||
{ name: "Users & Personas", status: "done", pct: 100 },
|
||||
{ name: "User Flows", status: "active", pct: 60 },
|
||||
{ name: "Feature Requirements", status: "pending", pct: 20 },
|
||||
{ name: "Screen Specs", status: "pending", pct: 0 },
|
||||
{ name: "Business Model", status: "pending", pct: 0 },
|
||||
{ name: "Non-Functional Reqs", status: "pending", pct: 0 },
|
||||
{ name: "Risks", status: "pending", pct: 0 },
|
||||
];
|
||||
|
||||
const discoveryPhases = [
|
||||
{ name: "Big Picture", done: true },
|
||||
{ name: "Users", done: true },
|
||||
{ name: "Features", active: true },
|
||||
{ name: "Business Model" },
|
||||
{ name: "Screens" },
|
||||
{ name: "Risks" },
|
||||
];
|
||||
|
||||
/* ─── Micro Components ─── */
|
||||
const Tag = ({ children, color = "#1a1a1a", bg = "#1a1a1a10" }) => (
|
||||
<span style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 9px", borderRadius: 4, fontSize: "0.68rem", fontWeight: 600, letterSpacing: "0.02em", color, background: bg, fontFamily: "Outfit" }}>{children}</span>
|
||||
);
|
||||
|
||||
const StatusDot = ({ status }) => {
|
||||
const c = status === "live" ? "#2e7d32" : status === "building" ? "#3d5afe" : "#d4a04a";
|
||||
return <span style={{ width: 7, height: 7, borderRadius: "50%", background: c, display: "inline-block", flexShrink: 0, animation: status === "building" ? "breathe 2.5s ease infinite" : "none" }} />;
|
||||
};
|
||||
|
||||
const SectionLabel = ({ children }) => (
|
||||
<div style={{ fontSize: "0.6rem", fontWeight: 600, color: "#a09a90", letterSpacing: "0.1em", textTransform: "uppercase", marginBottom: 12 }}>{children}</div>
|
||||
);
|
||||
|
||||
const Card = ({ children, style: s = {}, hover = true, ...rest }) => {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}
|
||||
style={{ background: "#fff", border: `1px solid ${hovered && hover ? "#d0ccc4" : "#e8e4dc"}`, borderRadius: 10, boxShadow: hovered && hover ? "0 2px 8px #1a1a1a0a" : "0 1px 2px #1a1a1a05", transition: "all 0.15s", ...s }}
|
||||
{...rest}
|
||||
>{children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Btn = ({ children, variant = "primary", style: s = {}, ...rest }) => {
|
||||
const styles = variant === "primary"
|
||||
? { background: "#1a1a1a", color: "#fff", border: "1px solid #1a1a1a" }
|
||||
: variant === "secondary"
|
||||
? { background: "#fff", color: "#1a1a1a", border: "1px solid #e0dcd4" }
|
||||
: { background: "transparent", color: "#a09a90", border: "1px solid transparent" };
|
||||
return (
|
||||
<button style={{ padding: "8px 16px", borderRadius: 7, fontSize: "0.78rem", fontWeight: 600, transition: "opacity 0.15s", ...styles, ...s }}
|
||||
onMouseEnter={e => e.currentTarget.style.opacity = "0.8"}
|
||||
onMouseLeave={e => e.currentTarget.style.opacity = "1"}
|
||||
{...rest}
|
||||
>{children}</button>
|
||||
);
|
||||
};
|
||||
|
||||
const InputField = ({ label, value, onChange, placeholder, type = "text", mono = false }) => (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<div style={{ fontSize: "0.72rem", fontWeight: 600, color: "#6b6560", marginBottom: 6 }}>{label}</div>
|
||||
<input type={type} value={value} onChange={onChange} placeholder={placeholder}
|
||||
style={{ width: "100%", padding: "9px 13px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", fontFamily: mono ? "IBM Plex Mono" : "Outfit", color: "#1a1a1a" }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const Toggle = ({ on, onToggle, label }) => (
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0", borderBottom: "1px solid #f0ece4" }}>
|
||||
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>{label}</span>
|
||||
<button onClick={onToggle} style={{
|
||||
width: 38, height: 22, borderRadius: 11, border: "none", padding: 2,
|
||||
background: on ? "#1a1a1a" : "#ddd8d0", transition: "background 0.2s",
|
||||
display: "flex", alignItems: "center",
|
||||
}}>
|
||||
<div style={{ width: 18, height: 18, borderRadius: "50%", background: "#fff", transition: "transform 0.2s", transform: on ? "translateX(16px)" : "translateX(0)" }} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
/* ─── SIDEBAR ─── */
|
||||
const Sidebar = ({ activeProject, setActiveProject, view, setView }) => (
|
||||
<nav style={{ width: 220, height: "100vh", background: "#fff", borderRight: "1px solid #e8e4dc", display: "flex", flexDirection: "column", fontFamily: "Outfit, sans-serif", flexShrink: 0 }}>
|
||||
<div style={{ padding: "22px 18px 18px", display: "flex", alignItems: "center", gap: 9 }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 7, background: "#1a1a1a", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", fontSize: "0.82rem", fontWeight: 700, fontFamily: "Newsreader, serif" }}>S</div>
|
||||
<span style={{ fontSize: "0.95rem", fontWeight: 600, color: "#1a1a1a", letterSpacing: "-0.03em" }}>stackless</span>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: "4px 10px" }}>
|
||||
{[
|
||||
{ id: "home", label: "Projects", icon: "⌗" },
|
||||
{ id: "activity", label: "Activity", icon: "↗" },
|
||||
{ id: "settings", label: "Settings", icon: "⚙" },
|
||||
].map(n => (
|
||||
<button key={n.id}
|
||||
onClick={() => { setView(n.id); setActiveProject(null); }}
|
||||
style={{
|
||||
width: "100%", display: "flex", alignItems: "center", gap: 9,
|
||||
padding: "8px 10px", borderRadius: 6, border: "none",
|
||||
background: view === n.id && !activeProject ? "#f6f4f0" : "transparent",
|
||||
color: view === n.id && !activeProject ? "#1a1a1a" : "#6b6560",
|
||||
fontSize: "0.82rem", fontWeight: view === n.id && !activeProject ? 600 : 500,
|
||||
transition: "all 0.12s",
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.background = "#f6f4f0"}
|
||||
onMouseLeave={e => { if (!(view === n.id && !activeProject)) e.currentTarget.style.background = "transparent"; }}
|
||||
>
|
||||
<span style={{ fontSize: "0.8rem", opacity: 0.45, width: 18, textAlign: "center" }}>{n.icon}</span>
|
||||
{n.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ height: 1, background: "#eae6de", margin: "10px 18px" }} />
|
||||
|
||||
<div style={{ padding: "2px 10px", flex: 1, overflow: "auto" }}>
|
||||
<div style={{ fontSize: "0.6rem", fontWeight: 600, color: "#a09a90", letterSpacing: "0.1em", textTransform: "uppercase", padding: "6px 10px 8px" }}>Projects</div>
|
||||
{projects.map(p => (
|
||||
<button key={p.id}
|
||||
onClick={() => { setActiveProject(p); setView("project"); }}
|
||||
style={{
|
||||
width: "100%", display: "flex", alignItems: "center", gap: 9,
|
||||
padding: "7px 10px", borderRadius: 6, border: "none",
|
||||
background: activeProject?.id === p.id ? "#f6f4f0" : "transparent",
|
||||
color: "#1a1a1a", fontSize: "0.82rem", fontWeight: activeProject?.id === p.id ? 600 : 450,
|
||||
transition: "background 0.12s", textAlign: "left",
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.background = "#f6f4f0"}
|
||||
onMouseLeave={e => { if (activeProject?.id !== p.id) e.currentTarget.style.background = "transparent"; }}
|
||||
>
|
||||
<StatusDot status={p.status} />
|
||||
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ padding: "14px 18px", borderTop: "1px solid #eae6de", display: "flex", alignItems: "center", gap: 9 }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.72rem", fontWeight: 600, color: "#8a8478" }}>M</div>
|
||||
<div>
|
||||
<div style={{ fontSize: "0.78rem", fontWeight: 500, color: "#1a1a1a" }}>Michael</div>
|
||||
<div style={{ fontSize: "0.62rem", color: "#a09a90" }}>Pro plan</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
|
||||
/* ─── ACTIVITY PAGE ─── */
|
||||
const ActivityPage = ({ setActiveProject, setView }) => {
|
||||
const [filter, setFilter] = useState("all");
|
||||
const filtered = filter === "all" ? activityFeed : activityFeed.filter(a => a.type === filter);
|
||||
const typeIcon = (t) => t === "atlas" ? "A" : t === "build" ? "⚡" : t === "deploy" ? "▲" : "●";
|
||||
const typeColor = (t) => t === "atlas" ? "#1a1a1a" : t === "build" ? "#3d5afe" : t === "deploy" ? "#2e7d32" : "#8a8478";
|
||||
|
||||
return (
|
||||
<div style={{ padding: "44px 52px", maxWidth: 720, fontFamily: "Outfit, sans-serif", animation: "enter 0.35s ease both" }}>
|
||||
<h1 style={{ fontFamily: "Newsreader, serif", fontSize: "1.9rem", fontWeight: 400, color: "#1a1a1a", letterSpacing: "-0.03em", marginBottom: 4 }}>Activity</h1>
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90", marginBottom: 28 }}>Everything happening across your projects</p>
|
||||
|
||||
{/* Filters */}
|
||||
<div style={{ display: "flex", gap: 4, marginBottom: 24 }}>
|
||||
{[
|
||||
{ id: "all", label: "All" },
|
||||
{ id: "atlas", label: "Atlas" },
|
||||
{ id: "build", label: "Builds" },
|
||||
{ id: "deploy", label: "Deploys" },
|
||||
{ id: "user", label: "You" },
|
||||
].map(f => (
|
||||
<button key={f.id} onClick={() => setFilter(f.id)}
|
||||
style={{
|
||||
padding: "6px 14px", borderRadius: 6, border: "none",
|
||||
background: filter === f.id ? "#1a1a1a" : "#fff",
|
||||
color: filter === f.id ? "#fff" : "#6b6560",
|
||||
fontSize: "0.75rem", fontWeight: 600, transition: "all 0.12s",
|
||||
}}>{f.label}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Feed */}
|
||||
<div style={{ position: "relative", paddingLeft: 24 }}>
|
||||
{/* Timeline line */}
|
||||
<div style={{ position: "absolute", left: 8, top: 8, bottom: 8, width: 1, background: "#e8e4dc" }} />
|
||||
|
||||
{filtered.map((item, i) => (
|
||||
<div key={i} style={{
|
||||
display: "flex", gap: 14, marginBottom: 4, padding: "12px 16px",
|
||||
animation: `enter 0.3s ease ${i * 0.03}s both`,
|
||||
borderRadius: 8, transition: "background 0.12s", position: "relative",
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.background = "#fff"}
|
||||
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
|
||||
>
|
||||
{/* Dot on timeline */}
|
||||
<div style={{
|
||||
position: "absolute", left: -20, top: 18,
|
||||
width: 9, height: 9, borderRadius: "50%",
|
||||
background: typeColor(item.type), border: "2px solid #f6f4f0",
|
||||
}} />
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
|
||||
<button onClick={() => {
|
||||
const proj = projects.find(p => p.name === item.project);
|
||||
if (proj) { setActiveProject(proj); setView("project"); }
|
||||
}} style={{
|
||||
background: "none", border: "none", padding: 0,
|
||||
fontSize: "0.82rem", fontWeight: 600, color: "#1a1a1a",
|
||||
textDecoration: "none",
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.textDecoration = "underline"}
|
||||
onMouseLeave={e => e.currentTarget.style.textDecoration = "none"}
|
||||
>{item.project}</button>
|
||||
<span style={{ fontSize: "0.68rem", color: "#b5b0a6" }}>·</span>
|
||||
<span style={{ fontSize: "0.72rem", color: "#b5b0a6" }}>{item.time}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: "0.82rem", color: "#6b6560", lineHeight: 1.5 }}>{item.action}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/* ─── SETTINGS PAGE ─── */
|
||||
const SettingsPage = () => {
|
||||
const [settingsTab, setSettingsTab] = useState("account");
|
||||
const [emailNotifs, setEmailNotifs] = useState(true);
|
||||
const [buildNotifs, setBuildNotifs] = useState(true);
|
||||
const [atlasDigest, setAtlasDigest] = useState(false);
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", height: "100vh", fontFamily: "Outfit, sans-serif", animation: "enter 0.35s ease both" }}>
|
||||
{/* Settings nav */}
|
||||
<div style={{ width: 200, padding: "44px 8px 44px 52px", flexShrink: 0 }}>
|
||||
<h1 style={{ fontFamily: "Newsreader, serif", fontSize: "1.9rem", fontWeight: 400, color: "#1a1a1a", letterSpacing: "-0.03em", marginBottom: 28 }}>Settings</h1>
|
||||
{[
|
||||
{ id: "account", label: "Account" },
|
||||
{ id: "notifications", label: "Notifications" },
|
||||
{ id: "billing", label: "Plan & Billing" },
|
||||
{ id: "team", label: "Team" },
|
||||
{ id: "domains", label: "Domains" },
|
||||
{ id: "api", label: "API Keys" },
|
||||
{ id: "danger", label: "Danger Zone" },
|
||||
].map(t => (
|
||||
<button key={t.id} onClick={() => setSettingsTab(t.id)}
|
||||
style={{
|
||||
display: "block", width: "100%", textAlign: "left",
|
||||
padding: "7px 12px", borderRadius: 6, border: "none",
|
||||
background: settingsTab === t.id ? "#fff" : "transparent",
|
||||
color: settingsTab === t.id ? "#1a1a1a" : "#8a8478",
|
||||
fontSize: "0.82rem", fontWeight: settingsTab === t.id ? 600 : 450,
|
||||
marginBottom: 2, transition: "all 0.12s",
|
||||
boxShadow: settingsTab === t.id ? "0 1px 3px #1a1a1a08" : "none",
|
||||
}}>{t.label}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Settings content */}
|
||||
<div style={{ flex: 1, padding: "44px 52px", overflow: "auto" }}>
|
||||
{settingsTab === "account" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Account</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Manage your profile and preferences</p>
|
||||
|
||||
<Card style={{ padding: "24px", marginBottom: 20 }} hover={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 24 }}>
|
||||
<div style={{ width: 52, height: 52, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "1.1rem", fontWeight: 600, color: "#8a8478" }}>M</div>
|
||||
<div>
|
||||
<div style={{ fontSize: "0.95rem", fontWeight: 600, color: "#1a1a1a" }}>Michael</div>
|
||||
<div style={{ fontSize: "0.78rem", color: "#a09a90" }}>michael@example.com</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ marginLeft: "auto", padding: "6px 14px", fontSize: "0.72rem" }}>Change photo</Btn>
|
||||
</div>
|
||||
<InputField label="Full name" value="Michael" onChange={() => {}} />
|
||||
<InputField label="Email" value="michael@example.com" onChange={() => {}} type="email" />
|
||||
<InputField label="Company" value="" onChange={() => {}} placeholder="Optional" />
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
|
||||
<Btn variant="primary" style={{ padding: "8px 20px" }}>Save changes</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card style={{ padding: "24px" }} hover={false}>
|
||||
<h3 style={{ fontSize: "0.88rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Preferences</h3>
|
||||
<p style={{ fontSize: "0.75rem", color: "#a09a90", marginBottom: 12 }}>Customize your workspace</p>
|
||||
<Toggle on={darkMode} onToggle={() => setDarkMode(!darkMode)} label="Dark mode" />
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0", borderBottom: "1px solid #f0ece4" }}>
|
||||
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>Default project view</span>
|
||||
<select style={{ padding: "4px 10px", borderRadius: 5, border: "1px solid #e0dcd4", fontSize: "0.78rem", fontFamily: "Outfit", color: "#1a1a1a", background: "#faf8f5" }}>
|
||||
<option>Chat</option>
|
||||
<option>PRD</option>
|
||||
<option>Build</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0" }}>
|
||||
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>Atlas personality</span>
|
||||
<select style={{ padding: "4px 10px", borderRadius: 5, border: "1px solid #e0dcd4", fontSize: "0.78rem", fontFamily: "Outfit", color: "#1a1a1a", background: "#faf8f5" }}>
|
||||
<option>Balanced</option>
|
||||
<option>Concise</option>
|
||||
<option>Thorough</option>
|
||||
</select>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsTab === "notifications" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Notifications</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Choose what you hear about and when</p>
|
||||
<Card style={{ padding: "24px" }} hover={false}>
|
||||
<Toggle on={emailNotifs} onToggle={() => setEmailNotifs(!emailNotifs)} label="Email notifications" />
|
||||
<Toggle on={buildNotifs} onToggle={() => setBuildNotifs(!buildNotifs)} label="Build completion alerts" />
|
||||
<Toggle on={atlasDigest} onToggle={() => setAtlasDigest(!atlasDigest)} label="Daily Atlas digest" />
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0" }}>
|
||||
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>Notification frequency</span>
|
||||
<select style={{ padding: "4px 10px", borderRadius: 5, border: "1px solid #e0dcd4", fontSize: "0.78rem", fontFamily: "Outfit", color: "#1a1a1a", background: "#faf8f5" }}>
|
||||
<option>Real-time</option>
|
||||
<option>Hourly digest</option>
|
||||
<option>Daily digest</option>
|
||||
</select>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsTab === "billing" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Plan & Billing</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Manage your subscription and usage</p>
|
||||
|
||||
<Card style={{ padding: "24px", marginBottom: 16 }} hover={false}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 18 }}>
|
||||
<div>
|
||||
<div style={{ fontSize: "0.62rem", fontWeight: 600, color: "#a09a90", letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 4 }}>Current plan</div>
|
||||
<div style={{ fontFamily: "Newsreader", fontSize: "1.4rem", color: "#1a1a1a" }}>Pro</div>
|
||||
</div>
|
||||
<Tag color="#2e7d32" bg="#2e7d3210">Active</Tag>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 24, paddingTop: 16, borderTop: "1px solid #f0ece4" }}>
|
||||
{[
|
||||
{ label: "Projects", value: "10", used: "4" },
|
||||
{ label: "Builds/mo", value: "20", used: "3" },
|
||||
{ label: "Deploys", value: "Unlimited", used: "—" },
|
||||
].map((m, i) => (
|
||||
<div key={i}>
|
||||
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 3 }}>{m.label}</div>
|
||||
<div style={{ fontSize: "0.88rem", color: "#1a1a1a" }}>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", fontWeight: 500 }}>{m.used}</span>
|
||||
<span style={{ color: "#b5b0a6" }}> / {m.value}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card style={{ padding: "20px", display: "flex", alignItems: "center", justifyContent: "space-between" }} hover={false}>
|
||||
<div>
|
||||
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#1a1a1a" }}>Payment method</div>
|
||||
<div style={{ fontSize: "0.78rem", color: "#a09a90", fontFamily: "IBM Plex Mono" }}>•••• 4242 — expires 08/27</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ padding: "6px 14px", fontSize: "0.72rem" }}>Update</Btn>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsTab === "team" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Team</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Manage collaborators and permissions</p>
|
||||
<Card style={{ padding: "24px" }} hover={false}>
|
||||
{[
|
||||
{ name: "Michael", email: "michael@example.com", role: "Owner" },
|
||||
{ name: "Craig F.", email: "craig@example.com", role: "Editor" },
|
||||
].map((m, i) => (
|
||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 0", borderBottom: i === 0 ? "1px solid #f0ece4" : "none" }}>
|
||||
<div style={{ width: 32, height: 32, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.72rem", fontWeight: 600, color: "#8a8478" }}>{m.name[0]}</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#1a1a1a" }}>{m.name}</div>
|
||||
<div style={{ fontSize: "0.72rem", color: "#a09a90" }}>{m.email}</div>
|
||||
</div>
|
||||
<Tag color="#6b6560" bg="#f0ece4">{m.role}</Tag>
|
||||
</div>
|
||||
))}
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Btn variant="secondary" style={{ width: "100%", fontSize: "0.78rem" }}>+ Invite team member</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsTab === "domains" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Domains</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Custom domains for your deployed projects</p>
|
||||
<Card style={{ padding: "24px" }} hover={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", fontWeight: 500, color: "#1a1a1a" }}>kb.acmecorp.com</div>
|
||||
<div style={{ fontSize: "0.72rem", color: "#a09a90" }}>→ Canopy</div>
|
||||
</div>
|
||||
<Tag color="#2e7d32" bg="#2e7d3210">Verified</Tag>
|
||||
</div>
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Btn variant="secondary" style={{ width: "100%", fontSize: "0.78rem" }}>+ Add custom domain</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsTab === "api" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>API Keys</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Access your projects programmatically</p>
|
||||
<Card style={{ padding: "24px" }} hover={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.82rem", fontWeight: 500, color: "#1a1a1a" }}>Production key</div>
|
||||
<div style={{ fontSize: "0.78rem", fontFamily: "IBM Plex Mono", color: "#a09a90" }}>sk_live_••••••••••••3kF9</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Reveal</Btn>
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.82rem", fontWeight: 500, color: "#1a1a1a" }}>Test key</div>
|
||||
<div style={{ fontSize: "0.78rem", fontFamily: "IBM Plex Mono", color: "#a09a90" }}>sk_test_••••••••••••7mR2</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Reveal</Btn>
|
||||
</div>
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Btn variant="secondary" style={{ width: "100%", fontSize: "0.78rem" }}>+ Generate new key</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{settingsTab === "danger" && (
|
||||
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#d32f2f", marginBottom: 4 }}>Danger Zone</h2>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Irreversible actions</p>
|
||||
<Card style={{ padding: "20px", borderColor: "#f5d5d5" }} hover={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<div>
|
||||
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#1a1a1a" }}>Delete account</div>
|
||||
<div style={{ fontSize: "0.75rem", color: "#a09a90" }}>Permanently remove your account and all projects</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ color: "#d32f2f", borderColor: "#f5d5d5", padding: "6px 14px", fontSize: "0.72rem" }}>Delete</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/* ─── PROJECT DETAIL ─── */
|
||||
const ProjectDetail = ({ project }) => {
|
||||
const [tab, setTab] = useState("chat");
|
||||
const [msgs, setMsgs] = useState(chatHistory);
|
||||
const [input, setInput] = useState("");
|
||||
const [typing, setTyping] = useState(false);
|
||||
const endRef = useRef(null);
|
||||
|
||||
useEffect(() => { endRef.current?.scrollIntoView({ behavior: "smooth" }); }, [msgs, typing]);
|
||||
|
||||
const send = () => {
|
||||
if (!input.trim()) return;
|
||||
setMsgs(p => [...p, { from: "user", text: input }]);
|
||||
setInput("");
|
||||
setTyping(true);
|
||||
setTimeout(() => {
|
||||
setTyping(false);
|
||||
setMsgs(p => [...p, { from: "atlas", text: "Good instinct. For multi-location, the core question is inventory: shared pool vs per-store tracking.\n\nOption A — Shared inventory. Simpler, but Store B can't trust the count if Store A just dispensed.\n\nOption B — Per-location with transfers. Accurate, adds a \"request stock\" workflow.\n\nMost independents start single-location and add multi-store as v2. Want to scope it that way?" }]);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const prdPct = Math.round(prdData.reduce((a, s) => a + s.pct, 0) / prdData.length);
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", height: "100vh", fontFamily: "Outfit, sans-serif", animation: "enter 0.3s ease" }}>
|
||||
<div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
|
||||
{/* Header */}
|
||||
<div style={{ padding: "18px 32px", borderBottom: "1px solid #e8e4dc", display: "flex", alignItems: "center", justifyContent: "space-between", background: "#fff" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 14 }}>
|
||||
<div style={{ width: 34, height: 34, borderRadius: 9, background: project.color + "12", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<span style={{ fontFamily: "Newsreader", fontSize: "1rem", fontWeight: 500, color: project.color }}>{project.name[0]}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", letterSpacing: "-0.02em" }}>{project.name}</h2>
|
||||
<Tag color={project.status === "live" ? "#2e7d32" : project.status === "building" ? "#3d5afe" : "#9a7b3a"}
|
||||
bg={project.status === "live" ? "#2e7d3210" : project.status === "building" ? "#3d5afe10" : "#d4a04a12"}>
|
||||
{project.status === "live" ? "Live" : project.status === "building" ? "Building" : "Defining"}
|
||||
</Tag>
|
||||
</div>
|
||||
<p style={{ fontSize: "0.75rem", color: "#a09a90", marginTop: 1 }}>{project.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", fontWeight: 500, color: "#1a1a1a", background: "#f6f4f0", padding: "6px 12px", borderRadius: 6 }}>
|
||||
{project.progress}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div style={{ padding: "0 32px", borderBottom: "1px solid #e8e4dc", display: "flex", gap: 0, background: "#fff" }}>
|
||||
{[
|
||||
{ id: "chat", label: "Atlas" },
|
||||
{ id: "prd", label: "PRD" },
|
||||
{ id: "build", label: "Build" },
|
||||
{ id: "deploy", label: "Deploy" },
|
||||
{ id: "projsettings", label: "Settings" },
|
||||
].map(t => (
|
||||
<button key={t.id} onClick={() => setTab(t.id)} style={{
|
||||
padding: "12px 18px", border: "none", background: "none",
|
||||
fontSize: "0.8rem", fontWeight: 500, cursor: "pointer",
|
||||
color: tab === t.id ? "#1a1a1a" : "#a09a90",
|
||||
borderBottom: tab === t.id ? "2px solid #1a1a1a" : "2px solid transparent",
|
||||
transition: "all 0.12s", fontFamily: "Outfit",
|
||||
}}>{t.label}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, overflow: "hidden", display: "flex", background: "#f6f4f0" }}>
|
||||
|
||||
{/* CHAT */}
|
||||
{tab === "chat" && (
|
||||
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
||||
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px" }}>
|
||||
{msgs.map((m, i) => (
|
||||
<div key={i} style={{ display: "flex", gap: 12, marginBottom: 22, animation: i >= chatHistory.length ? "enter 0.3s ease" : `enter 0.35s ease ${i * 0.08}s both` }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 7, flexShrink: 0, marginTop: 2, background: m.from === "atlas" ? "#1a1a1a" : "#e8e4dc", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.68rem", fontWeight: 700, color: m.from === "atlas" ? "#fff" : "#8a8478", fontFamily: m.from === "atlas" ? "Newsreader" : "Outfit" }}>
|
||||
{m.from === "atlas" ? "A" : "M"}
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.68rem", fontWeight: 600, color: "#a09a90", marginBottom: 5, textTransform: "uppercase", letterSpacing: "0.04em" }}>{m.from === "atlas" ? "Atlas" : "You"}</div>
|
||||
<div style={{ fontSize: "0.88rem", color: "#2a2824", lineHeight: 1.72, whiteSpace: "pre-wrap" }}>
|
||||
{m.text.split(/(\*\*.*?\*\*)/).map((seg, j) => seg.startsWith("**") && seg.endsWith("**") ? <strong key={j} style={{ fontWeight: 600, color: "#1a1a1a" }}>{seg.slice(2, -2)}</strong> : seg)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{typing && (
|
||||
<div style={{ display: "flex", gap: 12, animation: "enter 0.2s ease" }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 7, background: "#1a1a1a", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.68rem", fontWeight: 700, color: "#fff", fontFamily: "Newsreader" }}>A</div>
|
||||
<div style={{ display: "flex", gap: 5, paddingTop: 10 }}>
|
||||
{[0,1,2].map(d => <div key={d} style={{ width: 5, height: 5, borderRadius: "50%", background: "#b5b0a6", animation: `blink 1s ease ${d * 0.15}s infinite` }} />)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div ref={endRef} />
|
||||
</div>
|
||||
<div style={{ padding: "14px 32px 22px" }}>
|
||||
<div style={{ display: "flex", gap: 8, padding: "5px 5px 5px 16px", background: "#fff", border: "1px solid #e0dcd4", borderRadius: 10, alignItems: "center", boxShadow: "0 1px 4px #1a1a1a06" }}>
|
||||
<input value={input} onChange={e => setInput(e.target.value)} onKeyDown={e => e.key === "Enter" && send()} placeholder="Describe your thinking..."
|
||||
style={{ flex: 1, border: "none", background: "none", fontSize: "0.86rem", fontFamily: "Outfit", color: "#1a1a1a", padding: "8px 0" }} />
|
||||
<button onClick={send} style={{ padding: "9px 16px", borderRadius: 7, border: "none", background: input.trim() ? "#1a1a1a" : "#eae6de", color: input.trim() ? "#fff" : "#b5b0a6", fontSize: "0.78rem", fontWeight: 600, transition: "all 0.15s" }}>Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* PRD */}
|
||||
{tab === "prd" && (
|
||||
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px", animation: "enter 0.3s ease" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16, padding: "16px 20px", background: "#fff", border: "1px solid #e8e4dc", borderRadius: 10, marginBottom: 20, boxShadow: "0 1px 2px #1a1a1a05" }}>
|
||||
<div style={{ fontFamily: "IBM Plex Mono", fontSize: "1.4rem", fontWeight: 500, color: "#1a1a1a", minWidth: 48 }}>{prdPct}%</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ height: 4, borderRadius: 2, background: "#eae6de" }}>
|
||||
<div style={{ height: "100%", borderRadius: 2, width: `${prdPct}%`, background: "#1a1a1a", transition: "width 0.6s ease" }} />
|
||||
</div>
|
||||
</div>
|
||||
<span style={{ fontSize: "0.75rem", color: "#a09a90" }}>{prdData.filter(s => s.status === "done").length}/{prdData.length} approved</span>
|
||||
</div>
|
||||
{prdData.map((s, i) => (
|
||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "14px 18px", marginBottom: 4, background: "#fff", borderRadius: 8, border: "1px solid #e8e4dc", animation: `enter 0.3s ease ${i * 0.04}s both`, cursor: "pointer", transition: "border-color 0.12s" }}
|
||||
onMouseEnter={e => e.currentTarget.style.borderColor = "#d0ccc4"}
|
||||
onMouseLeave={e => e.currentTarget.style.borderColor = "#e8e4dc"}>
|
||||
<div style={{ width: 24, height: 24, borderRadius: 6, flexShrink: 0, background: s.status === "done" ? "#2e7d3210" : s.status === "active" ? "#d4a04a12" : "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.65rem", fontWeight: 700, color: s.status === "done" ? "#2e7d32" : s.status === "active" ? "#9a7b3a" : "#c5c0b8" }}>
|
||||
{s.status === "done" ? "✓" : s.status === "active" ? "◐" : "○"}
|
||||
</div>
|
||||
<span style={{ flex: 1, fontSize: "0.84rem", color: "#1a1a1a", fontWeight: 450 }}>{s.name}</span>
|
||||
<div style={{ width: 60, height: 3, borderRadius: 2, background: "#eae6de" }}>
|
||||
<div style={{ height: "100%", borderRadius: 2, width: `${s.pct}%`, background: s.status === "done" ? "#2e7d32" : s.status === "active" ? "#d4a04a" : "#d0ccc4" }} />
|
||||
</div>
|
||||
<span style={{ fontSize: "0.68rem", fontFamily: "IBM Plex Mono", color: s.status === "done" ? "#2e7d32" : "#a09a90", fontWeight: 500, minWidth: 28, textAlign: "right" }}>{s.pct}%</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* BUILD */}
|
||||
{tab === "build" && (
|
||||
<div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: 40, animation: "enter 0.3s ease" }}>
|
||||
{project.status === "prd" ? (
|
||||
<div style={{ textAlign: "center", maxWidth: 360 }}>
|
||||
<div style={{ width: 56, height: 56, borderRadius: 14, background: "#fff", border: "1px solid #e8e4dc", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "1.4rem", margin: "0 auto 18px", boxShadow: "0 2px 8px #1a1a1a08" }}>🔒</div>
|
||||
<h3 style={{ fontFamily: "Newsreader, serif", fontSize: "1.3rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 8 }}>Complete your PRD first</h3>
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90", lineHeight: 1.6, marginBottom: 20 }}>Approve all sections with Atlas, then the builder unlocks automatically.</p>
|
||||
<div style={{ display: "inline-flex", padding: "8px 16px", borderRadius: 7, background: "#fff", border: "1px solid #e0dcd4", fontSize: "0.78rem", color: "#6b6560", fontWeight: 500 }}>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", fontWeight: 600, color: "#1a1a1a", marginRight: 6 }}>{prdPct}%</span>complete
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ width: "100%", maxWidth: 500 }}>
|
||||
<h3 style={{ fontFamily: "Newsreader", fontSize: "1.2rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 18 }}>Build progress</h3>
|
||||
{["Auth System", "Database", "Dashboard UI", "Rx Queue", "Inventory", "API"].map((f, i) => {
|
||||
const pct = Math.max(0, Math.min(100, project.progress + (i * -12)));
|
||||
return (
|
||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", marginBottom: 4, borderRadius: 8, background: "#fff", border: "1px solid #e8e4dc", animation: `enter 0.3s ease ${i * 0.05}s both` }}>
|
||||
<StatusDot status={pct >= 100 ? "live" : pct > 0 ? "building" : "prd"} />
|
||||
<span style={{ flex: 1, fontSize: "0.84rem", color: "#1a1a1a" }}>{f}</span>
|
||||
<div style={{ width: 80, height: 3, borderRadius: 2, background: "#eae6de" }}>
|
||||
<div style={{ height: "100%", width: `${pct}%`, borderRadius: 2, background: pct >= 100 ? "#2e7d32" : "#3d5afe" }} />
|
||||
</div>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.7rem", color: "#a09a90", minWidth: 28, textAlign: "right" }}>{pct}%</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* DEPLOY */}
|
||||
{tab === "deploy" && (
|
||||
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px", animation: "enter 0.3s ease" }}>
|
||||
<div style={{ maxWidth: 560 }}>
|
||||
<h3 style={{ fontFamily: "Newsreader", fontSize: "1.2rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 4 }}>Deployment</h3>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 24 }}>Links, environments, and hosting for {project.name}</p>
|
||||
|
||||
{/* URLs card */}
|
||||
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
|
||||
<SectionLabel>Project URLs</SectionLabel>
|
||||
{project.domain ? (
|
||||
<>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 6, background: "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.7rem", color: "#8a8478" }}>▲</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.68rem", color: "#b5b0a6", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>Staging</div>
|
||||
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#3d5afe", fontWeight: 500 }}>{project.domain}</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Open ↗</Btn>
|
||||
</div>
|
||||
{project.customDomain && (
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 6, background: "#2e7d3210", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.7rem", color: "#2e7d32" }}>●</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.68rem", color: "#b5b0a6", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>Production</div>
|
||||
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#2e7d32", fontWeight: 500 }}>{project.customDomain}</div>
|
||||
</div>
|
||||
<Tag color="#2e7d32" bg="#2e7d3210">SSL Active</Tag>
|
||||
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Open ↗</Btn>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0" }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 6, background: "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.7rem", color: "#8a8478" }}>⚙</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: "0.68rem", color: "#b5b0a6", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>Build repo</div>
|
||||
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#6b6560", fontWeight: 500 }}>{project.repo}</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>View ↗</Btn>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ padding: "18px 0", textAlign: "center" }}>
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90", marginBottom: 12 }}>No deployment yet — complete your PRD and build to get a live URL.</p>
|
||||
<div style={{ display: "inline-flex", padding: "6px 14px", borderRadius: 6, background: "#f6f4f0", fontSize: "0.75rem", color: "#8a8478" }}>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", marginRight: 6 }}>{project.progress}%</span> to deployment
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Custom domain */}
|
||||
{project.domain && !project.customDomain && (
|
||||
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
|
||||
<SectionLabel>Custom Domain</SectionLabel>
|
||||
<p style={{ fontSize: "0.82rem", color: "#6b6560", lineHeight: 1.6, marginBottom: 14 }}>
|
||||
Point your own domain to this project. We'll handle SSL certificates automatically.
|
||||
</p>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<input placeholder="app.yourdomain.com" style={{ flex: 1, padding: "9px 13px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#1a1a1a" }} />
|
||||
<Btn variant="primary" style={{ padding: "9px 18px" }}>Connect</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Environment vars */}
|
||||
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
|
||||
<SectionLabel>Environment Variables</SectionLabel>
|
||||
{project.domain ? (
|
||||
<>
|
||||
{[
|
||||
{ key: "DATABASE_URL", val: "••••••••••••" },
|
||||
{ key: "API_SECRET", val: "••••••••••••" },
|
||||
{ key: "SMTP_HOST", val: "mail.stackless.dev" },
|
||||
].map((env, i) => (
|
||||
<div key={i} style={{ display: "flex", gap: 10, padding: "8px 0", borderBottom: i < 2 ? "1px solid #f0ece4" : "none", alignItems: "center" }}>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", fontWeight: 500, color: "#1a1a1a", minWidth: 130 }}>{env.key}</span>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", color: "#a09a90", flex: 1 }}>{env.val}</span>
|
||||
<button style={{ background: "none", border: "none", fontSize: "0.7rem", color: "#a09a90", padding: "2px 6px" }}>Edit</button>
|
||||
</div>
|
||||
))}
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<Btn variant="secondary" style={{ fontSize: "0.75rem", padding: "7px 14px" }}>+ Add variable</Btn>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90", padding: "10px 0" }}>Available after first build completes.</p>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Deploy history */}
|
||||
<Card style={{ padding: "22px" }} hover={false}>
|
||||
<SectionLabel>Deploy History</SectionLabel>
|
||||
{project.status === "live" ? (
|
||||
<>
|
||||
{[
|
||||
{ version: "v1.2", time: "1 day ago", status: "live", note: "Added search filters" },
|
||||
{ version: "v1.1", time: "5 days ago", status: "previous", note: "Bug fix: auth timeout" },
|
||||
{ version: "v1.0", time: "2 weeks ago", status: "previous", note: "Initial launch" },
|
||||
].map((d, i) => (
|
||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: i < 2 ? "1px solid #f0ece4" : "none" }}>
|
||||
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", fontWeight: 600, color: "#1a1a1a", minWidth: 36 }}>{d.version}</span>
|
||||
<span style={{ flex: 1, fontSize: "0.82rem", color: "#6b6560" }}>{d.note}</span>
|
||||
<span style={{ fontSize: "0.72rem", color: "#b5b0a6" }}>{d.time}</span>
|
||||
{d.status === "live" && <Tag color="#2e7d32" bg="#2e7d3210">Current</Tag>}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : project.domain ? (
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90", padding: "10px 0" }}>First deploy will appear here once the build completes.</p>
|
||||
) : (
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90", padding: "10px 0" }}>No deploys yet.</p>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* PROJECT SETTINGS */}
|
||||
{tab === "projsettings" && (
|
||||
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px", animation: "enter 0.3s ease" }}>
|
||||
<div style={{ maxWidth: 480 }}>
|
||||
<h3 style={{ fontFamily: "Newsreader", fontSize: "1.2rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 4 }}>Project Settings</h3>
|
||||
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 24 }}>Configure {project.name}</p>
|
||||
|
||||
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
|
||||
<SectionLabel>General</SectionLabel>
|
||||
<InputField label="Project name" value={project.name} onChange={() => {}} />
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<div style={{ fontSize: "0.72rem", fontWeight: 600, color: "#6b6560", marginBottom: 6 }}>Description</div>
|
||||
<textarea value={project.desc} onChange={() => {}} rows={2}
|
||||
style={{ width: "100%", padding: "9px 13px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", color: "#1a1a1a", fontFamily: "Outfit" }} />
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Btn variant="primary" style={{ padding: "8px 20px" }}>Save</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
|
||||
<SectionLabel>Collaborators</SectionLabel>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0" }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.68rem", fontWeight: 600, color: "#8a8478" }}>M</div>
|
||||
<span style={{ flex: 1, fontSize: "0.82rem", color: "#1a1a1a" }}>Michael</span>
|
||||
<Tag color="#6b6560" bg="#f0ece4">Owner</Tag>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ width: "100%", marginTop: 12, fontSize: "0.75rem" }}>+ Invite to project</Btn>
|
||||
</Card>
|
||||
|
||||
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
|
||||
<SectionLabel>Export</SectionLabel>
|
||||
<p style={{ fontSize: "0.82rem", color: "#6b6560", marginBottom: 14, lineHeight: 1.6 }}>Download your PRD or project data for external use.</p>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<Btn variant="secondary" style={{ fontSize: "0.75rem" }}>Export PRD as PDF</Btn>
|
||||
<Btn variant="secondary" style={{ fontSize: "0.75rem" }}>Export as JSON</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card style={{ padding: "20px", borderColor: "#f5d5d5" }} hover={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<div>
|
||||
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#d32f2f" }}>Delete project</div>
|
||||
<div style={{ fontSize: "0.75rem", color: "#a09a90" }}>This action cannot be undone</div>
|
||||
</div>
|
||||
<Btn variant="secondary" style={{ color: "#d32f2f", borderColor: "#f5d5d5", padding: "6px 14px", fontSize: "0.72rem" }}>Delete</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right panel */}
|
||||
<div style={{ width: 230, borderLeft: "1px solid #e8e4dc", background: "#fff", padding: "22px 18px", overflow: "auto", flexShrink: 0 }}>
|
||||
<SectionLabel>Discovery</SectionLabel>
|
||||
{discoveryPhases.map((ph, i) => (
|
||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 0", borderBottom: i < discoveryPhases.length - 1 ? "1px solid #f0ece4" : "none" }}>
|
||||
<div style={{ width: 20, height: 20, borderRadius: 5, flexShrink: 0, background: ph.done ? "#2e7d3210" : ph.active ? "#d4a04a12" : "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.58rem", fontWeight: 700, color: ph.done ? "#2e7d32" : ph.active ? "#9a7b3a" : "#c5c0b8" }}>
|
||||
{ph.done ? "✓" : ph.active ? "→" : i + 1}
|
||||
</div>
|
||||
<span style={{ fontSize: "0.78rem", fontWeight: ph.active ? 600 : 400, color: ph.done ? "#6b6560" : ph.active ? "#1a1a1a" : "#b5b0a6" }}>{ph.name}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
|
||||
|
||||
<SectionLabel>Captured</SectionLabel>
|
||||
{[
|
||||
{ k: "Users", v: "Pharmacist" },
|
||||
{ k: "Workflow", v: "Receive → Verify → Fill" },
|
||||
{ k: "Scope", v: "Single-location MVP" },
|
||||
{ k: "Open", v: "Multi-location support?" },
|
||||
].map((item, i) => (
|
||||
<div key={i} style={{ marginBottom: 14 }}>
|
||||
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3, fontWeight: 600 }}>{item.k}</div>
|
||||
<div style={{ fontSize: "0.8rem", color: "#4a4640", lineHeight: 1.45 }}>{item.v}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
|
||||
|
||||
<SectionLabel>Project Info</SectionLabel>
|
||||
{[
|
||||
{ k: "Created", v: project.created },
|
||||
{ k: "Last active", v: project.lastActive },
|
||||
{ k: "Features", v: `${project.features} defined` },
|
||||
].map((item, i) => (
|
||||
<div key={i} style={{ marginBottom: 12 }}>
|
||||
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3, fontWeight: 600 }}>{item.k}</div>
|
||||
<div style={{ fontSize: "0.8rem", color: "#4a4640" }}>{item.v}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/* ─── DASHBOARD ─── */
|
||||
const Home = ({ setActiveProject, setView }) => {
|
||||
const [showNew, setShowNew] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ padding: "44px 52px", maxWidth: 900, fontFamily: "Outfit, sans-serif", animation: "enter 0.35s ease both" }}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 36 }}>
|
||||
<div>
|
||||
<h1 style={{ fontFamily: "Newsreader, serif", fontSize: "1.9rem", fontWeight: 400, color: "#1a1a1a", letterSpacing: "-0.03em", lineHeight: 1.15, marginBottom: 4 }}>Projects</h1>
|
||||
<p style={{ fontSize: "0.82rem", color: "#a09a90" }}>4 total · 2 in definition · 1 building · 1 live</p>
|
||||
</div>
|
||||
<Btn variant="primary" onClick={() => setShowNew(!showNew)} style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
||||
<span style={{ fontSize: "1rem", lineHeight: 1, fontWeight: 300 }}>+</span> New project
|
||||
</Btn>
|
||||
</div>
|
||||
|
||||
{showNew && (
|
||||
<Card style={{ padding: "22px 26px", marginBottom: 28, animation: "enter 0.25s ease" }} hover={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 14 }}>
|
||||
<div style={{ width: 28, height: 28, borderRadius: 7, background: "#1a1a1a", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.72rem", fontWeight: 700, color: "#fff", fontFamily: "Newsreader" }}>A</div>
|
||||
<div>
|
||||
<span style={{ fontSize: "0.82rem", fontWeight: 600, color: "#1a1a1a" }}>Atlas</span>
|
||||
<span style={{ fontSize: "0.72rem", color: "#a09a90", marginLeft: 8 }}>product strategist</span>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontSize: "0.84rem", color: "#6b6560", lineHeight: 1.6, marginBottom: 16 }}>Tell me what you want to build. One sentence is fine — I'll ask the right questions to figure out the rest.</p>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<input placeholder="e.g. A scheduling tool for independent yoga instructors..." style={{ flex: 1, padding: "10px 14px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", fontFamily: "Outfit", color: "#1a1a1a" }} />
|
||||
<Btn variant="primary">Start →</Btn>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
||||
{projects.map((p, i) => (
|
||||
<button key={p.id} onClick={() => { setActiveProject(p); setView("project"); }}
|
||||
style={{
|
||||
width: "100%", display: "flex", alignItems: "center", padding: "18px 22px", borderRadius: 10,
|
||||
background: "#fff", border: "1px solid #e8e4dc", cursor: "pointer", fontFamily: "Outfit",
|
||||
transition: "all 0.15s", textAlign: "left", animation: `enter 0.35s ease ${i * 0.05}s both`,
|
||||
boxShadow: "0 1px 2px #1a1a1a05",
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.borderColor = "#d0ccc4"; e.currentTarget.style.boxShadow = "0 2px 8px #1a1a1a0a"; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.borderColor = "#e8e4dc"; e.currentTarget.style.boxShadow = "0 1px 2px #1a1a1a05"; }}
|
||||
>
|
||||
<div style={{ width: 36, height: 36, borderRadius: 9, marginRight: 16, background: p.color + "12", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
|
||||
<span style={{ fontFamily: "Newsreader, serif", fontSize: "1.05rem", fontWeight: 500, color: p.color }}>{p.name[0]}</span>
|
||||
</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 2 }}>
|
||||
<span style={{ fontSize: "0.9rem", fontWeight: 600, color: "#1a1a1a" }}>{p.name}</span>
|
||||
<Tag color={p.status === "live" ? "#2e7d32" : p.status === "building" ? "#3d5afe" : "#9a7b3a"} bg={p.status === "live" ? "#2e7d3210" : p.status === "building" ? "#3d5afe10" : "#d4a04a12"}>
|
||||
<StatusDot status={p.status} /> {p.status === "live" ? "Live" : p.status === "building" ? "Building" : "Defining"}
|
||||
</Tag>
|
||||
</div>
|
||||
<span style={{ fontSize: "0.78rem", color: "#a09a90" }}>{p.desc}</span>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 28, alignItems: "center", flexShrink: 0 }}>
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 2 }}>Phase</div>
|
||||
<div style={{ fontSize: "0.78rem", color: "#6b6560" }}>{p.phase}</div>
|
||||
</div>
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 2 }}>Features</div>
|
||||
<div style={{ fontSize: "0.78rem", color: "#6b6560" }}>{p.features}</div>
|
||||
</div>
|
||||
<div style={{ textAlign: "right", minWidth: 40 }}>
|
||||
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 2 }}>Progress</div>
|
||||
<div style={{ fontSize: "0.78rem", color: "#1a1a1a", fontWeight: 600, fontFamily: "IBM Plex Mono" }}>{p.progress}%</div>
|
||||
</div>
|
||||
<div style={{ width: 60, height: 3, borderRadius: 2, background: "#eae6de", flexShrink: 0 }}>
|
||||
<div style={{ height: "100%", borderRadius: 2, width: `${p.progress}%`, background: p.status === "live" ? "#2e7d32" : p.status === "building" ? "#3d5afe" : "#d4a04a", transition: "width 0.6s ease" }} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/* ─── ROOT ─── */
|
||||
export default function Stackless() {
|
||||
const [view, setView] = useState("home");
|
||||
const [activeProject, setActiveProject] = useState(null);
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", height: "100vh", background: "#f6f4f0", overflow: "hidden" }}>
|
||||
<FontLoader />
|
||||
<Sidebar activeProject={activeProject} setActiveProject={setActiveProject} view={view} setView={setView} />
|
||||
<div style={{ flex: 1, overflow: "auto" }}>
|
||||
{view === "home" && !activeProject && <Home setActiveProject={setActiveProject} setView={setView} />}
|
||||
{view === "activity" && <ActivityPage setActiveProject={setActiveProject} setView={setView} />}
|
||||
{view === "settings" && <SettingsPage />}
|
||||
{activeProject && <ProjectDetail project={activeProject} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user