diff --git a/.env b/.env
deleted file mode 100644
index 8c71b0e2..00000000
--- a/.env
+++ /dev/null
@@ -1,7 +0,0 @@
-VITE_SUPABASE_PROJECT_ID="vatgianzotsurljznsry"
-VITE_SUPABASE_PUBLISHABLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZhdGdpYW56b3RzdXJsanpuc3J5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjI2OTA2MDksImV4cCI6MjA3ODI2NjYwOX0.7AjzaZjAMcygsMiPbI8w43F00JDU6hlpOWlbejOAZS0"
-VITE_SUPABASE_URL="https://vatgianzotsurljznsry.supabase.co"
-
-# CAD PROCESSING SERVICE (Local Dev)
-VITE_CAD_SERVICE_URL="http://localhost:8888"
-VITE_CAD_SERVICE_API_KEY="eryxon_dev_key_12345"
diff --git a/.env.example b/.env.example
index 6102d551..51f68e1d 100644
--- a/.env.example
+++ b/.env.example
@@ -28,30 +28,9 @@ VITE_SUPABASE_PROJECT_ID="your-project-id"
# =============================================================================
# CAD PROCESSING SERVICE (Optional)
# =============================================================================
-# Server-side CAD processing for geometry and PMI extraction
-# See services/pmi-extractor/README.md for deployment instructions
# CAD service URL (leave empty to use browser-based processing)
# VITE_CAD_SERVICE_URL="https://your-cad-service.example.com"
-# API key for CAD service authentication (optional if service allows anonymous)
+# API key for CAD service authentication
# VITE_CAD_SERVICE_API_KEY="your-api-key-here"
-
-# Legacy PMI service URL (falls back to VITE_CAD_SERVICE_URL if not set)
-# VITE_PMI_SERVICE_URL="https://your-pmi-service.example.com"
-
-# =============================================================================
-# SELF-HOSTED NOTES
-# =============================================================================
-#
-# For self-hosted deployments:
-# 1. Create a Supabase project (cloud or self-hosted)
-# 2. Apply the database schema from supabase/migrations/
-# 3. Deploy edge functions: supabase functions deploy
-# 4. Configure storage buckets: parts-images, issues
-# 5. Set these environment variables
-#
-# See docs/SELF_HOSTING_GUIDE.md for complete instructions.
-#
-# License: BSL 1.1 - Self-hosting is free and unlimited.
-# You cannot offer commercial hosted versions that compete with eryxon.eu
diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml
new file mode 100644
index 00000000..1c11f78a
--- /dev/null
+++ b/.github/workflows/deploy-cloudflare.yml
@@ -0,0 +1,44 @@
+name: Deploy to Cloudflare Pages
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+ workflow_dispatch:
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ deployments: write
+ name: Deploy to Cloudflare Pages
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build
+ run: npm run build
+ env:
+ VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
+ VITE_SUPABASE_PUBLISHABLE_KEY: ${{ secrets.VITE_SUPABASE_PUBLISHABLE_KEY }}
+
+ - name: Deploy to Cloudflare Pages
+ uses: cloudflare/pages-action@v1
+ with:
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ projectName: eryxon-flow
+ directory: dist
+ gitHubToken: ${{ secrets.GITHUB_TOKEN }}
+ wranglerVersion: '3'
diff --git a/.gitignore b/.gitignore
index b451f134..c9dcc7dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,32 @@ __pycache__/
.Python
*.egg-info/
.eggs/
+
+# === Security: Sensitive Files ===
+
+# Supabase configuration (contains project ID)
+supabase/config.toml
+supabase/.temp/
+
+# Environment files (contains credentials)
+.env
+.env.local
+.env.*.local
+# Keep example files
+!.env.example
+
+# Secrets and credentials
+*.pem
+*.key
+*.p12
+secrets/
+credentials/
+
+# IDE settings (may contain sensitive paths)
+.vscode/settings.json
+.idea/workspace.xml
+
+# Backup files
+*.bak
+*.backup
+*~
diff --git a/DEPLOY.md b/DEPLOY.md
new file mode 100644
index 00000000..74162ee6
--- /dev/null
+++ b/DEPLOY.md
@@ -0,0 +1,53 @@
+# Deploy Eryxon Flow
+
+## Prerequisites
+
+- Supabase account
+- Cloudflare account (free tier)
+
+## Step 1: Create Supabase Project
+
+```bash
+# 1. Go to supabase.com → Create project
+# 2. Get credentials from Settings → API:
+# - Project URL
+# - anon key
+# - Project Ref
+
+# 3. Apply schema
+supabase link --project-ref YOUR_REF
+supabase db push
+
+# 4. Create storage
+supabase storage create parts-images
+supabase storage create issues
+
+# 5. Deploy functions
+supabase functions deploy
+```
+
+## Step 2: Deploy to Cloudflare Pages
+
+```bash
+# 1. Go to dash.cloudflare.com
+# 2. Pages → Create → Connect Git
+# 3. Select repo
+# 4. Build: npm run build
+# 5. Output: dist
+```
+
+**Environment Variables** (set in Cloudflare):
+```
+VITE_SUPABASE_URL = https://YOUR_REF.supabase.co
+VITE_SUPABASE_PUBLISHABLE_KEY = your-anon-key
+VITE_SUPABASE_PROJECT_ID = YOUR_REF
+```
+
+## Step 3: Custom Domain
+
+In Cloudflare Pages:
+- Custom domains → Add domain
+- Enter: app.eryxon.eu
+- DNS: CNAME app → eryxon-flow.pages.dev
+
+Done.
diff --git a/README.md b/README.md
index ea661d46..53557ef5 100644
--- a/README.md
+++ b/README.md
@@ -1,209 +1,82 @@
-# Eryxon MES
+# Eryxon Flow
-**The simple, elegant and powerful manufacturing execution system that your people will love to use. Made for SMB metal fabrication.**
+Open source Manufacturing Execution System (MES) for job shops and make-to-order manufacturers.
-
+## Features
-[](LICENSE)
-[](https://reactjs.org/)
-[](https://www.typescriptlang.org/)
-[](https://vite.dev/)
-[](https://supabase.com/)
+- Job and part tracking with real-time status updates
+- Production planning and scheduling
+- Multi-tenant SaaS architecture with row-level security
+- Analytics and reporting (OEE, QRM, quality metrics)
+- REST API with webhooks for ERP integration
+- Shipping and logistics management
+- Multi-language support (English, Dutch, German)
-
+## Quick Deploy
----
+See **[DEPLOY.md](DEPLOY.md)** for complete deployment instructions.
-## About This Project
+## Prerequisites
-Eryxon MES is built by [Sheet Metal Connect e.U.](https://www.sheetmetalconnect.com/), founded by Luke van Enkhuizen, for digital transformation of SMB metals companies.
+- [Supabase](https://supabase.com) account
+- [Cloudflare](https://cloudflare.com) account
+- Node.js 20+
-This is a starting point. Each shop is unique - fork it, customize it, make it yours. Sheet Metal Connect e.U. can help you self-host and adapt it to your specific needs.
-
-**Recommended:** Self-host with your own Supabase instance or Docker.
-
----
-
-## What Makes This Different
-
-- **MCP Server** - AI/automation ready out of the box
-- **API-first** - Send data from any system
-- **Webhooks** - Link to any other system
-- **Event-driven, real-time** - Industry 4.0 ready
-- **Modern UI** - Operators actually want to use it
-
-It's opinionated. Built for sheet metal manufacturing. Not for everyone.
-
-## ✨ Key Features
-
-- **Production Management** - Job tracking, parts routing, operation assignments, and issue tracking
-- **QRM Capacity Management** - WIP limits, capacity warnings, and bottleneck prevention
-- **Operator Terminal** - Real-time production interface with time tracking and 3D CAD viewer
-- **Admin Dashboard** - Live production metrics, job wizard, and activity monitoring
-- **Multi-tenant SaaS** - Complete tenant isolation with row-level security
-- **REST API & Webhooks** - Full integration capabilities with external systems
-- **MCP Server** - AI-powered automation via Model Context Protocol
-- **Multi-language** - English, Dutch, German with dark mode support
-
-## 🚀 Quick Start
+## Local Development
```bash
-# Install dependencies
-npm install
+git clone https://github.com/SheetMetalConnect/eryxon-flow.git
+cd eryxon-flow
-# Set up environment variables
cp .env.example .env
# Edit .env with your Supabase credentials
-# Start development server
+npm install
npm run dev
```
-Visit `http://localhost:8080` to access the application.
-
-## 📚 Documentation
-
-Comprehensive documentation is available in the [`/docs`](./docs) folder:
-
-- **[HOW-THE-APP-WORKS.md](docs/HOW-THE-APP-WORKS.md)** - Complete functional guide
-- **[API_DOCUMENTATION.md](docs/API_DOCUMENTATION.md)** - REST API reference
-- **[DESIGN_SYSTEM.md](docs/DESIGN_SYSTEM.md)** - Design tokens and styling
-- **[EDGE_FUNCTIONS_SETUP.md](docs/EDGE_FUNCTIONS_SETUP.md)** - Edge Functions guide
-- **[CICD_DEPLOYMENT_PLAN.md](docs/CICD_DEPLOYMENT_PLAN.md)** - CI/CD pipeline and Docker deployment
-- **[CLAUDE.md](CLAUDE.md)** - AI assistant guide for contributors
-
-Additional documentation:
-- [3D Viewer](docs/3d-viewer.md)
-- [Notifications System](docs/NOTIFICATIONS_SYSTEM.md)
-- [Data Export](docs/DATA_EXPORT_FEATURE.md)
-- [Integrations Marketplace](docs/INTEGRATIONS_MARKETPLACE.md)
-- [MCP Server Setup](mcp-server/README.md)
-
-## 🏗️ Tech Stack
-
-- **Frontend**: React 18, TypeScript 5.8, Vite 7, TailwindCSS 3
-- **UI**: shadcn/ui (54+ components), Material-UI, Lucide icons
-- **State**: React Query, React Context
-- **Backend**: Supabase (PostgreSQL, Realtime, Edge Functions, Storage)
-- **Forms**: react-hook-form, Zod validation
-- **3D**: Three.js for STEP file viewing
-- **Charts**: Recharts
-- **i18n**: i18next with en/nl/de support
-
-## 📁 Project Structure
+## Environment Variables
```
-├── src/
-│ ├── components/ # UI components (admin, operator, terminal, qrm, etc.)
-│ ├── pages/ # Route pages (admin, operator, common)
-│ ├── hooks/ # Custom React hooks
-│ ├── lib/ # Utility libraries
-│ └── integrations/ # Supabase client
-├── supabase/
-│ ├── functions/ # 23 Edge Functions
-│ └── migrations/ # Database schema
-├── mcp-server/ # Model Context Protocol server
-└── docs/ # Documentation
+VITE_SUPABASE_URL
+VITE_SUPABASE_PUBLISHABLE_KEY
+VITE_SUPABASE_PROJECT_ID
```
-## 🔒 Security
-
-- **Multi-Tenancy**: PostgreSQL Row-Level Security for complete data isolation
-- **Authentication**: Supabase Auth with JWT tokens
-- **API Security**: Bearer token auth with bcrypt-hashed keys
-- **Webhooks**: HMAC-SHA256 signatures for verification
-
-## Getting Started
+See `.env.example` for complete list.
-| | Hosted Demo | Self-Hosted (Recommended) |
-|---|---|---|
-| **Where** | Our infrastructure | Your infrastructure |
-| **Usage** | Limited | Unlimited |
-| **API** | Limited | Full |
-| **Webhooks** | Limited | Full |
-| **MCP Server** | Limited | Full |
-| **Support** | Docs only | Community + Consulting |
+## Architecture
-- **Hosted Demo** — Try it online, limited usage for evaluation and educational purposes
-- **Self-Hosted** — Full features, unlimited usage, bring your own Supabase or Docker
+**Frontend**: React + TypeScript + Vite
+**UI**: shadcn/ui + Tailwind CSS
+**Backend**: Supabase (PostgreSQL + Edge Functions)
+**Deployment**: Cloudflare Pages
+**Database**: 85 migrations, multi-tenant schema
+**API**: 28 Edge Functions
-**Recommended:** Self-host with your own Supabase instance. See the [Self-Hosting Guide](docs/SELF_HOSTING_GUIDE.md) for database setup, migrations, and deployment.
+## Documentation
-Need help setting up or customizing? [Contact Sheet Metal Connect e.U.](mailto:office@sheetmetalconnect.com)
-
-## Deployment
-
-### Self-Hosted
-
-```bash
-# Clone and configure
-git clone https://github.com/SheetMetalConnect/eryxon-flow.git
-cd eryxon-flow
-cp .env.example .env
-# Edit .env with your Supabase credentials
-
-# Run with Docker
-docker-compose up -d
-```
-
-See **[docs/SELF_HOSTING_GUIDE.md](docs/SELF_HOSTING_GUIDE.md)** for complete setup instructions.
-
-### Docker Quick Start
-
-```bash
-docker pull ghcr.io/sheetmetalconnect/eryxon-flow:latest
-docker run -p 8080:80 \
- -e VITE_SUPABASE_URL=your-url \
- -e VITE_SUPABASE_PUBLISHABLE_KEY=your-key \
- ghcr.io/sheetmetalconnect/eryxon-flow:latest
-```
+- [DEPLOY.md](DEPLOY.md) - Deployment guide
+- [docs/API_DOCUMENTATION.md](docs/API_DOCUMENTATION.md) - API reference
+- [docs/SELF_HOSTING_GUIDE.md](docs/SELF_HOSTING_GUIDE.md) - Self-hosting
+- [docs/DATABASE.md](docs/DATABASE.md) - Database schema
+- [docs/](docs/) - Complete documentation
## License
-**Business Source License 1.1 (BSL 1.1)** - Source Available
+**Business Source License 1.1**
-This is an **open source repository** under the BSL 1.1 license, which allows source code access while preventing competitive SaaS offerings.
-
-**TL;DR:** Use it, modify it, self-host it - all free. Just don't host it and charge others for access.
-
-- ✅ Self-host for your own manufacturing operations - free, unlimited
-- ✅ Fork it, modify it, make it yours - each shop is unique
-- ✅ Use for internal business, development, testing, education
-- ❌ Cannot host it and sell access as a SaaS to others
-- 🔄 Converts to Apache 2.0 after 4 years
+- Free to use for your own manufacturing business
+- Source available for modification and improvement
+- Self-host unlimited instances
+- Cannot offer as competing hosted service
See [LICENSE](LICENSE) for full terms.
-### External Components (Feature Flags)
-
-Some features require external services that must be deployed separately:
-
-| Feature | Service | Description | Feature Flag |
-|---------|---------|-------------|--------------|
-| Advanced CAD (PMI/MBD) | `services/eryxon3d` | Server-side CAD processing with PMI extraction | `advancedCAD` |
-
-These external components are:
-- **Disabled by default** - must be explicitly enabled via feature flags in Organization Settings
-- **Self-hosted** - you deploy and control the service
-- **Optional** - core MES functionality works without them
-
-To enable an external feature:
-1. Deploy the required service (see `services/` directory)
-2. Configure environment variables (see `.env.example`)
-3. Enable the feature flag in Admin → Settings → Organization Settings
-
----
-
-## Contributing & Support
-
-- **Website**: [sheetmetalconnect.com](https://www.sheetmetalconnect.com/)
-- **Issues & PRs**: [GitHub](https://github.com/SheetMetalConnect/eryxon-flow)
-- **Consulting & Custom Setup**: [office@sheetmetalconnect.com](mailto:office@sheetmetalconnect.com)
-
-No guarantees of continued development, but likely will be updated with latest features.
-
----
+**Change Date**: 2029-01-01 (converts to Apache 2.0)
-Copyright © 2025 Sheet Metal Connect e.U.
+## Support
-**Built with** React + TypeScript + Supabase | **Region**: EU (Netherlands)
+- Documentation: [docs/](docs/)
+- Issues: [GitHub Issues](https://github.com/SheetMetalConnect/eryxon-flow/issues)
+- Commercial support: office@sheetmetalconnect.com
diff --git a/public/_headers b/public/_headers
new file mode 100644
index 00000000..77b1104d
--- /dev/null
+++ b/public/_headers
@@ -0,0 +1,15 @@
+# Security headers
+/*
+ X-Frame-Options: DENY
+ X-Content-Type-Options: nosniff
+ X-XSS-Protection: 1; mode=block
+ Referrer-Policy: strict-origin-when-cross-origin
+ Permissions-Policy: camera=(), microphone=(), geolocation=()
+
+# Cache static assets
+/assets/*
+ Cache-Control: public, max-age=31536000, immutable
+
+# Don't cache index.html
+/index.html
+ Cache-Control: no-cache, no-store, must-revalidate
diff --git a/public/_redirects b/public/_redirects
new file mode 100644
index 00000000..1c8b7267
--- /dev/null
+++ b/public/_redirects
@@ -0,0 +1,2 @@
+# Cloudflare Pages - SPA routing
+/* /index.html 200
diff --git a/scripts/security/clean-git-history.sh b/scripts/security/clean-git-history.sh
new file mode 100755
index 00000000..0a967fdf
--- /dev/null
+++ b/scripts/security/clean-git-history.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# Clean .env and config.toml from git history
+# WARNING: This rewrites git history!
+
+set -e
+
+echo "🧹 Git History Cleanup"
+echo "====================="
+echo ""
+echo "⚠️ WARNING: This will rewrite git history!"
+echo ""
+echo "This removes from ALL commits:"
+echo " - .env"
+echo " - supabase/config.toml"
+echo ""
+echo "After running:"
+echo " - All contributors must re-clone"
+echo " - Force push required"
+echo " - Cannot be undone easily"
+echo ""
+read -p "Are you SURE? (type 'yes' to continue): " CONFIRM
+
+if [ "$CONFIRM" != "yes" ]; then
+ echo "Aborted."
+ exit 1
+fi
+
+echo ""
+echo "Checking for git-filter-repo..."
+
+if ! command -v git-filter-repo &> /dev/null; then
+ echo "❌ git-filter-repo not found"
+ echo ""
+ echo "Install it:"
+ echo " pip install git-filter-repo"
+ echo ""
+ echo "Or on macOS:"
+ echo " brew install git-filter-repo"
+ echo ""
+ exit 1
+fi
+
+echo "✓ git-filter-repo found"
+echo ""
+
+# Backup current branch
+CURRENT_BRANCH=$(git branch --show-current)
+echo "Current branch: $CURRENT_BRANCH"
+echo ""
+
+# Create backup tag
+BACKUP_TAG="backup-before-history-clean-$(date +%Y%m%d-%H%M%S)"
+echo "Creating backup tag: $BACKUP_TAG"
+git tag "$BACKUP_TAG"
+echo "✓ Backup tag created"
+echo ""
+
+# Remove .env from history
+echo "Removing .env from git history..."
+git filter-repo --invert-paths --path .env --force
+echo "✓ .env removed from history"
+echo ""
+
+# Remove config.toml from history
+echo "Removing supabase/config.toml from git history..."
+git filter-repo --invert-paths --path supabase/config.toml --force
+echo "✓ supabase/config.toml removed from history"
+echo ""
+
+echo "✅ Git history cleaned!"
+echo ""
+echo "Next steps:"
+echo ""
+echo "1. Verify the cleanup:"
+echo " git log --all --full-history -- .env"
+echo " (should show nothing)"
+echo ""
+echo "2. Force push to remote:"
+echo " git push --force --all"
+echo " git push --force --tags"
+echo ""
+echo "3. Notify all contributors to re-clone:"
+echo " git clone "
+echo ""
+echo "4. If something went wrong, restore from backup:"
+echo " git reset --hard $BACKUP_TAG"
+echo ""
diff --git a/src/integrations/supabase/client.ts b/src/integrations/supabase/client.ts
index 534b6a18..b017ed7e 100644
--- a/src/integrations/supabase/client.ts
+++ b/src/integrations/supabase/client.ts
@@ -2,8 +2,12 @@
import { createClient } from '@supabase/supabase-js';
import type { Database } from './types';
-const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL || "https://vatgianzotsurljznsry.supabase.co";
-const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZhdGdpYW56b3RzdXJsanpuc3J5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjI2OTA2MDksImV4cCI6MjA3ODI2NjYwOX0.7AjzaZjAMcygsMiPbI8w43F00JDU6hlpOWlbejOAZS0";
+const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
+const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
+
+if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) {
+ throw new Error('Missing VITE_SUPABASE_URL or VITE_SUPABASE_PUBLISHABLE_KEY environment variables. Please check your .env file.');
+}
// Import the supabase client like this:
// import { supabase } from "@/integrations/supabase/client";
diff --git a/src/lib/upload-with-progress.ts b/src/lib/upload-with-progress.ts
index fb848900..4aa36429 100644
--- a/src/lib/upload-with-progress.ts
+++ b/src/lib/upload-with-progress.ts
@@ -34,9 +34,13 @@ export async function uploadFileWithProgress(
const { onProgress, signal } = options;
try {
- // Use the actual Supabase project URL and anon key
- const supabaseUrl = 'https://vatgianzotsurljznsry.supabase.co';
- const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZhdGdpYW56b3RzdXJsanpuc3J5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjI2OTA2MDksImV4cCI6MjA3ODI2NjYwOX0.7AjzaZjAMcygsMiPbI8w43F00JDU6hlpOWlbejOAZS0';
+ // Get Supabase configuration from environment variables
+ const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
+ const supabaseKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
+
+ if (!supabaseUrl || !supabaseKey) {
+ throw new Error('Missing VITE_SUPABASE_URL or VITE_SUPABASE_PUBLISHABLE_KEY environment variables');
+ }
// Get the current session for auth
const { data: { session } } = await supabase.auth.getSession();
diff --git a/supabase/config.toml b/supabase/config.toml
deleted file mode 100644
index f8f53aa7..00000000
--- a/supabase/config.toml
+++ /dev/null
@@ -1 +0,0 @@
-project_id = "vatgianzotsurljznsry"
\ No newline at end of file
diff --git a/wrangler.toml b/wrangler.toml
new file mode 100644
index 00000000..a492f839
--- /dev/null
+++ b/wrangler.toml
@@ -0,0 +1,20 @@
+name = "eryxon-flow"
+compatibility_date = "2024-01-01"
+
+[build]
+command = "npm run build"
+
+[build.upload]
+format = "service-worker"
+main = "./dist"
+
+[site]
+bucket = "./dist"
+
+[env.production]
+name = "eryxon-flow"
+route = ""
+vars = { NODE_VERSION = "20" }
+
+[env.preview]
+name = "eryxon-flow-preview"