commit c8ed262197365085518a6c4c832ef2c408cadc91 Author: agent Date: Fri Mar 13 23:21:55 2026 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12bc7fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# compiled output +dist +tmp +out-tsc +*.tsbuildinfo +.expo +.expo-shared + +# dependencies +node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +.cursor/rules/nx-rules.mdc +.github/instructions/nx.instructions.md + +# Replit +.cache/ +.local/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..61e34c2 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +auto-install-peers=false +strict-peer-dependencies=false diff --git a/.replit b/.replit new file mode 100644 index 0000000..8dc8fdd --- /dev/null +++ b/.replit @@ -0,0 +1,26 @@ +modules = ["nodejs-24"] + +[[artifacts]] +id = "artifacts/api-server" + +[[artifacts]] +id = "artifacts/mockup-sandbox" + +[deployment] +router = "application" +deploymentTarget = "autoscale" + +[deployment.postBuild] +args = ["pnpm", "store", "prune"] +env = { "CI" = "true" } + +[workflows] +runButton = "Project" + +[agent] +stack = "PNPM_WORKSPACE" +expertMode = true + +[postMerge] +path = "scripts/post-merge.sh" +timeoutMs = 20000 diff --git a/.replitignore b/.replitignore new file mode 100644 index 0000000..9eb019c --- /dev/null +++ b/.replitignore @@ -0,0 +1,5 @@ +# The format of this file is identical to `.dockerignore`. +# It is used to reduce the size of deployed images to make the process of publishing faster. + +# No need to store the pnpm store twice. +.local diff --git a/artifacts/api-server/.replit-artifact/artifact.toml b/artifacts/api-server/.replit-artifact/artifact.toml new file mode 100644 index 0000000..cfa04bf --- /dev/null +++ b/artifacts/api-server/.replit-artifact/artifact.toml @@ -0,0 +1,25 @@ +kind = "api" +previewPath = "/api" # TODO - should be excluded from preview in the first place +title = "API Server" +version = "1.0.0" +id = "3B4_FFSkEVBkAeYMFRJ2e" + +[[services]] +localPort = 8080 +name = "API Server" +paths = ["/api"] + +[services.development] +run = "pnpm --filter @workspace/api-server run dev" + +[services.production] +build = "pnpm --filter @workspace/api-server run build" + +[services.production.run] +args = ["node", "artifacts/api-server/dist/index.cjs"] + +[services.production.run.env] +PORT = "8080" + +[services.production.health.startup] +path = "/api/healthz" diff --git a/artifacts/api-server/build.ts b/artifacts/api-server/build.ts new file mode 100644 index 0000000..e5f6896 --- /dev/null +++ b/artifacts/api-server/build.ts @@ -0,0 +1,75 @@ +import path from "path"; +import { fileURLToPath } from "url"; +import { build as esbuild } from "esbuild"; +import { rm, readFile } from "fs/promises"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// server deps to bundle to reduce openat(2) syscalls +// which helps cold start times without risking some +// packages that are not bundle compatible +const allowlist = [ + "@google/generative-ai", + "axios", + "connect-pg-simple", + "cors", + "date-fns", + "drizzle-orm", + "drizzle-zod", + "express", + "express-rate-limit", + "express-session", + "jsonwebtoken", + "memorystore", + "multer", + "nanoid", + "nodemailer", + "openai", + "passport", + "passport-local", + "pg", + "stripe", + "uuid", + "ws", + "xlsx", + "zod", + "zod-validation-error", +]; + +async function buildAll() { + const distDir = path.resolve(__dirname, "dist"); + await rm(distDir, { recursive: true, force: true }); + + console.log("building server..."); + const pkgPath = path.resolve(__dirname, "package.json"); + const pkg = JSON.parse(await readFile(pkgPath, "utf-8")); + const allDeps = [ + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.devDependencies || {}), + ]; + const externals = allDeps.filter( + (dep) => + !allowlist.includes(dep) && + !(pkg.dependencies?.[dep]?.startsWith("workspace:")), + ); + + await esbuild({ + entryPoints: [path.resolve(__dirname, "src/index.ts")], + platform: "node", + bundle: true, + format: "cjs", + outfile: path.resolve(distDir, "index.cjs"), + define: { + "process.env.NODE_ENV": '"production"', + }, + minify: true, + external: externals, + logLevel: "info", + }); +} + +buildAll().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/artifacts/api-server/package.json b/artifacts/api-server/package.json new file mode 100644 index 0000000..5e5e90d --- /dev/null +++ b/artifacts/api-server/package.json @@ -0,0 +1,27 @@ +{ + "name": "@workspace/api-server", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "NODE_ENV=development tsx ./src/index.ts", + "build": "tsx ./build.ts", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@workspace/db": "workspace:*", + "@workspace/api-zod": "workspace:*", + "drizzle-orm": "catalog:", + "express": "^5", + "cookie-parser": "^1.4.7", + "cors": "^2" + }, + "devDependencies": { + "@types/node": "catalog:", + "@types/express": "^5.0.6", + "@types/cors": "^2.8.19", + "@types/cookie-parser": "^1.4.10", + "esbuild": "^0.27.3", + "tsx": "catalog:" + } +} diff --git a/artifacts/api-server/src/app.ts b/artifacts/api-server/src/app.ts new file mode 100644 index 0000000..a88b36c --- /dev/null +++ b/artifacts/api-server/src/app.ts @@ -0,0 +1,13 @@ +import express, { type Express } from "express"; +import cors from "cors"; +import router from "./routes"; + +const app: Express = express(); + +app.use(cors()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +app.use("/api", router); + +export default app; diff --git a/artifacts/api-server/src/index.ts b/artifacts/api-server/src/index.ts new file mode 100644 index 0000000..f7394d1 --- /dev/null +++ b/artifacts/api-server/src/index.ts @@ -0,0 +1,19 @@ +import app from "./app"; + +const rawPort = process.env["PORT"]; + +if (!rawPort) { + throw new Error( + "PORT environment variable is required but was not provided.", + ); +} + +const port = Number(rawPort); + +if (Number.isNaN(port) || port <= 0) { + throw new Error(`Invalid PORT value: "${rawPort}"`); +} + +app.listen(port, () => { + console.log(`Server listening on port ${port}`); +}); diff --git a/artifacts/api-server/src/lib/.gitkeep b/artifacts/api-server/src/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/artifacts/api-server/src/middlewares/.gitkeep b/artifacts/api-server/src/middlewares/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/artifacts/api-server/src/routes/health.ts b/artifacts/api-server/src/routes/health.ts new file mode 100644 index 0000000..c0a1446 --- /dev/null +++ b/artifacts/api-server/src/routes/health.ts @@ -0,0 +1,11 @@ +import { Router, type IRouter } from "express"; +import { HealthCheckResponse } from "@workspace/api-zod"; + +const router: IRouter = Router(); + +router.get("/healthz", (_req, res) => { + const data = HealthCheckResponse.parse({ status: "ok" }); + res.json(data); +}); + +export default router; diff --git a/artifacts/api-server/src/routes/index.ts b/artifacts/api-server/src/routes/index.ts new file mode 100644 index 0000000..5a1f77a --- /dev/null +++ b/artifacts/api-server/src/routes/index.ts @@ -0,0 +1,8 @@ +import { Router, type IRouter } from "express"; +import healthRouter from "./health"; + +const router: IRouter = Router(); + +router.use(healthRouter); + +export default router; diff --git a/artifacts/api-server/tsconfig.json b/artifacts/api-server/tsconfig.json new file mode 100644 index 0000000..b60e718 --- /dev/null +++ b/artifacts/api-server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src"], + "references": [ + { + "path": "../../lib/db" + }, + { + "path": "../../lib/api-zod" + } + ] +} diff --git a/artifacts/mockup-sandbox/.replit-artifact/artifact.toml b/artifacts/mockup-sandbox/.replit-artifact/artifact.toml new file mode 100644 index 0000000..4e9156e --- /dev/null +++ b/artifacts/mockup-sandbox/.replit-artifact/artifact.toml @@ -0,0 +1,17 @@ +kind = "design" +previewPath = "/__mockup" +title = "Component Preview Server" +version = "1.0.0" +id = "XegfDyZt7HqfW2Bb8Ghoy" + +[[services]] +localPort = 8081 +name = "Component Preview Server" +paths = ["/__mockup"] + +[services.env] +PORT = "8081" +BASE_PATH = "/__mockup" + +[services.development] +run = "pnpm --filter @workspace/mockup-sandbox run dev" diff --git a/artifacts/mockup-sandbox/components.json b/artifacts/mockup-sandbox/components.json new file mode 100644 index 0000000..ba0c18c --- /dev/null +++ b/artifacts/mockup-sandbox/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/artifacts/mockup-sandbox/index.html b/artifacts/mockup-sandbox/index.html new file mode 100644 index 0000000..1397a63 --- /dev/null +++ b/artifacts/mockup-sandbox/index.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + Mockup Canvas + + + + + + + + +
+ + + diff --git a/artifacts/mockup-sandbox/mockupPreviewPlugin.ts b/artifacts/mockup-sandbox/mockupPreviewPlugin.ts new file mode 100644 index 0000000..c09ed92 --- /dev/null +++ b/artifacts/mockup-sandbox/mockupPreviewPlugin.ts @@ -0,0 +1,180 @@ +import { mkdirSync, writeFileSync } from "fs"; +import path from "path"; +import glob from "fast-glob"; +import chokidar from "chokidar"; +import type { FSWatcher } from "chokidar"; +import type { Plugin } from "vite"; + +const MOCKUPS_DIR = "src/components/mockups"; +const GENERATED_MODULE = "src/.generated/mockup-components.ts"; + +interface DiscoveredComponent { + globKey: string; + importPath: string; +} + +export function mockupPreviewPlugin(): Plugin { + let root = ""; + let currentSource = ""; + let watcher: FSWatcher | null = null; + + function getMockupsAbsDir(): string { + return path.join(root, MOCKUPS_DIR); + } + + function getGeneratedModuleAbsPath(): string { + return path.join(root, GENERATED_MODULE); + } + + function isMockupFile(absolutePath: string): boolean { + const rel = path.relative(getMockupsAbsDir(), absolutePath); + return ( + !rel.startsWith("..") && !path.isAbsolute(rel) && rel.endsWith(".tsx") + ); + } + + function isPreviewTarget(relativeToMockups: string): boolean { + return relativeToMockups + .split(path.sep) + .every((segment) => !segment.startsWith("_")); + } + + async function discoverComponents(): Promise> { + const files = await glob(`${MOCKUPS_DIR}/**/*.tsx`, { + cwd: root, + ignore: ["**/_*/**", "**/_*.tsx"], + }); + + return files.map((f) => ({ + globKey: "./" + f.slice("src/".length), + importPath: path.posix.relative("src/.generated", f), + })); + } + + function generateSource(components: Array): string { + const entries = components + .map( + (c) => + ` ${JSON.stringify(c.globKey)}: () => import(${JSON.stringify(c.importPath)})`, + ) + .join(",\n"); + + return [ + "// This file is auto-generated by mockupPreviewPlugin.ts.", + "type ModuleMap = Record Promise>>;", + "export const modules: ModuleMap = {", + entries, + "};", + "", + ].join("\n"); + } + + function shouldAutoRescan(pathname: string): boolean { + return ( + pathname.includes("/components/mockups/") || + pathname.includes("/.generated/mockup-components") + ); + } + + let refreshInFlight = false; + let refreshQueued = false; + + async function refresh(): Promise { + if (refreshInFlight) { + refreshQueued = true; + return false; + } + + refreshInFlight = true; + let changed = false; + try { + const components = await discoverComponents(); + const newSource = generateSource(components); + if (newSource !== currentSource) { + currentSource = newSource; + const generatedModuleAbsPath = getGeneratedModuleAbsPath(); + mkdirSync(path.dirname(generatedModuleAbsPath), { recursive: true }); + writeFileSync(generatedModuleAbsPath, currentSource); + changed = true; + } + } finally { + refreshInFlight = false; + } + + if (refreshQueued) { + refreshQueued = false; + const followUp = await refresh(); + return changed || followUp; + } + + return changed; + } + + async function onFileAddedOrRemoved(): Promise { + await refresh(); + } + + return { + name: "mockup-preview", + enforce: "pre", + + configResolved(config) { + root = config.root; + }, + + async buildStart() { + await refresh(); + }, + + async configureServer(viteServer) { + await refresh(); + + const mockupsAbsDir = getMockupsAbsDir(); + mkdirSync(mockupsAbsDir, { recursive: true }); + + watcher = chokidar.watch(mockupsAbsDir, { + ignoreInitial: true, + awaitWriteFinish: { + stabilityThreshold: 100, + pollInterval: 50, + }, + }); + + watcher.on("add", (file) => { + if ( + isMockupFile(file) && + isPreviewTarget(path.relative(mockupsAbsDir, file)) + ) { + void onFileAddedOrRemoved(); + } + }); + + watcher.on("unlink", (file) => { + if (isMockupFile(file)) { + void onFileAddedOrRemoved(); + } + }); + + viteServer.middlewares.use((req, res, next) => { + const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1"); + const pathname = requestUrl.pathname; + const originalEnd = res.end.bind(res); + + res.end = ((...args: Parameters) => { + if (res.statusCode === 404 && shouldAutoRescan(pathname)) { + void refresh(); + } + return originalEnd(...args); + }) as typeof res.end; + + next(); + }); + }, + + async closeWatcher() { + if (watcher) { + await watcher.close(); + } + }, + }; +} diff --git a/artifacts/mockup-sandbox/package.json b/artifacts/mockup-sandbox/package.json new file mode 100644 index 0000000..20e2806 --- /dev/null +++ b/artifacts/mockup-sandbox/package.json @@ -0,0 +1,74 @@ +{ + "name": "@workspace/mockup-sandbox", + "version": "2.0.0", + "type": "module", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "devDependencies": { + "@hookform/resolvers": "^3.10.0", + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-aspect-ratio": "^1.1.8", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-context-menu": "^2.2.16", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slider": "^1.3.6", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toast": "^1.2.7", + "@radix-ui/react-toggle": "^1.1.10", + "@radix-ui/react-toggle-group": "^1.1.11", + "@radix-ui/react-tooltip": "^1.2.8", + "@replit/vite-plugin-cartographer": "catalog:", + "@replit/vite-plugin-runtime-error-modal": "catalog:", + "@tailwindcss/vite": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@vitejs/plugin-react": "catalog:", + "chokidar": "^4.0.3", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "^1.1.1", + "date-fns": "^3.6.0", + "embla-carousel-react": "^8.6.0", + "fast-glob": "^3.3.3", + "framer-motion": "catalog:", + "input-otp": "^1.4.2", + "lucide-react": "catalog:", + "next-themes": "^0.4.6", + "react": "catalog:", + "react-day-picker": "^9.11.1", + "react-dom": "catalog:", + "react-hook-form": "^7.66.0", + "react-resizable-panels": "^2.1.9", + "recharts": "^2.15.4", + "sonner": "^2.0.7", + "tailwind-merge": "catalog:", + "tailwindcss": "catalog:", + "tailwindcss-animate": "^1.0.7", + "tw-animate-css": "^1.4.0", + "vaul": "^1.1.2", + "vite": "catalog:", + "zod": "catalog:" + } +} diff --git a/artifacts/mockup-sandbox/src/.generated/mockup-components.ts b/artifacts/mockup-sandbox/src/.generated/mockup-components.ts new file mode 100644 index 0000000..97c87e1 --- /dev/null +++ b/artifacts/mockup-sandbox/src/.generated/mockup-components.ts @@ -0,0 +1,5 @@ +// This file is auto-generated by mockupPreviewPlugin.ts. +type ModuleMap = Record Promise>>; +export const modules: ModuleMap = { + +}; diff --git a/artifacts/mockup-sandbox/src/App.tsx b/artifacts/mockup-sandbox/src/App.tsx new file mode 100644 index 0000000..d9c2317 --- /dev/null +++ b/artifacts/mockup-sandbox/src/App.tsx @@ -0,0 +1,146 @@ +import { useEffect, useState, type ComponentType } from "react"; + +import { modules as discoveredModules } from "./.generated/mockup-components"; + +type ModuleMap = Record Promise>>; + +function _resolveComponent( + mod: Record, + name: string, +): ComponentType | undefined { + const fns = Object.values(mod).filter( + (v) => typeof v === "function", + ) as ComponentType[]; + return ( + (mod.default as ComponentType) || + (mod.Preview as ComponentType) || + (mod[name] as ComponentType) || + fns[fns.length - 1] + ); +} + +function PreviewRenderer({ + componentPath, + modules, +}: { + componentPath: string; + modules: ModuleMap; +}) { + const [Component, setComponent] = useState(null); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + + setComponent(null); + setError(null); + + async function loadComponent(): Promise { + const key = `./components/mockups/${componentPath}.tsx`; + const loader = modules[key]; + if (!loader) { + setError(`No component found at ${componentPath}.tsx`); + return; + } + + try { + const mod = await loader(); + if (cancelled) { + return; + } + const name = componentPath.split("/").pop()!; + const comp = _resolveComponent(mod, name); + if (!comp) { + setError( + `No exported React component found in ${componentPath}.tsx\n\nMake sure the file has at least one exported function component.`, + ); + return; + } + setComponent(() => comp); + } catch (e) { + if (cancelled) { + return; + } + + const message = e instanceof Error ? e.message : String(e); + setError(`Failed to load preview.\n${message}`); + } + } + + void loadComponent(); + + return () => { + cancelled = true; + }; + }, [componentPath, modules]); + + if (error) { + return ( +
+        {error}
+      
+ ); + } + + if (!Component) return null; + + return ; +} + +function getBasePath(): string { + return import.meta.env.BASE_URL.replace(/\/$/, ""); +} + +function getPreviewExamplePath(): string { + const basePath = getBasePath(); + return `${basePath}/preview/ComponentName`; +} + +function Gallery() { + return ( +
+
+

+ Component Preview Server +

+

+ This server renders individual components for the workspace canvas. +

+

+ Access component previews at{" "} + + {getPreviewExamplePath()} + +

+
+
+ ); +} + +function getPreviewPath(): string | null { + const basePath = getBasePath(); + const { pathname } = window.location; + const local = + basePath && pathname.startsWith(basePath) + ? pathname.slice(basePath.length) || "/" + : pathname; + const match = local.match(/^\/preview\/(.+)$/); + return match ? match[1] : null; +} + +function App() { + const previewPath = getPreviewPath(); + + if (previewPath) { + return ( + + ); + } + + return ; +} + +export default App; diff --git a/artifacts/mockup-sandbox/src/components/ui/accordion.tsx b/artifacts/mockup-sandbox/src/components/ui/accordion.tsx new file mode 100644 index 0000000..e1797c9 --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/accordion.tsx @@ -0,0 +1,55 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/artifacts/mockup-sandbox/src/components/ui/alert-dialog.tsx b/artifacts/mockup-sandbox/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..fa2b442 --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/artifacts/mockup-sandbox/src/components/ui/alert.tsx b/artifacts/mockup-sandbox/src/components/ui/alert.tsx new file mode 100644 index 0000000..5afd41d --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/artifacts/mockup-sandbox/src/components/ui/aspect-ratio.tsx b/artifacts/mockup-sandbox/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..c4abbf3 --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,5 @@ +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/artifacts/mockup-sandbox/src/components/ui/avatar.tsx b/artifacts/mockup-sandbox/src/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/artifacts/mockup-sandbox/src/components/ui/badge.tsx b/artifacts/mockup-sandbox/src/components/ui/badge.tsx new file mode 100644 index 0000000..cfd176a --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/badge.tsx @@ -0,0 +1,37 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "whitespace-nowrap inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" + + " hover-elevate ", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow-xs", + secondary: + "border-transparent bg-secondary text-secondary-foreground", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow-xs", + outline: "text-foreground border [border-color:var(--badge-outline)]", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/artifacts/mockup-sandbox/src/components/ui/breadcrumb.tsx b/artifacts/mockup-sandbox/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/artifacts/mockup-sandbox/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>