made system changes
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(pnpm --version)",
|
||||||
|
"Bash(pnpm install *)",
|
||||||
|
"Bash(pnpm run *)",
|
||||||
|
"Bash(pnpm approve-builds *)",
|
||||||
|
"WebFetch(domain:github.com)",
|
||||||
|
"WebFetch(domain:www.figma.com)",
|
||||||
|
"mcp__Claude_in_Chrome__tabs_context_mcp",
|
||||||
|
"Bash(curl -s \"https://github.com/features/codespaces\")",
|
||||||
|
"Bash(curl -s \"https://www.figma.com/draw/\")",
|
||||||
|
"Bash(curl -s \"https://www.figma.com/make/\")",
|
||||||
|
"Bash(curl -s \"https://github.githubassets.com/assets/primer-primitives-0f570a01cbe448fb.css\")",
|
||||||
|
"Bash(pnpm --filter @originmain/app run build)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Supabase — get from https://supabase.com/dashboard/project/<project>/settings/api
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL=
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=
|
||||||
|
|
||||||
|
# Anthropic — https://console.anthropic.com/settings/keys
|
||||||
|
# Never expose this in the browser bundle — server-side only
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
|
|
||||||
|
# Clerk — https://dashboard.clerk.com
|
||||||
|
CLERK_PUBLISHABLE_KEY=
|
||||||
|
CLERK_SECRET_KEY=
|
||||||
|
|
||||||
|
# Liveblocks (Phase 3) — https://liveblocks.io/dashboard
|
||||||
|
LIVEBLOCKS_SECRET_KEY=
|
||||||
|
|
||||||
|
# Linear integration
|
||||||
|
LINEAR_WEBHOOK_SECRET=
|
||||||
|
|
||||||
|
# Slack integration
|
||||||
|
SLACK_BOT_TOKEN=
|
||||||
|
SLACK_SIGNING_SECRET=
|
||||||
|
|
||||||
|
# Agent Bridge WebSocket server port
|
||||||
|
AGENT_BRIDGE_PORT=3001
|
||||||
|
|
||||||
|
# App base URL
|
||||||
|
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: Lint · Type · Test · Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Type check
|
||||||
|
run: pnpm run typecheck
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: pnpm run lint
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: pnpm run test
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
env:
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
|
||||||
|
CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PUBLISHABLE_KEY }}
|
||||||
|
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
|
||||||
|
|
||||||
|
- name: Migration dry-run
|
||||||
|
run: pnpm run migration:dry-run
|
||||||
|
env:
|
||||||
|
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
|
||||||
|
SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_DB_PASSWORD }}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
name: Preview Deployment
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: preview-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-preview:
|
||||||
|
name: Deploy Vercel Preview
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Deploy to Vercel
|
||||||
|
id: vercel-deploy
|
||||||
|
uses: amondnet/vercel-action@v25
|
||||||
|
with:
|
||||||
|
vercel-token: ${{ secrets.VERCEL_TOKEN }}
|
||||||
|
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
|
||||||
|
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||||
|
working-directory: packages/app
|
||||||
|
|
||||||
|
- name: Post preview URL as PR comment
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
env:
|
||||||
|
PREVIEW_URL: ${{ steps.vercel-deploy.outputs.preview-url }}
|
||||||
|
COMMIT_SHA: ${{ github.sha }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const previewUrl = process.env.PREVIEW_URL;
|
||||||
|
const sha = process.env.COMMIT_SHA;
|
||||||
|
github.rest.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: `## Preview Deployment\n\n✅ **Preview URL:** ${previewUrl}\n\nDeployed from commit \`${sha}\``
|
||||||
|
});
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
.next/
|
||||||
|
dist/
|
||||||
|
out/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Test & coverage
|
||||||
|
coverage/
|
||||||
|
.vitest/
|
||||||
|
|
||||||
|
# Supabase
|
||||||
|
supabase/.branches
|
||||||
|
supabase/.temp
|
||||||
|
|
||||||
|
# Vercel
|
||||||
|
.vercel/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
.vscode/settings.json
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
@@ -0,0 +1,815 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Originmain — AI-Native Design Engineering Platform</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,300..800&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
/* ── Tokens ──────────────────────────────────────────────── */
|
||||||
|
:root {
|
||||||
|
--bg: #ffffff;
|
||||||
|
--bg-2: #f5f5f5;
|
||||||
|
--bg-3: #ebebeb;
|
||||||
|
--bg-inv: #0c0c10;
|
||||||
|
--bg-inv-2: #141418;
|
||||||
|
--fg: #0c0c10;
|
||||||
|
--fg-2: rgba(12,12,16,.58);
|
||||||
|
--fg-3: rgba(12,12,16,.32);
|
||||||
|
--accent: #0F52BA;
|
||||||
|
--accent-soft: rgba(15,82,186,.07);
|
||||||
|
--accent-mid: rgba(15,82,186,.16);
|
||||||
|
--border: rgba(0,0,0,.08);
|
||||||
|
--border-2: rgba(0,0,0,.13);
|
||||||
|
--r-s: 6px;
|
||||||
|
--r-m: 12px;
|
||||||
|
--r-l: 20px;
|
||||||
|
--r-xl: 28px;
|
||||||
|
--r-p: 999px;
|
||||||
|
--sh-s: 0 1px 3px rgba(0,0,0,.08),0 0 0 1px rgba(0,0,0,.06);
|
||||||
|
--sh-m: 0 4px 16px rgba(0,0,0,.10),0 0 0 1px rgba(0,0,0,.06);
|
||||||
|
--sh-l: 0 12px 40px rgba(0,0,0,.13),0 0 0 1px rgba(0,0,0,.06);
|
||||||
|
--sh-xl:0 28px 72px rgba(0,0,0,.18),0 0 0 1px rgba(0,0,0,.05);
|
||||||
|
--font-d: 'Bricolage Grotesque', -apple-system, 'SF Pro Display', sans-serif;
|
||||||
|
--font-b: 'Inter', -apple-system, sans-serif;
|
||||||
|
--font-m: 'JetBrains Mono', 'SF Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg: #0c0c10;
|
||||||
|
--bg-2: #141418;
|
||||||
|
--bg-3: #1c1c22;
|
||||||
|
--bg-inv: #f5f5f5;
|
||||||
|
--bg-inv-2: #e8e8e8;
|
||||||
|
--fg: #f0f0f0;
|
||||||
|
--fg-2: rgba(240,240,240,.58);
|
||||||
|
--fg-3: rgba(240,240,240,.32);
|
||||||
|
--accent: #4d84e0;
|
||||||
|
--accent-soft: rgba(77,132,224,.10);
|
||||||
|
--accent-mid: rgba(77,132,224,.20);
|
||||||
|
--border: rgba(255,255,255,.08);
|
||||||
|
--border-2: rgba(255,255,255,.13);
|
||||||
|
--sh-s: 0 1px 3px rgba(0,0,0,.4),0 0 0 1px rgba(255,255,255,.05);
|
||||||
|
--sh-m: 0 4px 16px rgba(0,0,0,.5);
|
||||||
|
--sh-l: 0 12px 40px rgba(0,0,0,.55);
|
||||||
|
--sh-xl:0 28px 72px rgba(0,0,0,.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Reset ───────────────────────────────────────────────── */
|
||||||
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
html{font-size:16px;scroll-behavior:smooth;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
|
||||||
|
body{font-family:var(--font-b);background:var(--bg);color:var(--fg);line-height:1.6;overflow-x:hidden;transition:background .25s,color .25s}
|
||||||
|
|
||||||
|
/* ── Layout ──────────────────────────────────────────────── */
|
||||||
|
.wrap{max-width:1200px;margin:0 auto;padding:0 32px}
|
||||||
|
.section{padding:128px 0}
|
||||||
|
.section-dark{background:var(--bg-inv);color:var(--bg)}
|
||||||
|
.section-tinted{background:var(--bg-2)}
|
||||||
|
|
||||||
|
/* ── Type scale ──────────────────────────────────────────── */
|
||||||
|
.d1{font-family:var(--font-d);font-size:clamp(3rem,7.5vw,6rem);font-weight:800;line-height:1.0;letter-spacing:-.045em;color:var(--fg)}
|
||||||
|
.d2{font-family:var(--font-d);font-size:clamp(2rem,4vw,3rem);font-weight:700;line-height:1.07;letter-spacing:-.035em}
|
||||||
|
.d3{font-family:var(--font-d);font-size:clamp(1.125rem,2vw,1.5rem);font-weight:600;line-height:1.3;letter-spacing:-.025em}
|
||||||
|
.body-l{font-size:1.125rem;line-height:1.7;color:var(--fg-2)}
|
||||||
|
.body-m{font-size:1rem;line-height:1.65;color:var(--fg-2)}
|
||||||
|
.mono-label{font-family:var(--font-m);font-size:.6875rem;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-3)}
|
||||||
|
|
||||||
|
/* ── Buttons ─────────────────────────────────────────────── */
|
||||||
|
.btn{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-b);font-size:.9375rem;font-weight:500;border-radius:var(--r-m);padding:11px 22px;border:none;cursor:pointer;text-decoration:none;white-space:nowrap;transition:opacity .14s,transform .14s,box-shadow .14s}
|
||||||
|
.btn-blk{background:var(--fg);color:var(--bg)}
|
||||||
|
.btn-blk:hover{opacity:.82;transform:translateY(-1px)}
|
||||||
|
.btn-ghost{background:transparent;color:var(--fg);border:1px solid var(--border-2)}
|
||||||
|
.btn-ghost:hover{background:var(--bg-2);transform:translateY(-1px)}
|
||||||
|
.btn-accent{background:var(--accent);color:#fff}
|
||||||
|
.btn-accent:hover{opacity:.88;transform:translateY(-1px)}
|
||||||
|
.btn-lg{font-size:1rem;padding:13px 28px}
|
||||||
|
.btn-full{width:100%;justify-content:center}
|
||||||
|
|
||||||
|
/* ── Badges ──────────────────────────────────────────────── */
|
||||||
|
.badge{display:inline-flex;align-items:center;gap:6px;font-size:.8125rem;font-weight:500;padding:4px 12px;border-radius:var(--r-p);background:var(--bg-2);color:var(--fg-2);border:1px solid var(--border)}
|
||||||
|
.badge-accent{background:var(--accent-soft);color:var(--accent);border-color:var(--accent-mid)}
|
||||||
|
|
||||||
|
/* ── NAV ─────────────────────────────────────────────────── */
|
||||||
|
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:60px;display:flex;align-items:center;padding:0 32px;transition:background .2s,border-color .2s}
|
||||||
|
nav.glass{background:rgba(255,255,255,.88);backdrop-filter:blur(20px) saturate(1.4);border-bottom:1px solid var(--border)}
|
||||||
|
[data-theme="dark"] nav.glass{background:rgba(12,12,16,.88)}
|
||||||
|
.nav-i{max-width:1200px;margin:0 auto;width:100%;display:flex;align-items:center;justify-content:space-between}
|
||||||
|
.nav-logo{font-family:var(--font-d);font-size:1.125rem;font-weight:800;letter-spacing:-.04em;color:var(--fg);text-decoration:none}
|
||||||
|
.nav-logo b{color:var(--accent)}
|
||||||
|
.nav-links{display:flex;align-items:center;gap:2px;list-style:none}
|
||||||
|
.nav-links a{font-size:.9375rem;color:var(--fg-2);text-decoration:none;padding:6px 12px;border-radius:var(--r-s);transition:color .14s}
|
||||||
|
.nav-links a:hover{color:var(--fg)}
|
||||||
|
.nav-r{display:flex;align-items:center;gap:10px}
|
||||||
|
|
||||||
|
/* ── HERO ────────────────────────────────────────────────── */
|
||||||
|
#hero{padding:140px 32px 80px;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;overflow:hidden}
|
||||||
|
#hero::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse 90% 55% at 50% -5%,rgba(15,82,186,.055) 0%,transparent 70%);pointer-events:none}
|
||||||
|
.hero-eye{margin-bottom:32px}
|
||||||
|
.hero-h{max-width:960px;margin-bottom:28px}
|
||||||
|
.hero-sub{max-width:520px;margin:0 auto 44px}
|
||||||
|
.hero-ctas{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-bottom:80px}
|
||||||
|
|
||||||
|
/* ── MOCKUP ──────────────────────────────────────────────── */
|
||||||
|
.mockup{width:100%;max-width:1040px;border-radius:var(--r-xl);background:#111116;box-shadow:var(--sh-xl),0 0 0 1px rgba(255,255,255,.06);overflow:hidden;display:flex;flex-direction:column}
|
||||||
|
.m-bar{height:42px;background:#1b1b23;display:flex;align-items:center;padding:0 16px;gap:8px;border-bottom:1px solid rgba(255,255,255,.055);flex-shrink:0}
|
||||||
|
.m-dot{width:10px;height:10px;border-radius:50%}
|
||||||
|
.m-dot.r{background:#ff5f57}.m-dot.y{background:#febc2e}.m-dot.g{background:#28c840}
|
||||||
|
.m-bar-title{margin:0 auto;font-family:var(--font-m);font-size:.7rem;color:rgba(255,255,255,.28);letter-spacing:.02em}
|
||||||
|
.m-toolbar{height:40px;background:#191921;border-bottom:1px solid rgba(255,255,255,.05);display:flex;align-items:center;padding:0 14px;gap:6px;flex-shrink:0}
|
||||||
|
.m-tb-group{display:flex;align-items:center;gap:2px;padding:0 6px;border-right:1px solid rgba(255,255,255,.07)}
|
||||||
|
.m-tb-group:last-child{border:none;margin-left:auto}
|
||||||
|
.m-tb-btn{width:26px;height:26px;border-radius:5px;background:transparent;display:flex;align-items:center;justify-content:center;font-size:.7rem;color:rgba(255,255,255,.35);cursor:pointer;transition:background .12s}
|
||||||
|
.m-tb-btn:hover,.m-tb-btn.active{background:rgba(255,255,255,.1);color:rgba(255,255,255,.75)}
|
||||||
|
.m-tb-btn.active{color:#4d84e0}
|
||||||
|
.m-body{display:grid;grid-template-columns:196px 1fr 272px;min-height:0;height:440px}
|
||||||
|
|
||||||
|
/* Left nav */
|
||||||
|
.m-nav{background:#16161e;border-right:1px solid rgba(255,255,255,.055);display:flex;flex-direction:column;overflow:hidden}
|
||||||
|
.m-nav-head{padding:11px 13px 7px;font-family:var(--font-m);font-size:.6rem;color:rgba(255,255,255,.28);letter-spacing:.1em;text-transform:uppercase;border-bottom:1px solid rgba(255,255,255,.04)}
|
||||||
|
.m-nav-item{padding:7px 13px;font-size:.73rem;color:rgba(255,255,255,.45);display:flex;align-items:center;gap:8px;cursor:pointer;transition:background .12s}
|
||||||
|
.m-nav-item:hover{background:rgba(255,255,255,.04);color:rgba(255,255,255,.7)}
|
||||||
|
.m-nav-item.on{background:rgba(77,132,224,.12);color:rgba(255,255,255,.88)}
|
||||||
|
.m-nav-dot{width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,.25);flex-shrink:0}
|
||||||
|
.m-nav-item.on .m-nav-dot{background:#4d84e0}
|
||||||
|
.m-nav-indent{padding-left:26px}
|
||||||
|
.m-sep{height:1px;background:rgba(255,255,255,.04);margin:6px 0}
|
||||||
|
|
||||||
|
/* Canvas */
|
||||||
|
.m-canvas{background:#0e0e14;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}
|
||||||
|
.m-canvas::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.065) 1px,transparent 1px);background-size:22px 22px}
|
||||||
|
.m-artboard{position:relative;width:236px;background:#fff;border-radius:8px;box-shadow:0 8px 40px rgba(0,0,0,.7)}
|
||||||
|
.m-artboard::after{content:'';position:absolute;inset:-2px;border:2px solid #4d84e0;border-radius:10px;pointer-events:none}
|
||||||
|
.m-ab-label{position:absolute;top:-26px;left:0;font-family:var(--font-m);font-size:.6rem;background:#4d84e0;color:#fff;padding:3px 8px;border-radius:4px;white-space:nowrap}
|
||||||
|
.m-handle{position:absolute;width:7px;height:7px;background:#fff;border:2px solid #4d84e0;border-radius:2px}
|
||||||
|
.m-handle.tl{top:-3px;left:-3px}.m-handle.tr{top:-3px;right:-3px}
|
||||||
|
.m-handle.bl{bottom:-3px;left:-3px}.m-handle.br{bottom:-3px;right:-3px}
|
||||||
|
|
||||||
|
/* Component inside artboard */
|
||||||
|
.m-comp{padding:16px}
|
||||||
|
.m-comp-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}
|
||||||
|
.m-comp-title{font-size:.65rem;font-weight:600;color:#111;letter-spacing:-.01em}
|
||||||
|
.m-comp-live{font-size:.55rem;background:#ecfdf5;color:#059669;padding:2px 7px;border-radius:99px;font-weight:600;font-family:var(--font-m)}
|
||||||
|
.m-comp-val{font-size:1.25rem;font-weight:800;color:#0c0c10;letter-spacing:-.04em;line-height:1;margin-bottom:5px}
|
||||||
|
.m-comp-delta{font-size:.6rem;color:#16a34a;font-weight:500;display:flex;align-items:center;gap:3px;margin-bottom:12px}
|
||||||
|
.m-bar2{height:3px;background:#f0f0f0;border-radius:99px;overflow:hidden;margin-bottom:12px}
|
||||||
|
.m-bar2-fill{height:100%;width:68%;background:linear-gradient(90deg,#0F52BA,#4d84e0);border-radius:99px}
|
||||||
|
.m-comp-row{display:flex;gap:4px}
|
||||||
|
.m-chip{font-size:.55rem;background:#f5f5f5;color:#666;padding:3px 8px;border-radius:99px}
|
||||||
|
|
||||||
|
/* Inspector */
|
||||||
|
.m-insp{background:#16161e;border-left:1px solid rgba(255,255,255,.055);display:flex;flex-direction:column;overflow:hidden}
|
||||||
|
.m-tabs{display:flex;border-bottom:1px solid rgba(255,255,255,.055)}
|
||||||
|
.m-tab{flex:1;padding:10px 0;font-family:var(--font-m);font-size:.62rem;color:rgba(255,255,255,.3);text-align:center;letter-spacing:.06em;cursor:pointer;transition:color .12s}
|
||||||
|
.m-tab.on{color:rgba(255,255,255,.85);border-bottom:2px solid #4d84e0}
|
||||||
|
.m-sec{padding:12px 14px;border-bottom:1px solid rgba(255,255,255,.04)}
|
||||||
|
.m-sec-head{font-family:var(--font-m);font-size:.58rem;color:rgba(255,255,255,.28);letter-spacing:.1em;text-transform:uppercase;margin-bottom:9px}
|
||||||
|
.m-row{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:7px}
|
||||||
|
.m-key{font-family:var(--font-m);font-size:.62rem;color:rgba(255,255,255,.38)}
|
||||||
|
.m-val{font-family:var(--font-m);font-size:.62rem;color:rgba(255,255,255,.72)}
|
||||||
|
.m-val.s{color:#a8d9a0}.m-val.n{color:#79c0ff}.m-val.b{color:#f8a96a}
|
||||||
|
.m-diff{font-family:var(--font-m);font-size:.6rem;padding:3px 8px;border-radius:3px;margin-bottom:3px;line-height:1.5}
|
||||||
|
.m-diff.del{background:rgba(255,80,80,.1);color:#ff8888}
|
||||||
|
.m-diff.add{background:rgba(80,220,120,.09);color:#7ddba0}
|
||||||
|
.m-status{height:24px;background:#111116;border-top:1px solid rgba(255,255,255,.04);display:flex;align-items:center;padding:0 14px;gap:12px;flex-shrink:0}
|
||||||
|
.m-st-dot{width:6px;height:6px;border-radius:50%;background:#28c840;flex-shrink:0}
|
||||||
|
.m-st{font-family:var(--font-m);font-size:.58rem;color:rgba(255,255,255,.28)}
|
||||||
|
.m-st.on{color:rgba(255,255,255,.58)}
|
||||||
|
|
||||||
|
/* ── Animations ──────────────────────────────────────────── */
|
||||||
|
.rev{opacity:0;transform:translateY(22px);transition:opacity .65s cubic-bezier(.16,1,.3,1),transform .65s cubic-bezier(.16,1,.3,1)}
|
||||||
|
.rev.vis{opacity:1;transform:none}
|
||||||
|
.stag>*{opacity:0;transform:translateY(18px);transition:opacity .5s cubic-bezier(.16,1,.3,1),transform .5s cubic-bezier(.16,1,.3,1)}
|
||||||
|
.stag.vis>*:nth-child(1){opacity:1;transform:none;transition-delay:.00s}
|
||||||
|
.stag.vis>*:nth-child(2){opacity:1;transform:none;transition-delay:.07s}
|
||||||
|
.stag.vis>*:nth-child(3){opacity:1;transform:none;transition-delay:.14s}
|
||||||
|
.stag.vis>*:nth-child(4){opacity:1;transform:none;transition-delay:.21s}
|
||||||
|
.stag.vis>*:nth-child(5){opacity:1;transform:none;transition-delay:.28s}
|
||||||
|
|
||||||
|
/* ── Sec header ──────────────────────────────────────────── */
|
||||||
|
.sh{text-align:center;max-width:620px;margin:0 auto}
|
||||||
|
.sh .mono-label{display:block;margin-bottom:16px}
|
||||||
|
.sh .d2{margin-bottom:16px}
|
||||||
|
|
||||||
|
/* ── PROBLEM section ─────────────────────────────────────── */
|
||||||
|
.prob-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:2px;margin-top:64px}
|
||||||
|
.prob-card{background:#18181f;padding:40px 32px}
|
||||||
|
.prob-card:first-child{border-radius:var(--r-l) 0 0 var(--r-l)}
|
||||||
|
.prob-card:last-child{border-radius:0 var(--r-l) var(--r-l) 0}
|
||||||
|
.prob-icon{width:38px;height:38px;border-radius:var(--r-s);display:flex;align-items:center;justify-content:center;margin-bottom:20px;font-size:1rem}
|
||||||
|
.prob-icon.a{background:rgba(255,90,90,.12)}
|
||||||
|
.prob-icon.b{background:rgba(255,200,60,.10)}
|
||||||
|
.prob-icon.c{background:rgba(160,100,255,.10)}
|
||||||
|
.prob-card h3{font-family:var(--font-d);font-size:1.0625rem;font-weight:700;color:rgba(240,240,245,.9);margin-bottom:12px;letter-spacing:-.02em}
|
||||||
|
.prob-card p{font-size:.9rem;color:rgba(200,200,210,.45);line-height:1.7}
|
||||||
|
/* dark section labels */
|
||||||
|
.section-dark .sh .mono-label{color:rgba(240,240,245,.3)}
|
||||||
|
.section-dark .sh .d2{color:rgba(240,240,245,.95)}
|
||||||
|
.section-dark .sh .body-l{color:rgba(200,200,210,.45)}
|
||||||
|
|
||||||
|
/* ── FEATURES grid ───────────────────────────────────────── */
|
||||||
|
.feat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--border);border-radius:var(--r-xl);overflow:hidden;margin-top:64px}
|
||||||
|
.feat-card{background:var(--bg);padding:36px 30px;transition:background .15s}
|
||||||
|
.feat-card:hover{background:var(--bg-2)}
|
||||||
|
.feat-card:nth-child(4){grid-column:1/2}
|
||||||
|
.feat-card:nth-child(5){grid-column:2/4}
|
||||||
|
.feat-icon{width:36px;height:36px;border-radius:var(--r-s);background:var(--accent-soft);display:flex;align-items:center;justify-content:center;font-size:.95rem;margin-bottom:18px}
|
||||||
|
.feat-card h3{font-family:var(--font-d);font-size:1rem;font-weight:700;color:var(--fg);margin-bottom:8px;letter-spacing:-.02em}
|
||||||
|
.feat-card p{font-size:.875rem;color:var(--fg-2);line-height:1.65}
|
||||||
|
|
||||||
|
/* ── HOW IT WORKS ────────────────────────────────────────── */
|
||||||
|
.steps{display:grid;grid-template-columns:repeat(5,1fr);gap:0;margin-top:64px;position:relative}
|
||||||
|
.steps::before{content:'';position:absolute;top:27px;left:calc(10%);right:calc(10%);height:1px;background:var(--border-2);z-index:0}
|
||||||
|
.step{display:flex;flex-direction:column;align-items:center;text-align:center;padding:0 12px;position:relative;z-index:1}
|
||||||
|
.step-n{width:54px;height:54px;border-radius:50%;background:var(--bg);border:1px solid var(--border-2);display:flex;align-items:center;justify-content:center;font-family:var(--font-d);font-size:1.0625rem;font-weight:800;color:var(--accent);margin-bottom:20px;letter-spacing:-.03em}
|
||||||
|
.step h3{font-family:var(--font-d);font-size:.9375rem;font-weight:700;color:var(--fg);margin-bottom:8px;letter-spacing:-.015em}
|
||||||
|
.step p{font-size:.8125rem;color:var(--fg-2);line-height:1.6}
|
||||||
|
|
||||||
|
/* ── INTEGRATIONS ────────────────────────────────────────── */
|
||||||
|
.int-grid{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:52px}
|
||||||
|
.int-pill{display:flex;align-items:center;gap:8px;padding:10px 20px;border-radius:var(--r-p);background:var(--bg);border:1px solid var(--border-2);font-size:.875rem;font-weight:500;color:var(--fg);box-shadow:var(--sh-s);transition:transform .14s,box-shadow .14s;cursor:default}
|
||||||
|
.int-pill:hover{transform:translateY(-2px);box-shadow:var(--sh-m)}
|
||||||
|
.int-pill .ico{width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:.9rem}
|
||||||
|
|
||||||
|
/* ── COMPARISON ──────────────────────────────────────────── */
|
||||||
|
.cmp-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:64px}
|
||||||
|
.cmp-card{background:var(--bg);border-radius:var(--r-l);padding:36px 30px;border:1px solid var(--border)}
|
||||||
|
.cmp-card.feat{background:var(--fg);border-color:transparent}
|
||||||
|
.cmp-vs{font-family:var(--font-m);font-size:.65rem;letter-spacing:.1em;text-transform:uppercase;margin-bottom:16px;color:var(--fg-3)}
|
||||||
|
.cmp-card.feat .cmp-vs{color:rgba(255,255,255,.35)}
|
||||||
|
.cmp-name{font-family:var(--font-d);font-size:1.5rem;font-weight:700;letter-spacing:-.035em;margin-bottom:16px;color:var(--fg)}
|
||||||
|
.cmp-card.feat .cmp-name{color:#fff}
|
||||||
|
.cmp-div{height:1px;background:var(--border);margin:16px 0}
|
||||||
|
.cmp-card.feat .cmp-div{background:rgba(255,255,255,.1)}
|
||||||
|
.cmp-body{font-size:.875rem;line-height:1.65;color:var(--fg-2);margin-bottom:20px}
|
||||||
|
.cmp-card.feat .cmp-body{color:rgba(255,255,255,.58)}
|
||||||
|
.cmp-tags{display:flex;flex-wrap:wrap;gap:6px}
|
||||||
|
.cmp-tag{font-size:.75rem;padding:4px 11px;border-radius:var(--r-p);font-weight:500;background:var(--bg-2);color:var(--fg-2)}
|
||||||
|
.cmp-card.feat .cmp-tag{background:rgba(255,255,255,.11);color:rgba(255,255,255,.7)}
|
||||||
|
|
||||||
|
/* ── QUOTE ───────────────────────────────────────────────── */
|
||||||
|
#quote{padding:140px 32px}
|
||||||
|
.q-wrap{max-width:780px;margin:0 auto;text-align:center}
|
||||||
|
.q-mark{font-family:var(--font-d);font-size:4.5rem;line-height:.5;color:var(--accent);margin-bottom:36px;display:block;font-weight:800}
|
||||||
|
.q-text{font-family:var(--font-d);font-size:clamp(1.25rem,2.5vw,1.875rem);font-weight:600;line-height:1.4;letter-spacing:-.025em;color:var(--fg);margin-bottom:32px}
|
||||||
|
.q-attr{font-size:.875rem;color:var(--fg-3)}
|
||||||
|
|
||||||
|
/* ── PRICING ─────────────────────────────────────────────── */
|
||||||
|
.px-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:64px}
|
||||||
|
.px-card{border-radius:var(--r-l);padding:36px 30px;border:1px solid var(--border);background:var(--bg)}
|
||||||
|
.px-card.feat{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent),var(--sh-m)}
|
||||||
|
.px-tier{font-family:var(--font-m);font-size:.65rem;letter-spacing:.1em;text-transform:uppercase;color:var(--fg-3);margin-bottom:12px}
|
||||||
|
.px-card.feat .px-tier{color:var(--accent)}
|
||||||
|
.px-price{font-family:var(--font-d);font-size:2.75rem;font-weight:800;letter-spacing:-.045em;color:var(--fg);margin-bottom:4px;line-height:1}
|
||||||
|
.px-period{font-size:.8125rem;color:var(--fg-3);margin-bottom:24px}
|
||||||
|
.px-div{height:1px;background:var(--border);margin:20px 0}
|
||||||
|
.px-feats{list-style:none;display:flex;flex-direction:column;gap:10px;margin-bottom:28px}
|
||||||
|
.px-feats li{font-size:.875rem;color:var(--fg-2);display:flex;align-items:flex-start;gap:10px;line-height:1.5}
|
||||||
|
.px-feats li::before{content:'✓';color:var(--accent);font-weight:700;flex-shrink:0;margin-top:1px}
|
||||||
|
|
||||||
|
/* ── FOOTER ──────────────────────────────────────────────── */
|
||||||
|
footer{background:var(--fg);padding:80px 32px 40px}
|
||||||
|
.ft-i{max-width:1200px;margin:0 auto}
|
||||||
|
.ft-top{display:grid;grid-template-columns:260px repeat(4,1fr);gap:48px;padding-bottom:60px;border-bottom:1px solid rgba(255,255,255,.1);margin-bottom:40px}
|
||||||
|
.ft-logo{font-family:var(--font-d);font-size:1.25rem;font-weight:800;color:#fff;letter-spacing:-.04em;margin-bottom:12px}
|
||||||
|
.ft-tag{font-size:.875rem;color:rgba(255,255,255,.38);line-height:1.6}
|
||||||
|
.ft-col h4{font-size:.875rem;font-weight:600;color:rgba(255,255,255,.82);margin-bottom:16px}
|
||||||
|
.ft-col ul{list-style:none;display:flex;flex-direction:column;gap:10px}
|
||||||
|
.ft-col a{font-size:.875rem;color:rgba(255,255,255,.38);text-decoration:none;transition:color .14s}
|
||||||
|
.ft-col a:hover{color:rgba(255,255,255,.72)}
|
||||||
|
.ft-bot{display:flex;justify-content:space-between;align-items:center}
|
||||||
|
.ft-copy{font-size:.8125rem;color:rgba(255,255,255,.28)}
|
||||||
|
.theme-btn{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:var(--r-p);padding:7px 16px;font-size:.8125rem;color:rgba(255,255,255,.5);cursor:pointer;display:flex;align-items:center;gap:6px;transition:background .14s,color .14s;font-family:var(--font-b)}
|
||||||
|
.theme-btn:hover{background:rgba(255,255,255,.12);color:rgba(255,255,255,.82)}
|
||||||
|
|
||||||
|
/* ── Responsive ──────────────────────────────────────────── */
|
||||||
|
@media(max-width:900px){
|
||||||
|
.prob-grid,.feat-grid,.cmp-grid,.px-grid,.steps{grid-template-columns:1fr}
|
||||||
|
.prob-card:first-child{border-radius:var(--r-l) var(--r-l) 0 0}
|
||||||
|
.prob-card:last-child{border-radius:0 0 var(--r-l) var(--r-l)}
|
||||||
|
.feat-card:nth-child(4),.feat-card:nth-child(5){grid-column:1}
|
||||||
|
.steps::before{display:none}
|
||||||
|
.m-body{grid-template-columns:164px 1fr}
|
||||||
|
.m-insp{display:none}
|
||||||
|
}
|
||||||
|
@media(max-width:700px){
|
||||||
|
.section{padding:80px 0}
|
||||||
|
.wrap{padding:0 20px}
|
||||||
|
nav,.m-bar{padding-left:20px;padding-right:20px}
|
||||||
|
.nav-links{display:none}
|
||||||
|
.ft-top{grid-template-columns:1fr 1fr}
|
||||||
|
.hero-ctas{flex-direction:column;align-items:center}
|
||||||
|
#hero{padding:120px 20px 60px}
|
||||||
|
}
|
||||||
|
@media(max-width:480px){
|
||||||
|
.ft-top{grid-template-columns:1fr}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- NAV -->
|
||||||
|
<nav id="nav">
|
||||||
|
<div class="nav-i">
|
||||||
|
<a href="#" class="nav-logo">Origin<b>main</b></a>
|
||||||
|
<ul class="nav-links">
|
||||||
|
<li><a href="#features">Product</a></li>
|
||||||
|
<li><a href="#integrations">Integrations</a></li>
|
||||||
|
<li><a href="#pricing">Pricing</a></li>
|
||||||
|
<li><a href="#">Docs</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="nav-r">
|
||||||
|
<a href="#" class="btn btn-ghost" style="padding:7px 16px;font-size:.875rem">Log in</a>
|
||||||
|
<a href="#" class="btn btn-blk" style="padding:8px 18px;font-size:.875rem">Request Access</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- HERO -->
|
||||||
|
<section id="hero">
|
||||||
|
<div class="hero-eye rev">
|
||||||
|
<span class="badge badge-accent">
|
||||||
|
<span style="font-size:.5rem;line-height:1">●</span> Early Access — Now Open
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="d1 hero-h rev">
|
||||||
|
Design is code.<br>Code is design.
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="body-l hero-sub rev">
|
||||||
|
The first canvas where every artboard is a live render of your actual app — and every edit is a code diff.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="hero-ctas rev">
|
||||||
|
<a href="#" class="btn btn-blk btn-lg">Request Early Access</a>
|
||||||
|
<a href="#" class="btn btn-ghost btn-lg">
|
||||||
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
||||||
|
Watch the demo
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- App mockup -->
|
||||||
|
<div class="mockup rev">
|
||||||
|
<div class="m-bar">
|
||||||
|
<div class="m-dot r"></div>
|
||||||
|
<div class="m-dot y"></div>
|
||||||
|
<div class="m-dot g"></div>
|
||||||
|
<span class="m-bar-title">Originmain — DashboardCard.tsx</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-toolbar">
|
||||||
|
<div class="m-tb-group">
|
||||||
|
<div class="m-tb-btn active" title="Select">↖</div>
|
||||||
|
<div class="m-tb-btn" title="Pan">✥</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-tb-group">
|
||||||
|
<div class="m-tb-btn" title="New artboard">⬜</div>
|
||||||
|
<div class="m-tb-btn" title="Add component">+</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-tb-group">
|
||||||
|
<div class="m-tb-btn" title="Completion zone">◈</div>
|
||||||
|
<div class="m-tb-btn" title="Diff view">△</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-tb-group">
|
||||||
|
<div class="m-tb-btn" style="font-size:.6rem;color:rgba(255,255,255,.45);width:auto;padding:0 6px;gap:4px">
|
||||||
|
<span style="color:#28c840;font-size:.55rem">●</span> Live
|
||||||
|
</div>
|
||||||
|
<div class="m-tb-btn" style="font-size:.6rem;width:auto;padding:0 6px">100%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-body">
|
||||||
|
<!-- Navigator -->
|
||||||
|
<div class="m-nav">
|
||||||
|
<div class="m-nav-head">Artboards</div>
|
||||||
|
<div class="m-nav-item on">
|
||||||
|
<div class="m-nav-dot"></div>DashboardCard
|
||||||
|
</div>
|
||||||
|
<div class="m-nav-item">
|
||||||
|
<div class="m-nav-dot"></div>UserProfile
|
||||||
|
</div>
|
||||||
|
<div class="m-nav-item">
|
||||||
|
<div class="m-nav-dot"></div>NavSidebar
|
||||||
|
</div>
|
||||||
|
<div class="m-nav-item">
|
||||||
|
<div class="m-nav-dot"></div>DataTable
|
||||||
|
</div>
|
||||||
|
<div class="m-sep"></div>
|
||||||
|
<div class="m-nav-head">Origin Graph</div>
|
||||||
|
<div class="m-nav-item">
|
||||||
|
<div class="m-nav-dot"></div>src/components
|
||||||
|
</div>
|
||||||
|
<div class="m-nav-item m-nav-indent" style="font-size:.68rem">
|
||||||
|
<div class="m-nav-dot"></div>DashboardCard
|
||||||
|
</div>
|
||||||
|
<div class="m-nav-item m-nav-indent" style="font-size:.68rem">
|
||||||
|
<div class="m-nav-dot"></div>StatsCard
|
||||||
|
</div>
|
||||||
|
<div class="m-nav-item">
|
||||||
|
<div class="m-nav-dot"></div>src/app
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Canvas -->
|
||||||
|
<div class="m-canvas">
|
||||||
|
<div class="m-artboard">
|
||||||
|
<div class="m-ab-label">DashboardCard</div>
|
||||||
|
<div class="m-handle tl"></div>
|
||||||
|
<div class="m-handle tr"></div>
|
||||||
|
<div class="m-handle bl"></div>
|
||||||
|
<div class="m-handle br"></div>
|
||||||
|
<div class="m-comp">
|
||||||
|
<div class="m-comp-head">
|
||||||
|
<div class="m-comp-title">Revenue Overview</div>
|
||||||
|
<div class="m-comp-live">Live</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-comp-val">$12,450</div>
|
||||||
|
<div class="m-comp-delta">↑ +2.4% vs last month</div>
|
||||||
|
<div class="m-bar2"><div class="m-bar2-fill"></div></div>
|
||||||
|
<div class="m-comp-row">
|
||||||
|
<div class="m-chip">Q4 2024</div>
|
||||||
|
<div class="m-chip">MRR</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Inspector -->
|
||||||
|
<div class="m-insp">
|
||||||
|
<div class="m-tabs">
|
||||||
|
<div class="m-tab on">Props</div>
|
||||||
|
<div class="m-tab">Diff</div>
|
||||||
|
<div class="m-tab">Graph</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-sec">
|
||||||
|
<div class="m-sec-head">Component Props</div>
|
||||||
|
<div class="m-row"><span class="m-key">title</span><span class="m-val s">"Revenue Overview"</span></div>
|
||||||
|
<div class="m-row"><span class="m-key">value</span><span class="m-val s">"$12,450"</span></div>
|
||||||
|
<div class="m-row"><span class="m-key">delta</span><span class="m-val n">+2.4</span></div>
|
||||||
|
<div class="m-row"><span class="m-key">period</span><span class="m-val s">"monthly"</span></div>
|
||||||
|
<div class="m-row"><span class="m-key">loading</span><span class="m-val b">false</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="m-sec">
|
||||||
|
<div class="m-sec-head">Intent Diff</div>
|
||||||
|
<div class="m-diff del">− borderRadius: 8px</div>
|
||||||
|
<div class="m-diff add">+ borderRadius: 12px</div>
|
||||||
|
<div class="m-diff del">− accentColor: #2A6CD4</div>
|
||||||
|
<div class="m-diff add">+ accentColor: #0F52BA</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-sec">
|
||||||
|
<div class="m-sec-head">Render Target</div>
|
||||||
|
<div class="m-row"><span class="m-key">file</span><span class="m-val n">dashboard.tsx:42</span></div>
|
||||||
|
<div class="m-row"><span class="m-key">status</span><span class="m-val b">connected</span></div>
|
||||||
|
<div class="m-row"><span class="m-key">agent</span><span class="m-val s">claude-code</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-status">
|
||||||
|
<div class="m-st-dot"></div>
|
||||||
|
<span class="m-st on">Live render connected</span>
|
||||||
|
<span class="m-st" style="margin-left:auto">Agent Bridge: ready</span>
|
||||||
|
<span class="m-st">Graph: 284 nodes</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- THE PROBLEM -->
|
||||||
|
<section class="section section-dark" id="problem">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sh rev">
|
||||||
|
<span class="mono-label">The Problem</span>
|
||||||
|
<h2 class="d2">Every design tool lies.</h2>
|
||||||
|
<p class="body-l">Your design file and your codebase diverge the moment you ship. Every handoff creates a gap. Every gap creates debt.</p>
|
||||||
|
</div>
|
||||||
|
<div class="prob-grid stag">
|
||||||
|
<div class="prob-card">
|
||||||
|
<div class="prob-icon a">↕</div>
|
||||||
|
<h3>The Handoff Gap</h3>
|
||||||
|
<p>Design files describe pixels. Code describes behaviour. Neither speaks the other's language — designers spec what they imagine, engineers ship what they can build in time.</p>
|
||||||
|
</div>
|
||||||
|
<div class="prob-card">
|
||||||
|
<div class="prob-icon b">⊘</div>
|
||||||
|
<h3>The Context Collapse</h3>
|
||||||
|
<p>Switch from Figma to your editor and every annotation, constraint, and decision stays behind. Context collapses into a Slack thread that nobody reads twice.</p>
|
||||||
|
</div>
|
||||||
|
<div class="prob-card">
|
||||||
|
<div class="prob-icon c">□</div>
|
||||||
|
<h3>The Blank Canvas Problem</h3>
|
||||||
|
<p>AI tools generate strangers. v0 produces components your design system doesn't recognise. The blank canvas ignores all the decisions already encoded in your app.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FEATURES -->
|
||||||
|
<section class="section" id="features">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sh rev">
|
||||||
|
<span class="mono-label">Core Concepts</span>
|
||||||
|
<h2 class="d2">Five ideas that change how design works.</h2>
|
||||||
|
<p class="body-l">Each feature removes a seam between design intent and production code.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feat-grid stag">
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="feat-icon">⬜</div>
|
||||||
|
<h3>Live Artboard</h3>
|
||||||
|
<p>A live render of your actual application — not a mockup. Every artboard is backed by a real component tree running your real code.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="feat-icon">◉</div>
|
||||||
|
<h3>Origin Graph</h3>
|
||||||
|
<p>Every artboard traces its provenance through a queryable graph. Know exactly which files, props, and tokens compose each surface.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="feat-icon">△</div>
|
||||||
|
<h3>Intent Diff</h3>
|
||||||
|
<p>Visual changes expressed at the component level, not the pixel level. Ship diffs that say "increase Card border-radius" — not a PNG redline.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="feat-icon">◈</div>
|
||||||
|
<h3>Completion Zone</h3>
|
||||||
|
<p>Mark a region. AI fills it within your design language constraints — using your actual tokens, your actual components, nothing invented.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="feat-icon">⇄</div>
|
||||||
|
<h3>Agent Bridge</h3>
|
||||||
|
<p>Bidirectional MCP channel between your design surface and coding agents. Claude, Cursor, Copilot — they all see the canvas and the graph simultaneously.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- HOW IT WORKS -->
|
||||||
|
<section class="section section-tinted" id="how">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sh rev">
|
||||||
|
<span class="mono-label">Workflow</span>
|
||||||
|
<h2 class="d2">From codebase to canvas in five steps.</h2>
|
||||||
|
</div>
|
||||||
|
<div class="steps stag">
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-n">01</div>
|
||||||
|
<h3>Ingest</h3>
|
||||||
|
<p>Point Originmain at your repo. The Origin Graph indexes your components, tokens, and routes.</p>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-n">02</div>
|
||||||
|
<h3>Edit</h3>
|
||||||
|
<p>Open any route as a live artboard. Every visual change is a real prop or style edit.</p>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-n">03</div>
|
||||||
|
<h3>Complete</h3>
|
||||||
|
<p>Mark a region and let AI suggest completions constrained to your existing design language.</p>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-n">04</div>
|
||||||
|
<h3>Export</h3>
|
||||||
|
<p>Export an Intent Diff — a machine-readable component-level description of what changed.</p>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-n">05</div>
|
||||||
|
<h3>Verify</h3>
|
||||||
|
<p>Agents apply the diff and verify rendering parity. The canvas confirms the result.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- INTEGRATIONS -->
|
||||||
|
<section class="section" id="integrations">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sh rev">
|
||||||
|
<span class="mono-label">Integrations</span>
|
||||||
|
<h2 class="d2">Connects to your entire stack.</h2>
|
||||||
|
<p class="body-l">Slots into the tools you already use. No migration required.</p>
|
||||||
|
</div>
|
||||||
|
<div class="int-grid rev">
|
||||||
|
<div class="int-pill"><span class="ico">🔗</span> Linear</div>
|
||||||
|
<div class="int-pill"><span class="ico">💬</span> Slack</div>
|
||||||
|
<div class="int-pill"><span class="ico">🐙</span> GitHub</div>
|
||||||
|
<div class="int-pill"><span class="ico">🖱</span> Cursor</div>
|
||||||
|
<div class="int-pill"><span class="ico">🤖</span> Claude Code</div>
|
||||||
|
<div class="int-pill"><span class="ico">◆</span> Figma (import)</div>
|
||||||
|
<div class="int-pill"><span class="ico">▲</span> Next.js</div>
|
||||||
|
<div class="int-pill"><span class="ico">⚛</span> React</div>
|
||||||
|
<div class="int-pill"><span class="ico">🟦</span> VS Code</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- COMPARISON -->
|
||||||
|
<section class="section section-tinted" id="comparison">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sh rev">
|
||||||
|
<span class="mono-label">Differentiation</span>
|
||||||
|
<h2 class="d2">Not another design tool.</h2>
|
||||||
|
<p class="body-l">Existing tools solve half the problem. Originmain closes the loop.</p>
|
||||||
|
</div>
|
||||||
|
<div class="cmp-grid stag">
|
||||||
|
<div class="cmp-card">
|
||||||
|
<div class="cmp-vs">vs.</div>
|
||||||
|
<div class="cmp-name">Figma</div>
|
||||||
|
<div class="cmp-div"></div>
|
||||||
|
<p class="cmp-body">Figma is the best interface design tool ever made — but what you design is a fiction. It has no knowledge of your codebase, tokens, or component API surface. The handoff is manual, lossy, and perpetually stale.</p>
|
||||||
|
<div class="cmp-tags">
|
||||||
|
<span class="cmp-tag">Static mockups</span>
|
||||||
|
<span class="cmp-tag">Manual handoff</span>
|
||||||
|
<span class="cmp-tag">No code awareness</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cmp-card feat">
|
||||||
|
<div class="cmp-vs">Originmain</div>
|
||||||
|
<div class="cmp-name">Design is live code.</div>
|
||||||
|
<div class="cmp-div"></div>
|
||||||
|
<p class="cmp-body">Every artboard is a live render. Every edit writes code. The gap between design and production is zero — because they're the same surface, indexed by the same graph.</p>
|
||||||
|
<div class="cmp-tags">
|
||||||
|
<span class="cmp-tag">Live renders</span>
|
||||||
|
<span class="cmp-tag">Code diffs</span>
|
||||||
|
<span class="cmp-tag">AI-constrained</span>
|
||||||
|
<span class="cmp-tag">Graph-indexed</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cmp-card">
|
||||||
|
<div class="cmp-vs">vs.</div>
|
||||||
|
<div class="cmp-name">v0 / Cursor</div>
|
||||||
|
<div class="cmp-div"></div>
|
||||||
|
<p class="cmp-body">v0 and Cursor are exceptional at generating code from a blank slate — but they hallucinate design systems. They don't know your tokens, your component API, or what "on-brand" means for your product.</p>
|
||||||
|
<div class="cmp-tags">
|
||||||
|
<span class="cmp-tag">Generates strangers</span>
|
||||||
|
<span class="cmp-tag">No design system</span>
|
||||||
|
<span class="cmp-tag">No visual canvas</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- QUOTE -->
|
||||||
|
<section id="quote">
|
||||||
|
<div class="q-wrap rev">
|
||||||
|
<span class="q-mark">"</span>
|
||||||
|
<p class="q-text">The ideal design tool is a canvas where any view of your application can be rendered, explored, duplicated, and modified visually — with every change traced back to a diff in your actual codebase.</p>
|
||||||
|
<div class="q-attr">— Design Engineer at a Series B startup</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- PRICING -->
|
||||||
|
<section class="section" id="pricing">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sh rev">
|
||||||
|
<span class="mono-label">Pricing</span>
|
||||||
|
<h2 class="d2">Simple, team-based pricing.</h2>
|
||||||
|
<p class="body-l">Early access is free. All plans include the full Origin Graph and Live Artboard engine.</p>
|
||||||
|
</div>
|
||||||
|
<div class="px-grid stag">
|
||||||
|
<div class="px-card">
|
||||||
|
<div class="px-tier">Starter</div>
|
||||||
|
<div class="px-price">Free</div>
|
||||||
|
<div class="px-period">Solo use, forever</div>
|
||||||
|
<div class="px-div"></div>
|
||||||
|
<ul class="px-feats">
|
||||||
|
<li>Unlimited artboards</li>
|
||||||
|
<li>Origin Graph (up to 5k nodes)</li>
|
||||||
|
<li>Intent Diff export</li>
|
||||||
|
<li>3 AI completions / day</li>
|
||||||
|
<li>GitHub integration</li>
|
||||||
|
</ul>
|
||||||
|
<a href="#" class="btn btn-ghost btn-full">Get started free</a>
|
||||||
|
</div>
|
||||||
|
<div class="px-card feat">
|
||||||
|
<div class="px-tier">Team</div>
|
||||||
|
<div class="px-price">$49</div>
|
||||||
|
<div class="px-period">per seat / month · up to 10 seats</div>
|
||||||
|
<div class="px-div"></div>
|
||||||
|
<ul class="px-feats">
|
||||||
|
<li>Everything in Starter</li>
|
||||||
|
<li>Unlimited Origin Graph nodes</li>
|
||||||
|
<li>Unlimited AI completions</li>
|
||||||
|
<li>Agent Bridge (MCP)</li>
|
||||||
|
<li>Linear + Slack integration</li>
|
||||||
|
<li>Multiplayer canvas</li>
|
||||||
|
</ul>
|
||||||
|
<a href="#" class="btn btn-accent btn-full">Request early access</a>
|
||||||
|
</div>
|
||||||
|
<div class="px-card">
|
||||||
|
<div class="px-tier">Enterprise</div>
|
||||||
|
<div class="px-price">Custom</div>
|
||||||
|
<div class="px-period">Unlimited seats</div>
|
||||||
|
<div class="px-div"></div>
|
||||||
|
<ul class="px-feats">
|
||||||
|
<li>Everything in Team</li>
|
||||||
|
<li>SSO / SAML</li>
|
||||||
|
<li>On-premise deployment</li>
|
||||||
|
<li>SLA & dedicated support</li>
|
||||||
|
<li>Custom integrations</li>
|
||||||
|
<li>Design system audit</li>
|
||||||
|
</ul>
|
||||||
|
<a href="#" class="btn btn-ghost btn-full">Talk to us</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<footer>
|
||||||
|
<div class="ft-i">
|
||||||
|
<div class="ft-top">
|
||||||
|
<div>
|
||||||
|
<div class="ft-logo">Originmain</div>
|
||||||
|
<p class="ft-tag">Where design intent becomes production code.</p>
|
||||||
|
</div>
|
||||||
|
<div class="ft-col">
|
||||||
|
<h4>Product</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#">Live Artboard</a></li>
|
||||||
|
<li><a href="#">Origin Graph</a></li>
|
||||||
|
<li><a href="#">Intent Diff</a></li>
|
||||||
|
<li><a href="#">Agent Bridge</a></li>
|
||||||
|
<li><a href="#">Pricing</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="ft-col">
|
||||||
|
<h4>Developers</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#">Documentation</a></li>
|
||||||
|
<li><a href="#">API Reference</a></li>
|
||||||
|
<li><a href="#">MCP Protocol</a></li>
|
||||||
|
<li><a href="#">Changelog</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="ft-col">
|
||||||
|
<h4>Company</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#">About</a></li>
|
||||||
|
<li><a href="#">Blog</a></li>
|
||||||
|
<li><a href="#">Careers</a></li>
|
||||||
|
<li><a href="#">Contact</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="ft-col">
|
||||||
|
<h4>Legal</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#">Privacy</a></li>
|
||||||
|
<li><a href="#">Terms</a></li>
|
||||||
|
<li><a href="#">Security</a></li>
|
||||||
|
<li><a href="#">Cookies</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ft-bot">
|
||||||
|
<span class="ft-copy">© 2025 Originmain. All rights reserved.</span>
|
||||||
|
<button class="theme-btn" id="themeBtn">
|
||||||
|
<span id="themeIco">☾</span>
|
||||||
|
<span id="themeLbl">Dark mode</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Storage-safe helper
|
||||||
|
function ls(op, key, val) {
|
||||||
|
try { return op === 'get' ? localStorage.getItem(key) : localStorage.setItem(key, val); } catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme
|
||||||
|
const root = document.documentElement;
|
||||||
|
let theme = ls('get','om-theme') || 'light';
|
||||||
|
const btn = document.getElementById('themeBtn');
|
||||||
|
const ico = document.getElementById('themeIco');
|
||||||
|
const lbl = document.getElementById('themeLbl');
|
||||||
|
|
||||||
|
function applyTheme(t) {
|
||||||
|
theme = t;
|
||||||
|
root.setAttribute('data-theme', t);
|
||||||
|
ls('set','om-theme', t);
|
||||||
|
ico.textContent = t === 'dark' ? '☀' : '☾';
|
||||||
|
lbl.textContent = t === 'dark' ? 'Light mode' : 'Dark mode';
|
||||||
|
}
|
||||||
|
applyTheme(theme);
|
||||||
|
btn.addEventListener('click', () => applyTheme(theme === 'light' ? 'dark' : 'light'));
|
||||||
|
|
||||||
|
// Nav glass
|
||||||
|
const nav = document.getElementById('nav');
|
||||||
|
window.addEventListener('scroll', () => nav.classList.toggle('glass', window.scrollY > 16), {passive:true});
|
||||||
|
|
||||||
|
// Scroll reveal — always show everything after 400ms as a fallback
|
||||||
|
const revEls = document.querySelectorAll('.rev,.stag');
|
||||||
|
const revealAll = () => revEls.forEach(el => el.classList.add('vis'));
|
||||||
|
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
const io = new IntersectionObserver(entries => {
|
||||||
|
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('vis'); io.unobserve(e.target); } });
|
||||||
|
}, {threshold: 0.01, rootMargin: '0px'});
|
||||||
|
revEls.forEach(el => io.observe(el));
|
||||||
|
} else {
|
||||||
|
revealAll();
|
||||||
|
}
|
||||||
|
// Failsafe: if anything is still hidden after 600ms, show it
|
||||||
|
setTimeout(revealAll, 600);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "originmain",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "pnpm -r run typecheck",
|
||||||
|
"lint": "eslint . --max-warnings 0",
|
||||||
|
"test": "vitest run --coverage",
|
||||||
|
"build": "pnpm --filter @originmain/app run build",
|
||||||
|
"dev": "pnpm --filter @originmain/app run dev",
|
||||||
|
"migration:dry-run": "supabase db diff --schema public"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
|
"@vitest/coverage-v8": "^2.0.0",
|
||||||
|
"eslint": "^9.0.0",
|
||||||
|
"typescript": "^5.5.0",
|
||||||
|
"vitest": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22",
|
||||||
|
"pnpm": ">=9"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/agent-bridge",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@originmain/diff-engine": "workspace:*",
|
||||||
|
"@originmain/origin-graph": "workspace:*",
|
||||||
|
"@originmain/ai-layer": "workspace:*",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/ai-layer",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@originmain/diff-engine": "workspace:*",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
/// <reference path="./.next/types/routes.d.ts" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import type { NextConfig } from 'next';
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
transpilePackages: ['@originmain/ui', '@fluentui/react-components'],
|
||||||
|
experimental: {
|
||||||
|
optimizePackageImports: ['@fluentui/react-components'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/app",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@originmain/ui": "workspace:*",
|
||||||
|
"@originmain/diff-engine": "workspace:*",
|
||||||
|
"@originmain/origin-graph": "workspace:*",
|
||||||
|
"@originmain/renderer": "workspace:*",
|
||||||
|
"@fluentui/react-components": "^9.54.0",
|
||||||
|
"next": "^15.1.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"zustand": "^5.0.0",
|
||||||
|
"@tanstack/react-query": "^5.62.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"@types/react-dom": "^19.0.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body, #__next {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI Variable', 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { Providers } from './providers';
|
||||||
|
import './globals.css';
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: 'Originmain',
|
||||||
|
description: 'AI-Native Design Engineering Platform',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<Providers>{children}</Providers>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
|
||||||
|
<p style={{ fontFamily: 'system-ui, sans-serif', color: '#888', fontSize: '0.875rem' }}>
|
||||||
|
Originmain — coming soon
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { FluentProvider } from '@fluentui/react-components';
|
||||||
|
import { originmainLightTheme } from '@originmain/ui';
|
||||||
|
|
||||||
|
export function Providers({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<FluentProvider theme={originmainLightTheme} style={{ height: '100%' }}>
|
||||||
|
{children}
|
||||||
|
</FluentProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface ViewportState {
|
||||||
|
panX: number;
|
||||||
|
panY: number;
|
||||||
|
zoom: number;
|
||||||
|
setPan: (x: number, y: number) => void;
|
||||||
|
setZoom: (zoom: number, originX?: number, originY?: number) => void;
|
||||||
|
reset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useViewport = create<ViewportState>((set, get) => ({
|
||||||
|
panX: 0,
|
||||||
|
panY: 0,
|
||||||
|
zoom: 1,
|
||||||
|
|
||||||
|
setPan: (x, y) => set({ panX: x, panY: y }),
|
||||||
|
|
||||||
|
setZoom: (zoom, originX = 0, originY = 0) => {
|
||||||
|
const prev = get();
|
||||||
|
const clampedZoom = Math.min(Math.max(zoom, 0.1), 8);
|
||||||
|
const scale = clampedZoom / prev.zoom;
|
||||||
|
set({
|
||||||
|
zoom: clampedZoom,
|
||||||
|
panX: originX - (originX - prev.panX) * scale,
|
||||||
|
panY: originY - (originY - prev.panY) * scale,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: () => set({ panX: 0, panY: 0, zoom: 1 }),
|
||||||
|
}));
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noEmit": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"incremental": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"jsx": "preserve"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"next.config.ts",
|
||||||
|
"src",
|
||||||
|
".next/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
".next"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/diff-engine",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0",
|
||||||
|
"vitest": "^2.0.0",
|
||||||
|
"@vitest/coverage-v8": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src", "__tests__"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/e2e",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "playwright test",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.40.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// placeholder — Playwright tests added in Layer 11
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "**/*.tsx"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/integrations",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@originmain/origin-graph": "workspace:*",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/origin-graph",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@originmain/diff-engine": "workspace:*",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0",
|
||||||
|
"vitest": "^2.0.0",
|
||||||
|
"@vitest/coverage-v8": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src", "__tests__"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/renderer",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.5.0",
|
||||||
|
"zod": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "@originmain/ui",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fluentui/react-components": "^9.54.0",
|
||||||
|
"react": "^19.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"typescript": "^5.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { originmainLightTheme, originmainDarkTheme } from './themes/originmain-theme';
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { createLightTheme, createDarkTheme, type BrandVariants } from '@fluentui/react-components';
|
||||||
|
|
||||||
|
const cobaltBrand: BrandVariants = {
|
||||||
|
10: '#020B1A',
|
||||||
|
20: '#041630',
|
||||||
|
30: '#062246',
|
||||||
|
40: '#083060',
|
||||||
|
50: '#0A3E7A',
|
||||||
|
60: '#0C4C94',
|
||||||
|
70: '#0F52BA',
|
||||||
|
80: '#2A6CD4',
|
||||||
|
90: '#4C86E0',
|
||||||
|
100: '#70A0E8',
|
||||||
|
110: '#94BAEF',
|
||||||
|
120: '#B4CEF5',
|
||||||
|
130: '#CEE0F9',
|
||||||
|
140: '#E4EEFB',
|
||||||
|
150: '#F0F6FD',
|
||||||
|
160: '#F8FAFF',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const originmainLightTheme = createLightTheme(cobaltBrand);
|
||||||
|
export const originmainDarkTheme = createDarkTheme(cobaltBrand);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Generated
+4731
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Supabase local development configuration
|
||||||
|
# See https://supabase.com/docs/guides/cli/config
|
||||||
|
|
||||||
|
[api]
|
||||||
|
enabled = true
|
||||||
|
port = 54321
|
||||||
|
schemas = ["public", "graphql_public"]
|
||||||
|
extra_search_path = ["public", "extensions"]
|
||||||
|
|
||||||
|
[db]
|
||||||
|
port = 54322
|
||||||
|
shadow_port = 54320
|
||||||
|
major_version = 15
|
||||||
|
|
||||||
|
[studio]
|
||||||
|
enabled = true
|
||||||
|
port = 54323
|
||||||
|
api_url = "http://127.0.0.1"
|
||||||
|
|
||||||
|
[ingest]
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[storage]
|
||||||
|
enabled = true
|
||||||
|
file_size_limit = "50MiB"
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
enabled = true
|
||||||
|
site_url = "http://127.0.0.1:3000"
|
||||||
|
additional_redirect_urls = ["https://127.0.0.1:3000"]
|
||||||
|
jwt_expiry = 3600
|
||||||
|
enable_refresh_token_rotation = true
|
||||||
|
refresh_token_reuse_interval = 10
|
||||||
|
enable_signup = true
|
||||||
|
|
||||||
|
[auth.email]
|
||||||
|
enable_signup = true
|
||||||
|
double_confirm_changes = true
|
||||||
|
enable_confirmations = false
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"strict": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"paths": {
|
||||||
|
"@originmain/ui": ["./packages/ui/src/index.ts"],
|
||||||
|
"@originmain/diff-engine": ["./packages/diff-engine/src/index.ts"],
|
||||||
|
"@originmain/origin-graph": ["./packages/origin-graph/src/index.ts"],
|
||||||
|
"@originmain/agent-bridge": ["./packages/agent-bridge/src/index.ts"],
|
||||||
|
"@originmain/ai-layer": ["./packages/ai-layer/src/index.ts"],
|
||||||
|
"@originmain/renderer": ["./packages/renderer/src/index.ts"],
|
||||||
|
"@originmain/integrations": ["./packages/integrations/src/index.ts"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"framework": "nextjs",
|
||||||
|
"buildCommand": "pnpm --filter @originmain/app run build",
|
||||||
|
"outputDirectory": "packages/app/.next",
|
||||||
|
"installCommand": "pnpm install --frozen-lockfile"
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
projects: [
|
||||||
|
'packages/diff-engine',
|
||||||
|
'packages/origin-graph',
|
||||||
|
'packages/ai-layer',
|
||||||
|
'packages/agent-bridge',
|
||||||
|
],
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
thresholds: {
|
||||||
|
// diff-engine and origin-graph have higher gates per the spec
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user