Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .builder/rules/deploy-app.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Deploy the app to production using Netlify or Vercel to make the site live
globs:
alwaysApply: true
---

Deploying the app to production is powered by either Netlify or Vercel through MCP (Model Context Protocol) integrations:

**For Netlify:**

- If the Netlify tools are not available, tell the user to [Connect Netlify MCP](#open-mcp-popover).
- If the user asks to deploy the app, use the appropriate Netlify MCP tools to deploy the app.
- Note: Netlify builds the source code on their servers, so while it's good to check if `npm run build` works locally, it's not required.
- Do not ask user to your any command in the terminal, use the Bash tool when needed.

**For Vercel:**

- If the Vercel tools are not available, tell the user to [Connect Vercel MCP](#open-mcp-popover).
- Vercel deployment should work automatically once connected - simply use the appropriate Vercel MCP tools.
- Do not ask user to your any command in the terminal, use the Bash tool when needed.

You can also suggest sharing the [Open Preview](#open-preview), but explain that it won't be a production-ready link.
9 changes: 9 additions & 0 deletions .builder/rules/organize-ui.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
description: Keep component and files small
globs:
alwaysApply: true
---

As development goes, break down complex UI into smaller components, avoiding very nested JSX trees.
Also, even within a single page (route), feel free to create auxiliary extra smaller files to make the page more maintenable.
Breaking big components into multiple files will help down the road when the user asks for changes or refactors.
62 changes: 62 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
node_modules
dist
.git
.gitignore
README.md
.env
.env.local
.env.development
.env.test
.env.production
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.DS_Store
.vscode
.idea
*.swp
*.swo
*~
.cursor
coverage
.nyc_output
.eslintcache
*.log
*.lock
*.tmp
*.tmp.*
log.txt

.DS_Store
node_modules
**/node_modules/**
build
data
.env
load-ids.txt

server
tmp
types
.git
.gitignore
dist
service
tests
fixtures-pages
fixtures-apps

# Netlify
.netlify
packages/ml-air/lib
packages/ml-air/bin
packages/ml-air/project
packages/ml-air/share
packages/ml-air/random_forest_classification/
packages/ml-air/__pycache__/
packages/ml-air/app/__pycache__/
packages/vcp-common/native-bridge/build
packages/vcp-common/_tests_/dataset-ranking.csv
node_modules/
Dockerfile
.gitignore
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.config/
!.env
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"trailingComma": "all"
}
164 changes: 164 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Fusion Starter

A production-ready full-stack React application template with integrated Express server, featuring React Router 6 SPA mode, TypeScript, Vitest, Zod and modern tooling.

While the starter comes with a express server, only create endpoint when strictly neccesary, for example to encapsulate logic that must leave in the server, such as private keys handling, or certain DB operations, db...

## Tech Stack

- **PNPM**: Prefer pnpm
- **Frontend**: React 18 + React Router 6 (spa) + TypeScript + Vite + TailwindCSS 3
- **Backend**: Express server integrated with Vite dev server
- **Testing**: Vitest
- **UI**: Radix UI + TailwindCSS 3 + Lucide React icons

## Project Structure

```
client/ # React SPA frontend
├── pages/ # Route components (Index.tsx = home)
├── components/ui/ # Pre-built UI component library
├── App.tsx # App entry point and with SPA routing setup
└── global.css # TailwindCSS 3 theming and global styles

server/ # Express API backend
├── index.ts # Main server setup (express config + routes)
└── routes/ # API handlers

shared/ # Types used by both client & server
└── api.ts # Example of how to share api interfaces
```

## Key Features

## SPA Routing System

The routing system is powered by React Router 6:

- `client/pages/Index.tsx` represents the home page.
- Routes are defined in `client/App.tsx` using the `react-router-dom` import
- Route files are located in the `client/pages/` directory

For example, routes can be defined with:

```typescript
import { BrowserRouter, Routes, Route } from "react-router-dom";

<Routes>
<Route path="/" element={<Index />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>;
```

### Styling System

- **Primary**: TailwindCSS 3 utility classes
- **Theme and design tokens**: Configure in `client/global.css`
- **UI components**: Pre-built library in `client/components/ui/`
- **Utility**: `cn()` function combines `clsx` + `tailwind-merge` for conditional classes

```typescript
// cn utility usage
className={cn(
"base-classes",
{ "conditional-class": condition },
props.className // User overrides
)}
```

### Express Server Integration

- **Development**: Single port (8080) for both frontend/backend
- **Hot reload**: Both client and server code
- **API endpoints**: Prefixed with `/api/`

#### Example API Routes
- `GET /api/ping` - Simple ping api
- `GET /api/demo` - Demo endpoint

### Shared Types
Import consistent types in both client and server:
```typescript
import { DemoResponse } from '@shared/api';
```

Path aliases:
- `@shared/*` - Shared folder
- `@/*` - Client folder

## Development Commands

```bash
pnpm dev # Start dev server (client + server)
pnpm build # Production build
pnpm start # Start production server
pnpm typecheck # TypeScript validation
pnpm test # Run Vitest tests
```

## Adding Features

### Add new colors to the theme

Open `client/global.css` and `tailwind.config.ts` and add new tailwind colors.

### New API Route
1. **Optional**: Create a shared interface in `shared/api.ts`:
```typescript
export interface MyRouteResponse {
message: string;
// Add other response properties here
}
```

2. Create a new route handler in `server/routes/my-route.ts`:
```typescript
import { RequestHandler } from "express";
import { MyRouteResponse } from "@shared/api"; // Optional: for type safety

export const handleMyRoute: RequestHandler = (req, res) => {
const response: MyRouteResponse = {
message: 'Hello from my endpoint!'
};
res.json(response);
};
```

3. Register the route in `server/index.ts`:
```typescript
import { handleMyRoute } from "./routes/my-route";

// Add to the createServer function:
app.get("/api/my-endpoint", handleMyRoute);
```

4. Use in React components with type safety:
```typescript
import { MyRouteResponse } from '@shared/api'; // Optional: for type safety

const response = await fetch('/api/my-endpoint');
const data: MyRouteResponse = await response.json();
```

### New Page Route
1. Create component in `client/pages/MyPage.tsx`
2. Add route in `client/App.tsx`:
```typescript
<Route path="/my-page" element={<MyPage />} />
```

## Production Deployment

- **Standard**: `pnpm build`
- **Binary**: Self-contained executables (Linux, macOS, Windows)
- **Cloud Deployment**: Use either Netlify or Vercel via their MCP integrations for easy deployment. Both providers work well with this starter template.

## Architecture Notes

- Single-port development with Vite + Express integration
- TypeScript throughout (client, server, shared)
- Full hot reload for rapid development
- Production-ready with multiple deployment options
- Comprehensive UI component library included
- Type-safe API communication via shared interfaces
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

5 changes: 4 additions & 1 deletion src/App.tsx → client/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import "./global.css";

import { Toaster } from "@/components/ui/toaster";
import { createRoot } from "react-dom/client";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
Expand All @@ -24,4 +27,4 @@ const App = () => (
</QueryClientProvider>
);

export default App;
createRoot(document.getElementById("root")!).render(<App />);
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDown } from "lucide-react"
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDown } from "lucide-react";

import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";

const Accordion = AccordionPrimitive.Root
const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
Expand All @@ -15,8 +15,8 @@ const AccordionItem = React.forwardRef<
className={cn("border-b", className)}
{...props}
/>
))
AccordionItem.displayName = "AccordionItem"
));
AccordionItem.displayName = "AccordionItem";

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
Expand All @@ -27,16 +27,16 @@ const AccordionTrigger = React.forwardRef<
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className
className,
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
Expand All @@ -49,8 +49,8 @@ const AccordionContent = React.forwardRef<
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
))
));

AccordionContent.displayName = AccordionPrimitive.Content.displayName
AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
Loading