Commit
Initial public release — VibeQuant v2.1.0
AI-powered financial intelligence platform: Claude AI agent (213 tools), 265+ FMP financial data endpoints, quantitative Python engine (Monte Carlo, GARCH, VaR, Black-Scholes, portfolio optimization), multi-source web research, SSE streaming chat, PDF/slides export, Electron desktop app. Live demo: https://www.vquant.ai Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
added
.editorconfig
+29 −0
@@ -0,0 +1,29 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 3 | +# ----------------------------------------------------------------------------- | |
| 4 | +# File: .editorconfig | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Contact: contact@spboucher.ai | |
| 8 | +# Website: https://www.spboucher.ai | |
| 9 | +# Demo: https://www.vquant.ai | |
| 10 | +# License: MIT (see LICENSE) | |
| 11 | +# | |
| 12 | +# Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 13 | +# ============================================================================= | |
| 14 | + | |
| 15 | +root = true | |
| 16 | + | |
| 17 | +[*] | |
| 18 | +indent_style = space | |
| 19 | +indent_size = 2 | |
| 20 | +end_of_line = lf | |
| 21 | +charset = utf-8 | |
| 22 | +trim_trailing_whitespace = true | |
| 23 | +insert_final_newline = true | |
| 24 | + | |
| 25 | +[*.md] | |
| 26 | +trim_trailing_whitespace = false | |
| 27 | + | |
| 28 | +[*.py] | |
| 29 | +indent_size = 4 | |
added
.env.example
+35 −0
@@ -0,0 +1,35 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 3 | +# ----------------------------------------------------------------------------- | |
| 4 | +# File: .env.example | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Contact: contact@spboucher.ai | |
| 8 | +# Website: https://www.spboucher.ai | |
| 9 | +# Demo: https://www.vquant.ai | |
| 10 | +# License: MIT (see LICENSE) | |
| 11 | +# | |
| 12 | +# Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 13 | +# ============================================================================= | |
| 14 | + | |
| 15 | +# ─── Required ────────────────────────────────────────── | |
| 16 | +ANTHROPIC_API_KEY=sk-ant-api03-your-key-here | |
| 17 | +FMP_API_KEY=your-fmp-key-here | |
| 18 | + | |
| 19 | +# ─── Recommended ─────────────────────────────────────── | |
| 20 | +FIRECRAWL_API_KEY=fc-your-key-here | |
| 21 | +TAVILY_API_KEY=tvly-your-key-here | |
| 22 | + | |
| 23 | +# ─── Optional ────────────────────────────────────────── | |
| 24 | +EXA_API_KEY=your-exa-key-here | |
| 25 | +SERPAPI_API_KEY=your-serpapi-key-here | |
| 26 | +ELEVENLABS_API_KEY=your-elevenlabs-key-here | |
| 27 | + | |
| 28 | +# ─── Server Config ───────────────────────────────────── | |
| 29 | +DATABASE_URL=sqlite://local.db | |
| 30 | +SESSION_SECRET=generate-a-strong-random-secret-here | |
| 31 | +PORT=5000 | |
| 32 | +NODE_ENV=development | |
| 33 | + | |
| 34 | +# ─── Python (optional, auto-detected) ───────────────── | |
| 35 | +# PYTHON_PATH=/path/to/.venv/bin/python | |
added
.github/workflows/ci.yml
+59 −0
@@ -0,0 +1,59 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 3 | +# ----------------------------------------------------------------------------- | |
| 4 | +# File: .github/workflows/ci.yml | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Contact: contact@spboucher.ai | |
| 8 | +# Website: https://www.spboucher.ai | |
| 9 | +# Demo: https://www.vquant.ai | |
| 10 | +# License: MIT (see LICENSE) | |
| 11 | +# | |
| 12 | +# Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 13 | +# ============================================================================= | |
| 14 | + | |
| 15 | +name: CI | |
| 16 | + | |
| 17 | +on: | |
| 18 | + push: | |
| 19 | + branches: [main] | |
| 20 | + pull_request: | |
| 21 | + branches: [main] | |
| 22 | + | |
| 23 | +jobs: | |
| 24 | + ci: | |
| 25 | + name: Lint, Typecheck, Test & Build | |
| 26 | + runs-on: ubuntu-latest | |
| 27 | + | |
| 28 | + strategy: | |
| 29 | + matrix: | |
| 30 | + node-version: [20, 22] | |
| 31 | + | |
| 32 | + steps: | |
| 33 | + - name: Checkout | |
| 34 | + uses: actions/checkout@v4 | |
| 35 | + | |
| 36 | + - name: Setup Node.js ${{ matrix.node-version }} | |
| 37 | + uses: actions/setup-node@v4 | |
| 38 | + with: | |
| 39 | + node-version: ${{ matrix.node-version }} | |
| 40 | + cache: npm | |
| 41 | + | |
| 42 | + - name: Install dependencies | |
| 43 | + run: npm ci --legacy-peer-deps | |
| 44 | + | |
| 45 | + - name: Lint | |
| 46 | + run: npm run lint | |
| 47 | + continue-on-error: true # 572 pre-existing warnings, fixing incrementally | |
| 48 | + | |
| 49 | + - name: Typecheck | |
| 50 | + run: npm run typecheck | |
| 51 | + continue-on-error: true # 15 pre-existing errors, fixing incrementally | |
| 52 | + | |
| 53 | + - name: Test | |
| 54 | + run: npm test | |
| 55 | + | |
| 56 | + - name: Build | |
| 57 | + run: npm run build | |
| 58 | + env: | |
| 59 | + NODE_ENV: production | |
added
.gitignore
+64 −0
@@ -0,0 +1,64 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 3 | +# ----------------------------------------------------------------------------- | |
| 4 | +# File: .gitignore | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Contact: contact@spboucher.ai | |
| 8 | +# Website: https://www.spboucher.ai | |
| 9 | +# Demo: https://www.vquant.ai | |
| 10 | +# License: MIT (see LICENSE) | |
| 11 | +# | |
| 12 | +# Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 13 | +# ============================================================================= | |
| 14 | + | |
| 15 | +# Dependencies | |
| 16 | +node_modules/ | |
| 17 | +.venv/ | |
| 18 | + | |
| 19 | +# Build output | |
| 20 | +dist/ | |
| 21 | + | |
| 22 | +# Environment variables | |
| 23 | +.env | |
| 24 | +.env.local | |
| 25 | +.env.*.local | |
| 26 | + | |
| 27 | +# Database | |
| 28 | +local.db | |
| 29 | +local.db-shm | |
| 30 | +local.db-wal | |
| 31 | + | |
| 32 | +# Logs | |
| 33 | +logs/ | |
| 34 | + | |
| 35 | +# Temp & caches | |
| 36 | +temp/ | |
| 37 | +__pycache__/ | |
| 38 | +*.pyc | |
| 39 | + | |
| 40 | +# Backups | |
| 41 | +backups/ | |
| 42 | + | |
| 43 | +# Generated images | |
| 44 | +/*.png | |
| 45 | +public/plots/ | |
| 46 | +public/figures/ | |
| 47 | + | |
| 48 | +# OS files | |
| 49 | +.DS_Store | |
| 50 | +Thumbs.db | |
| 51 | + | |
| 52 | +# IDE | |
| 53 | +.vscode/ | |
| 54 | +.idea/ | |
| 55 | +*.swp | |
| 56 | +*.swo | |
| 57 | +*~ | |
| 58 | + | |
| 59 | +# Lock files (keep package-lock.json, ignore uv.lock) | |
| 60 | +uv.lock | |
| 61 | + | |
| 62 | +# Misc artifacts | |
| 63 | +downloads/ | |
| 64 | +attached_assets/ | |
added
.prettierignore
+20 −0
@@ -0,0 +1,20 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 3 | +# ----------------------------------------------------------------------------- | |
| 4 | +# File: .prettierignore | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Contact: contact@spboucher.ai | |
| 8 | +# Website: https://www.spboucher.ai | |
| 9 | +# Demo: https://www.vquant.ai | |
| 10 | +# License: MIT (see LICENSE) | |
| 11 | +# | |
| 12 | +# Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 13 | +# ============================================================================= | |
| 14 | + | |
| 15 | +node_modules/ | |
| 16 | +dist/ | |
| 17 | +.venv/ | |
| 18 | +package-lock.json | |
| 19 | +*.py | |
| 20 | +*.md | |
added
.prettierrc
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +{ | |
| 2 | + "semi": true, | |
| 3 | + "singleQuote": false, | |
| 4 | + "trailingComma": "all", | |
| 5 | + "printWidth": 100, | |
| 6 | + "tabWidth": 2, | |
| 7 | + "arrowParens": "always", | |
| 8 | + "endOfLine": "lf" | |
| 9 | +} | |
added
LICENSE
+21 −0
@@ -0,0 +1,21 @@ | ||
| 1 | +MIT License | |
| 2 | + | |
| 3 | +Copyright (c) 2026 Simon-Pierre Boucher | |
| 4 | + | |
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | +of this software and associated documentation files (the "Software"), to deal | |
| 7 | +in the Software without restriction, including without limitation the rights | |
| 8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | +copies of the Software, and to permit persons to whom the Software is | |
| 10 | +furnished to do so, subject to the following conditions: | |
| 11 | + | |
| 12 | +The above copyright notice and this permission notice shall be included in all | |
| 13 | +copies or substantial portions of the Software. | |
| 14 | + | |
| 15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | +SOFTWARE. | |
added
README.md
+689 −0
@@ -0,0 +1,689 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================================= | |
| 3 | + VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + ----------------------------------------------------------------------------- | |
| 5 | + File: README.md | |
| 6 | + | |
| 7 | + Author: Simon-Pierre Boucher | |
| 8 | + Contact: contact@spboucher.ai | |
| 9 | + Website: https://www.spboucher.ai | |
| 10 | + Demo: https://www.vquant.ai | |
| 11 | + License: MIT (see LICENSE) | |
| 12 | + | |
| 13 | + Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + ============================================================================= | |
| 15 | +--> | |
| 16 | + | |
| 17 | +<div align="center"> | |
| 18 | + | |
| 19 | +# VibeQuant | |
| 20 | + | |
| 21 | +### AI-Powered Institutional-Grade Financial Intelligence Platform | |
| 22 | + | |
| 23 | +<br/> | |
| 24 | + | |
| 25 | +**🌐 Live Demo: [www.vquant.ai](https://www.vquant.ai)** | |
| 26 | + | |
| 27 | +[](https://www.vquant.ai) | |
| 28 | +[](./LICENSE) | |
| 29 | +[](./) | |
| 30 | + | |
| 31 | +<br/> | |
| 32 | + | |
| 33 | +**Stack** | |
| 34 | + | |
| 35 | +[](https://www.typescriptlang.org/) | |
| 36 | +[](https://react.dev/) | |
| 37 | +[](https://vitejs.dev/) | |
| 38 | +[](https://expressjs.com/) | |
| 39 | +[](https://nodejs.org/) | |
| 40 | +[](https://tailwindcss.com/) | |
| 41 | + | |
| 42 | +**AI & Data** | |
| 43 | + | |
| 44 | +[-d97706?style=flat-square&logo=anthropic&logoColor=white)](https://anthropic.com/) | |
| 45 | +[](https://python.org/) | |
| 46 | +[](https://numpy.org/) | |
| 47 | +[](https://pandas.pydata.org/) | |
| 48 | +[](https://scipy.org/) | |
| 49 | +[](https://scikit-learn.org/) | |
| 50 | + | |
| 51 | +**Database & ORM** | |
| 52 | + | |
| 53 | +[](https://sqlite.org/) | |
| 54 | +[](https://postgresql.org/) | |
| 55 | +[](https://orm.drizzle.team/) | |
| 56 | +[](https://zod.dev/) | |
| 57 | + | |
| 58 | +**Quality & Testing** | |
| 59 | + | |
| 60 | +[](https://eslint.org/) | |
| 61 | +[](https://prettier.io/) | |
| 62 | +[](https://vitest.dev/) | |
| 63 | +[](https://testing-library.com/) | |
| 64 | + | |
| 65 | +**Metrics** | |
| 66 | + | |
| 67 | +[](./) | |
| 68 | +[](./) | |
| 69 | +[](./) | |
| 70 | +[](./) | |
| 71 | +[](./) | |
| 72 | +[](./) | |
| 73 | +[](./) | |
| 74 | +[](./) | |
| 75 | +[](./) | |
| 76 | +[](./) | |
| 77 | + | |
| 78 | +--- | |
| 79 | + | |
| 80 | +[**Getting Started**](#-getting-started) • [**Features**](#-features) • [**Architecture**](#-architecture) • [**API Reference**](#-api-reference) • [**Contributing**](#-contributing) | |
| 81 | + | |
| 82 | +</div> | |
| 83 | + | |
| 84 | +--- | |
| 85 | + | |
| 86 | +## Overview | |
| 87 | + | |
| 88 | +VibeQuant is a production-grade full-stack financial analysis platform that combines **Claude AI (Fable 5 default, 6 selectable models, up to 1M token context)** with **265+ financial data endpoints**, **quantitative Python analysis** (Monte Carlo, GARCH, VaR, Black-Scholes, portfolio optimization), and **multi-source web research** (Tavily, Firecrawl, Exa, SerpAPI) to deliver institutional-grade insights through a real-time streaming conversational interface. | |
| 89 | + | |
| 90 | +Built with a modern TypeScript stack (React 18 + Vite + Express + Drizzle ORM), validated with Zod schemas, tested with Vitest, and linted with ESLint + Prettier. | |
| 91 | + | |
| 92 | +--- | |
| 93 | + | |
| 94 | +## Features | |
| 95 | + | |
| 96 | +### Financial Data Engine | |
| 97 | + | |
| 98 | +[](https://financialmodelingprep.com/) | |
| 99 | + | |
| 100 | +| Category | Endpoints | Details | | |
| 101 | +|----------|-----------|---------| | |
| 102 | +| **Company Fundamentals** | 40+ | Profiles, income statements, balance sheets, cash flows, key metrics, financial ratios, growth rates | | |
| 103 | +| **Market Data** | 30+ | Real-time quotes, historical prices, intraday data, batch quotes, market hours, gainers/losers/actives | | |
| 104 | +| **Technical Analysis** | 15+ | RSI, MACD, EMA, SMA, ADX, Williams %R, CCI, Stochastic, WMA, DEMA, TEMA, Bollinger Bands | | |
| 105 | +| **Analyst & Ratings** | 20+ | Estimates, price targets, upgrades/downgrades, consensus, historical grades, ratings snapshots | | |
| 106 | +| **Insider & Institutional** | 15+ | Insider trading, institutional holders, 13F filings, Senate/House trading, beneficial ownership | | |
| 107 | +| **Economic & Macro** | 15+ | Economic calendar, Treasury rates, economic indicators, market risk premium, COT reports | | |
| 108 | +| **SEC & Compliance** | 25+ | SEC filings (8-K, 10-K, 10-Q), EDGAR search, company notes, earnings transcripts | | |
| 109 | +| **Alternative Data** | 20+ | ESG scores, social sentiment, Congressional trading, crowdfunding, equity offerings | | |
| 110 | +| **ETFs & Indices** | 15+ | ETF holdings, sector weightings, S&P 500/NASDAQ/Dow Jones constituents | | |
| 111 | +| **Valuation** | 10+ | DCF, levered DCF, custom DCF, enterprise values, owner earnings, financial scores | | |
| 112 | +| **Forex & Crypto** | 15+ | Forex pairs, crypto quotes, commodity prices, historical forex data | | |
| 113 | +| **Events** | 10+ | Earnings calendar, IPO calendar, dividend history, stock splits, mergers & acquisitions | | |
| 114 | + | |
| 115 | +### Quantitative Analysis Engine | |
| 116 | + | |
| 117 | +[](https://python.org/) | |
| 118 | + | |
| 119 | +| Model | Description | Libraries | | |
| 120 | +|-------|-------------|-----------| | |
| 121 | +| **Monte Carlo Simulation** | Price path simulation with customizable parameters (10k+ paths, configurable horizon) | `numpy`, `scipy` | | |
| 122 | +| **Options Pricing** | Black-Scholes model with full Greeks (delta, gamma, theta, vega, rho) | `scipy.stats` | | |
| 123 | +| **GARCH Volatility** | Volatility modeling and forecasting with GARCH(1,1) | `arch`, `statsmodels` | | |
| 124 | +| **Value at Risk (VaR)** | Historical, parametric, and Monte Carlo VaR at multiple confidence levels | `numpy`, `scipy` | | |
| 125 | +| **Portfolio Optimization** | Mean-variance optimization (Modern Portfolio Theory), efficient frontier | `cvxpy`, `scipy.optimize` | | |
| 126 | +| **Risk Metrics** | Sharpe, Sortino, Calmar, max drawdown, beta, alpha, information ratio | `numpy`, `pandas` | | |
| 127 | +| **Custom Python** | Arbitrary Python code execution with access to 17 libraries and FMP data | All libraries | | |
| 128 | + | |
| 129 | +<details> | |
| 130 | +<summary><b>Full Python library list (17 packages)</b></summary> | |
| 131 | + | |
| 132 | +| Library | Version | Purpose | | |
| 133 | +|---------|---------|---------| | |
| 134 | +| `numpy` | latest | Numerical computing | | |
| 135 | +| `pandas` | latest | Data manipulation | | |
| 136 | +| `scipy` | latest | Scientific computing | | |
| 137 | +| `scikit-learn` | latest | Machine learning | | |
| 138 | +| `statsmodels` | latest | Statistical models | | |
| 139 | +| `matplotlib` | latest | Plotting & charts | | |
| 140 | +| `seaborn` | latest | Statistical visualization | | |
| 141 | +| `plotly` | latest | Interactive charts | | |
| 142 | +| `arch` | latest | GARCH models | | |
| 143 | +| `cvxpy` | latest | Convex optimization | | |
| 144 | +| `yfinance` | latest | Market data | | |
| 145 | +| `ta` | latest | Technical analysis | | |
| 146 | +| `quantstats` | latest | Portfolio analytics | | |
| 147 | +| `beautifulsoup4` | latest | Web scraping | | |
| 148 | +| `requests` | latest | HTTP client | | |
| 149 | +| `lxml` | latest | XML/HTML parsing | | |
| 150 | +| `Pillow` | latest | Image processing | | |
| 151 | + | |
| 152 | +</details> | |
| 153 | + | |
| 154 | +### Web Research Engine | |
| 155 | + | |
| 156 | +| Source | Capabilities | Badge | | |
| 157 | +|--------|-------------|-------| | |
| 158 | +| **Tavily** | AI-powered search with generated answers, time filters, domain filtering | [](https://tavily.com/) | | |
| 159 | +| **Firecrawl** | Web scraping, PDF extraction (up to 750 pages), intelligent crawling, structured extraction, autonomous agents | [](https://firecrawl.dev/) | | |
| 160 | +| **Exa** | Semantic neural search, content retrieval, similar page discovery | [](https://exa.ai/) | | |
| 161 | +| **SerpAPI** | Google Search, Images, Shopping, Finance, Trends, Scholar, Videos, Flights, Hotels | [](https://serpapi.com/) | | |
| 162 | + | |
| 163 | +### AI Agent | |
| 164 | + | |
| 165 | +[](https://anthropic.com/) | |
| 166 | + | |
| 167 | +| Feature | Details | | |
| 168 | +|---------|---------| | |
| 169 | +| **Models** | 6 selectable Claude models — **Fable 5** (default), **Opus 4.8 / 4.7 / 4.6**, **Sonnet 4.6**, **Haiku 4.5** — up to 1M token context | | |
| 170 | +| **Model Selector** | Switchable from the input bar and the welcome screen (per-model icons); selection persisted in `localStorage` | | |
| 171 | +| **Reasoning** | Adaptive extended thinking shown in a collapsible "Raisonnement" widget that auto-expands while the model thinks (Fable 5 / Opus / Sonnet; Haiku has no adaptive thinking) | | |
| 172 | +| **Tools** | 213 specialized financial tools | | |
| 173 | +| **Batching** | Intelligent tool batching (5 tools per batch with 500ms pauses) | | |
| 174 | +| **Streaming** | Real-time Server-Sent Events (SSE) for instant response display | | |
| 175 | +| **History** | Persistent conversation sessions with token tracking | | |
| 176 | +| **Vision** | Image analysis support (charts, screenshots, documents) | | |
| 177 | +| **Cost Tracking** | Per-session input/output token counting and cost estimation | | |
| 178 | + | |
| 179 | +### Interface & Design | |
| 180 | + | |
| 181 | +| Aspect | Details | | |
| 182 | +|--------|---------| | |
| 183 | +| **Theme** | Modern light/dark themes with an indigo-violet accent; toggle persisted in `localStorage` | | |
| 184 | +| **Design** | Rounded soft-cornered components, subtle background gradient, thin rounded scrollbars | | |
| 185 | +| **Welcome screen** | Categorized example prompts + dedicated model selector with per-model icons | | |
| 186 | +| **Stack** | Tailwind CSS 3.4 + Radix UI primitives | | |
| 187 | + | |
| 188 | +### Export & Reports | |
| 189 | + | |
| 190 | +| Format | Description | Badge | | |
| 191 | +|--------|-------------|-------| | |
| 192 | +| **PDF** | Professional reports generated with Puppeteer, markdown rendering | [](./) | | |
| 193 | +| **DOCX** | Word documents via Pandoc conversion | [](./) | | |
| 194 | +| **LaTeX Slides** | Beamer presentations with professional theming | [](./) | | |
| 195 | +| **Data Files** | CSV, XLSX, JSON, TXT downloads of analysis data | [](./) | | |
| 196 | +| **Share Links** | Shareable report URLs with unique IDs | [](./) | | |
| 197 | + | |
| 198 | +--- | |
| 199 | + | |
| 200 | +## Getting Started | |
| 201 | + | |
| 202 | +### Prerequisites | |
| 203 | + | |
| 204 | +[](https://nodejs.org/) | |
| 205 | +[](https://python.org/) | |
| 206 | +[](https://npmjs.com/) | |
| 207 | + | |
| 208 | +### Installation | |
| 209 | + | |
| 210 | +```bash | |
| 211 | +# Clone the repository | |
| 212 | +git clone https://github.com/spboucher-ai/vquant.git | |
| 213 | +cd vquant | |
| 214 | + | |
| 215 | +# Install Node.js dependencies | |
| 216 | +npm install | |
| 217 | + | |
| 218 | +# Set up Python virtual environment | |
| 219 | +python3 -m venv .venv | |
| 220 | +source .venv/bin/activate # macOS/Linux | |
| 221 | +# .venv\Scripts\activate # Windows | |
| 222 | +pip install -r requirements-safe.txt | |
| 223 | + | |
| 224 | +# Configure environment variables | |
| 225 | +cp .env.example .env | |
| 226 | +# Edit .env with your API keys | |
| 227 | + | |
| 228 | +# Start development server | |
| 229 | +npm run dev | |
| 230 | +``` | |
| 231 | + | |
| 232 | +The app will be available at `http://localhost:5000`. | |
| 233 | + | |
| 234 | +### API Keys | |
| 235 | + | |
| 236 | +All services offer generous free tiers: | |
| 237 | + | |
| 238 | +| Service | Sign Up | Free Tier | Required | | |
| 239 | +|---------|---------|-----------|----------| | |
| 240 | +| [](https://console.anthropic.com) | [console.anthropic.com](https://console.anthropic.com) | $5 credit | **Yes** | | |
| 241 | +| [](https://financialmodelingprep.com) | [financialmodelingprep.com](https://financialmodelingprep.com) | 250 calls/day | **Yes** | | |
| 242 | +| [](https://firecrawl.dev) | [firecrawl.dev](https://firecrawl.dev) | 500 credits | Recommended | | |
| 243 | +| [](https://app.tavily.com) | [app.tavily.com](https://app.tavily.com) | 1000 calls/month | Recommended | | |
| 244 | +| [](https://exa.ai) | [exa.ai](https://exa.ai) | 1000 searches/month | Optional | | |
| 245 | +| [](https://serpapi.com) | [serpapi.com](https://serpapi.com) | 100 searches/month | Optional | | |
| 246 | + | |
| 247 | +--- | |
| 248 | + | |
| 249 | +## Scripts | |
| 250 | + | |
| 251 | +| Command | Description | | |
| 252 | +|---------|-------------| | |
| 253 | +| `npm run dev` | Start development server (Vite HMR + Express) | | |
| 254 | +| `npm run build` | Build for production (client + server) | | |
| 255 | +| `npm start` | Start production server | | |
| 256 | +| `npm run typecheck` | TypeScript type checking (`tsc --noEmit`) | | |
| 257 | +| `npm run lint` | ESLint check | | |
| 258 | +| `npm run lint:fix` | ESLint auto-fix | | |
| 259 | +| `npm run format` | Prettier format all files | | |
| 260 | +| `npm run format:check` | Prettier check (CI-friendly) | | |
| 261 | +| `npm test` | Run all tests (Vitest) | | |
| 262 | +| `npm run test:watch` | Run tests in watch mode | | |
| 263 | +| `npm run test:coverage` | Run tests with coverage report | | |
| 264 | +| `npm run db:push` | Push database schema changes (Drizzle Kit) | | |
| 265 | + | |
| 266 | +--- | |
| 267 | + | |
| 268 | +## Architecture | |
| 269 | + | |
| 270 | +``` | |
| 271 | +vquant/ | |
| 272 | +├── client/src/ # Frontend (React + TypeScript) | |
| 273 | +│ ├── App.tsx # Router + providers + ErrorBoundary | |
| 274 | +│ ├── main.tsx # Entry point | |
| 275 | +│ ├── config.ts # App config, endpoints, feature flags | |
| 276 | +│ ├── components/ | |
| 277 | +│ │ ├── chat/ # 13 chat-specific components | |
| 278 | +│ │ │ ├── search-bar.tsx # Query input with image support | |
| 279 | +│ │ │ ├── streaming-answer.tsx # Real-time markdown rendering | |
| 280 | +│ │ │ ├── agent-steps.tsx # Tool execution progress | |
| 281 | +│ │ │ ├── tool-results.tsx # Financial data display | |
| 282 | +│ │ │ ├── monte-carlo-results.tsx # Simulation visualizations | |
| 283 | +│ │ │ ├── share-button.tsx # Report sharing | |
| 284 | +│ │ │ ├── download-buttons.tsx # PDF/DOCX export | |
| 285 | +│ │ │ └── ... # + 6 more components | |
| 286 | +│ │ ├── explore/ # 7 stock explorer components | |
| 287 | +│ │ ├── financial/ # Price charts, options pricing | |
| 288 | +│ │ ├── ui/ # 35 shadcn/ui primitives | |
| 289 | +│ │ └── error-boundary.tsx # Global error handler | |
| 290 | +│ ├── hooks/ | |
| 291 | +│ │ ├── useChatSession.ts # All chat state & business logic | |
| 292 | +│ │ ├── useChatMutation.ts # SSE streaming client | |
| 293 | +│ │ ├── useAnalytics.ts # Heartbeat tracking | |
| 294 | +│ │ ├── use-toast.ts # Toast notifications | |
| 295 | +│ │ └── use-mobile.tsx # Responsive detection | |
| 296 | +│ ├── pages/ # 9 route pages | |
| 297 | +│ │ ├── home.tsx # Main chat interface (255 lines) | |
| 298 | +│ │ ├── explore.tsx # Stock explorer | |
| 299 | +│ │ ├── admin.tsx # Admin dashboard | |
| 300 | +│ │ ├── auth.tsx # Login/register | |
| 301 | +│ │ ├── showcase.tsx # Community reports | |
| 302 | +│ │ ├── shared-report.tsx # Shared report viewer | |
| 303 | +│ │ ├── documentation.tsx # API documentation | |
| 304 | +│ │ └── ... # + privacy, terms, slides, 404 | |
| 305 | +│ ├── lib/ | |
| 306 | +│ │ ├── queryClient.ts # TanStack Query config | |
| 307 | +│ │ └── utils.ts # cn() + helpers | |
| 308 | +│ └── utils/ | |
| 309 | +│ ├── chartCapture.ts # Chart-to-image conversion | |
| 310 | +│ └── pdfGenerator.ts # Client-side PDF generation | |
| 311 | +│ | |
| 312 | +├── server/ # Backend (Express + TypeScript) | |
| 313 | +│ ├── index.ts # Server bootstrap, CORS, sessions | |
| 314 | +│ ├── routes.ts # /api/chat (SSE streaming) + speech-to-text | |
| 315 | +│ ├── routes/ | |
| 316 | +│ │ ├── auth.ts # /api/auth/* (register, login, logout, me) | |
| 317 | +│ │ ├── admin.ts # /api/admin/* (database, CRUD) + requireAdmin | |
| 318 | +│ │ ├── analytics.ts # /api/analytics/* (heartbeat, active users, metrics) | |
| 319 | +│ │ ├── fmp.ts # /api/fmp/* (23 financial data endpoints) | |
| 320 | +│ │ ├── reports.ts # /api/share/*, sessions, PDF/DOCX/slides, download | |
| 321 | +│ │ └── validation.ts # Zod request schemas | |
| 322 | +│ ├── services/ | |
| 323 | +│ │ ├── claude/ | |
| 324 | +│ │ │ ├── toolDefinitions.ts # 213 tool definitions (4,480 lines) | |
| 325 | +│ │ │ └── tokenManagement.ts # Context window management | |
| 326 | +│ │ ├── claudeService.ts # Claude API integration | |
| 327 | +│ │ ├── fmpService.ts # 230 FMP API wrapper functions | |
| 328 | +│ │ ├── python/ | |
| 329 | +│ │ │ ├── monteCarlo.ts # Monte Carlo simulation | |
| 330 | +│ │ │ ├── optionsPricing.ts # Black-Scholes pricing | |
| 331 | +│ │ │ ├── garch.ts # GARCH volatility model | |
| 332 | +│ │ │ ├── var.ts # VaR + portfolio + risk metrics | |
| 333 | +│ │ │ └── shared.ts # Python executor config | |
| 334 | +│ │ ├── *.py # 11 Python scientific services | |
| 335 | +│ │ ├── firecrawlService.ts # Web extraction | |
| 336 | +│ │ ├── tavilyService.ts # AI search | |
| 337 | +│ │ ├── exaService.ts # Semantic search | |
| 338 | +│ │ └── serpapiService.ts # Google search | |
| 339 | +│ ├── db.ts # Drizzle ORM (SQLite + PostgreSQL) | |
| 340 | +│ ├── storage.ts # Typed data access layer | |
| 341 | +│ ├── types/ | |
| 342 | +│ │ └── modules.d.ts # Module declarations | |
| 343 | +│ └── utils/ | |
| 344 | +│ └── logger.ts # Structured logging | |
| 345 | +│ | |
| 346 | +├── shared/ # Shared between client & server | |
| 347 | +│ ├── schema.ts # PostgreSQL schema (Drizzle) | |
| 348 | +│ ├── schema-sqlite.ts # SQLite schema (Drizzle) | |
| 349 | +│ └── types.ts # API interfaces (SearchResult, etc.) | |
| 350 | +│ | |
| 351 | +├── vitest.config.ts # Test configuration | |
| 352 | +├── eslint.config.js # ESLint flat config | |
| 353 | +├── vite.config.ts # Vite build config | |
| 354 | +├── tsconfig.json # TypeScript config | |
| 355 | +├── tailwind.config.ts # Tailwind CSS config | |
| 356 | +├── .prettierrc # Prettier config | |
| 357 | +├── .editorconfig # Editor config | |
| 358 | +├── .env.example # Environment template | |
| 359 | +└── .gitignore # Git ignore rules | |
| 360 | +``` | |
| 361 | + | |
| 362 | +### System Architecture | |
| 363 | + | |
| 364 | +``` | |
| 365 | +┌──────────────────────────────────────────────────────────────────┐ | |
| 366 | +│ CLIENT (React 18) │ | |
| 367 | +│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ ┌───────────┐ │ | |
| 368 | +│ │ Chat UI │ │ Explore Page │ │ Admin Panel│ │ Auth/Docs │ │ | |
| 369 | +│ │ (SSE) │ │ (FMP data) │ │ (Analytics)│ │ (Sessions)│ │ | |
| 370 | +│ └────┬─────┘ └──────┬───────┘ └─────┬──────┘ └─────┬─────┘ │ | |
| 371 | +│ │ │ │ │ │ | |
| 372 | +│ ┌────┴───────────────┴────────────────┴───────────────┴─────┐ │ | |
| 373 | +│ │ TanStack Query + useChatSession │ │ | |
| 374 | +│ └───────────────────────────┬───────────────────────────────┘ │ | |
| 375 | +└──────────────────────────────┼──────────────────────────────────┘ | |
| 376 | + │ HTTP / SSE | |
| 377 | +┌──────────────────────────────┼──────────────────────────────────┐ | |
| 378 | +│ SERVER (Express 4) │ | |
| 379 | +│ ┌───────────────────────────┴───────────────────────────────┐ │ | |
| 380 | +│ │ Route Modules (6) │ │ | |
| 381 | +│ │ auth │ admin │ analytics │ fmp │ reports │ chat (SSE) │ │ | |
| 382 | +│ └───┬───────┬──────────┬──────────┬──────────┬──────────────┘ │ | |
| 383 | +│ │ │ │ │ │ │ | |
| 384 | +│ ┌───┴───┐ ┌─┴──────┐ ┌┴────────┐ │ ┌─────┴──────────────┐ │ | |
| 385 | +│ │Storage│ │Analytics│ │Zod Valid│ │ │ Claude Opus 4.8 │ │ | |
| 386 | +│ │(Drizzle)│ │Metrics │ │Schemas │ │ │ 213 tools │ │ | |
| 387 | +│ └───┬───┘ └────────┘ └────────┘ │ │ 5-tool batching │ │ | |
| 388 | +│ │ │ └──────────┬──────────┘ │ | |
| 389 | +│ ┌───┴──────┐ ┌─────┴──────┐ ┌─────┴──────────┐ │ | |
| 390 | +│ │SQLite/PG │ │ FMP Service│ │ Python Engine │ │ | |
| 391 | +│ │(Drizzle) │ │ 230 funcs │ │ 17 libraries │ │ | |
| 392 | +│ └──────────┘ └────────────┘ └────────────────┘ │ | |
| 393 | +│ │ | |
| 394 | +│ ┌────────────────────────────────────────────────────────────┐ │ | |
| 395 | +│ │ External APIs │ │ | |
| 396 | +│ │ Tavily │ Firecrawl │ Exa │ SerpAPI │ ElevenLabs │ │ | |
| 397 | +│ └────────────────────────────────────────────────────────────┘ │ | |
| 398 | +└─────────────────────────────────────────────────────────────────┘ | |
| 399 | +``` | |
| 400 | + | |
| 401 | +--- | |
| 402 | + | |
| 403 | +## API Reference | |
| 404 | + | |
| 405 | +### Chat (SSE Streaming) | |
| 406 | + | |
| 407 | +| Method | Endpoint | Description | | |
| 408 | +|--------|----------|-------------| | |
| 409 | +| `POST` | `/api/chat` | Main AI chat with streaming (Server-Sent Events) | | |
| 410 | +| `POST` | `/api/speech-to-text` | Audio transcription (ElevenLabs Scribe) | | |
| 411 | + | |
| 412 | +### Authentication | |
| 413 | + | |
| 414 | +| Method | Endpoint | Description | | |
| 415 | +|--------|----------|-------------| | |
| 416 | +| `POST` | `/api/auth/register` | Create account (generates unique token) | | |
| 417 | +| `POST` | `/api/auth/login` | Authenticate with token | | |
| 418 | +| `POST` | `/api/auth/logout` | End session | | |
| 419 | +| `GET` | `/api/auth/me` | Get current user | | |
| 420 | + | |
| 421 | +### Shared Reports & Sessions | |
| 422 | + | |
| 423 | +| Method | Endpoint | Description | | |
| 424 | +|--------|----------|-------------| | |
| 425 | +| `GET` | `/api/shared-reports` | List all shared reports | | |
| 426 | +| `POST` | `/api/share` | Create shareable report | | |
| 427 | +| `GET` | `/api/share/:shareId` | Get shared report | | |
| 428 | +| `GET` | `/api/sessions` | List user sessions | | |
| 429 | +| `GET` | `/api/sessions/:id` | Get session with messages | | |
| 430 | + | |
| 431 | +### Document Generation | |
| 432 | + | |
| 433 | +| Method | Endpoint | Description | | |
| 434 | +|--------|----------|-------------| | |
| 435 | +| `POST` | `/api/generate-pdf` | Markdown to PDF (Puppeteer) | | |
| 436 | +| `POST` | `/api/generate-docx` | Markdown to DOCX (Pandoc) | | |
| 437 | +| `POST` | `/api/convert-to-slides` | Markdown to Beamer LaTeX PDF | | |
| 438 | +| `GET` | `/api/download/:filename` | Download data file | | |
| 439 | + | |
| 440 | +### Financial Data (FMP) | |
| 441 | + | |
| 442 | +| Method | Endpoint | Description | | |
| 443 | +|--------|----------|-------------| | |
| 444 | +| `GET` | `/api/fmp/quote/:symbol` | Real-time stock quote | | |
| 445 | +| `GET` | `/api/fmp/company-profile/:symbol` | Company profile | | |
| 446 | +| `GET` | `/api/fmp/historical-price/:symbol` | Historical prices | | |
| 447 | +| `GET` | `/api/fmp/intraday/:symbol` | Intraday price data | | |
| 448 | +| `GET` | `/api/fmp/income-statement/:symbol` | Income statement | | |
| 449 | +| `GET` | `/api/fmp/balance-sheet/:symbol` | Balance sheet | | |
| 450 | +| `GET` | `/api/fmp/cash-flow/:symbol` | Cash flow statement | | |
| 451 | +| `GET` | `/api/fmp/key-metrics/:symbol` | Key financial metrics | | |
| 452 | +| `GET` | `/api/fmp/financial-ratios/:symbol` | Financial ratios | | |
| 453 | +| `GET` | `/api/fmp/analyst-estimates/:symbol` | Analyst estimates | | |
| 454 | +| `GET` | `/api/fmp/price-target/:symbol` | Price targets | | |
| 455 | +| `GET` | `/api/fmp/insider-trading/:symbol` | Insider trades | | |
| 456 | +| `GET` | `/api/fmp/institutional-holders/:symbol` | Institutional holders | | |
| 457 | +| `GET` | `/api/fmp/earnings-surprises/:symbol` | Earnings surprises | | |
| 458 | +| `GET` | `/api/fmp/dividend-history/:symbol` | Dividend history | | |
| 459 | +| `GET` | `/api/fmp/esg-score/:symbol` | ESG score | | |
| 460 | +| `GET` | `/api/fmp/financial-news/:symbol` | Financial news | | |
| 461 | +| `GET` | `/api/fmp/upgrades-downgrades/:symbol` | Analyst upgrades/downgrades | | |
| 462 | +| `GET` | `/api/fmp/gainers` | Top market gainers | | |
| 463 | +| `GET` | `/api/fmp/losers` | Top market losers | | |
| 464 | +| `GET` | `/api/fmp/actives` | Most active stocks | | |
| 465 | +| `GET` | `/api/fmp/market-hours` | Market hours | | |
| 466 | +| `GET` | `/api/fmp/chart/light/:symbol` | Lightweight chart data | | |
| 467 | + | |
| 468 | +### Analytics & Admin | |
| 469 | + | |
| 470 | +| Method | Endpoint | Description | | |
| 471 | +|--------|----------|-------------| | |
| 472 | +| `POST` | `/api/analytics/heartbeat` | Update user activity | | |
| 473 | +| `GET` | `/api/analytics/active-users` | Active users (admin) | | |
| 474 | +| `GET` | `/api/analytics/real-time-stats` | Real-time stats | | |
| 475 | +| `GET` | `/api/analytics/metrics` | Historical metrics (admin) | | |
| 476 | +| `POST` | `/api/admin/login` | Admin login | | |
| 477 | +| `GET` | `/api/admin/database` | Full database view (admin) | | |
| 478 | +| `DELETE` | `/api/admin/users/:id` | Delete user (admin) | | |
| 479 | +| `DELETE` | `/api/admin/sessions/:id` | Delete session (admin) | | |
| 480 | +| `DELETE` | `/api/admin/reports/:id` | Delete report (admin) | | |
| 481 | + | |
| 482 | +--- | |
| 483 | + | |
| 484 | +## Usage Examples | |
| 485 | + | |
| 486 | +### Financial Analysis | |
| 487 | +``` | |
| 488 | +"Full analysis of NVDA: profile, financials, insider trades, analyst ratings, sector performance" | |
| 489 | + | |
| 490 | +"Compare AAPL, MSFT, GOOGL on: market cap, P/E, revenue growth, ESG scores, analyst consensus" | |
| 491 | + | |
| 492 | +"Due diligence on TSLA: DCF valuation, institutional holders, insider trading patterns, earnings surprises" | |
| 493 | + | |
| 494 | +"Show me the top gainers today with their financial ratios and analyst ratings" | |
| 495 | +``` | |
| 496 | + | |
| 497 | +### Quantitative Analysis | |
| 498 | +``` | |
| 499 | +"Run a Monte Carlo simulation on AAPL with 10,000 paths over 252 trading days" | |
| 500 | + | |
| 501 | +"Calculate the correlation matrix of FAANG stocks and display a heatmap" | |
| 502 | + | |
| 503 | +"Backtest an RSI strategy on SPY and show the equity curve with Sharpe ratio" | |
| 504 | + | |
| 505 | +"Optimize a portfolio of AAPL, MSFT, GOOGL, AMZN for maximum Sharpe ratio" | |
| 506 | + | |
| 507 | +"Calculate Value at Risk for a $100,000 NVDA position at 95% and 99% confidence" | |
| 508 | + | |
| 509 | +"Estimate GARCH volatility for TSLA and forecast 30-day ahead volatility" | |
| 510 | + | |
| 511 | +"Price a call option on AAPL with strike $200, 30 days to expiry, and show all Greeks" | |
| 512 | +``` | |
| 513 | + | |
| 514 | +### Web Research | |
| 515 | +``` | |
| 516 | +"Search latest AI industry news this week and summarize key developments" | |
| 517 | + | |
| 518 | +"Extract the last NVIDIA earnings call transcript and highlight guidance changes" | |
| 519 | + | |
| 520 | +"Research the impact of Fed rate decisions on tech stocks in 2024-2025" | |
| 521 | + | |
| 522 | +"Crawl investor.nvidia.com and extract all Q4 financial press releases" | |
| 523 | +``` | |
| 524 | + | |
| 525 | +--- | |
| 526 | + | |
| 527 | +## Documentation | |
| 528 | + | |
| 529 | +Exhaustive documentation is available in the [`docs/`](./docs) directory: | |
| 530 | + | |
| 531 | +| Document | Description | | |
| 532 | +|----------|-------------| | |
| 533 | +| **[Getting Started](./docs/GETTING_STARTED.md)** | Prerequisites, installation, Python setup, env vars, first run, troubleshooting | | |
| 534 | +| **[Architecture](./docs/ARCHITECTURE.md)** | System diagram, directory structure, data flow, design decisions | | |
| 535 | +| **[API Reference](./docs/API_REFERENCE.md)** | All 44 endpoints with request/response schemas, SSE event types, query params | | |
| 536 | +| **[Python Analysis](./docs/PYTHON_ANALYSIS.md)** | 8 built-in models (Monte Carlo, Black-Scholes, GARCH, VaR, portfolio, risk, plots, custom), all 17 libraries | | |
| 537 | +| **[Database Schema](./docs/DATABASE.md)** | All 9 tables with columns, types, constraints, relationships | | |
| 538 | +| **[Deployment](./docs/DEPLOYMENT.md)** | Production build, env vars, Docker example, CORS, security checklist | | |
| 539 | +| **[Contributing](./docs/CONTRIBUTING.md)** | Dev setup, code conventions, commit format, how to add endpoints/tools | | |
| 540 | +| **[`.env.example`](./.env.example)** | All environment variables with categories | | |
| 541 | + | |
| 542 | +--- | |
| 543 | + | |
| 544 | +## Testing | |
| 545 | + | |
| 546 | +[](./) | |
| 547 | +[](./) | |
| 548 | +[](./) | |
| 549 | + | |
| 550 | +| Suite | Tests | What it covers | | |
| 551 | +|-------|-------|---------------| | |
| 552 | +| `App.test.tsx` | 3 | Smoke test: renders, shows home page, search bar present | | |
| 553 | +| `config.test.ts` | 5 | App config: name, domain, endpoints, limits, feature flags | | |
| 554 | +| `utils.test.ts` | 5 | `cn()`: class merging, conditionals, dedup, edge cases | | |
| 555 | +| `queryClient.test.ts` | 6 | Query defaults: staleTime, gcTime, retry, refetch policy | | |
| 556 | +| `useChatSession.test.tsx` | 6 | Hook: initial state, reset, history toggle, mutation shape, handlers | | |
| 557 | +| `validation.test.ts` | 16 | All Zod schemas: valid/invalid inputs, missing fields, max lengths | | |
| 558 | + | |
| 559 | +```bash | |
| 560 | +npm test # Run once | |
| 561 | +npm run test:watch # Watch mode | |
| 562 | +npm run test:coverage # With coverage | |
| 563 | +``` | |
| 564 | + | |
| 565 | +--- | |
| 566 | + | |
| 567 | +## Deployment | |
| 568 | + | |
| 569 | +### Production Build | |
| 570 | + | |
| 571 | +```bash | |
| 572 | +npm run build # Builds client (Vite) + server (esbuild) | |
| 573 | +npm start # Starts production server | |
| 574 | +``` | |
| 575 | + | |
| 576 | +### Environment Variables | |
| 577 | + | |
| 578 | +```bash | |
| 579 | +# Required | |
| 580 | +NODE_ENV=production | |
| 581 | +PORT=5000 | |
| 582 | +SESSION_SECRET=<generate-a-strong-random-secret> | |
| 583 | +ANTHROPIC_API_KEY=sk-ant-... | |
| 584 | +FINANCIAL_MODELING_PREP_API_KEY=... | |
| 585 | + | |
| 586 | +# Database (default: SQLite) | |
| 587 | +DATABASE_URL=sqlite://local.db | |
| 588 | +# DATABASE_URL=postgresql://user:pass@host:5432/db | |
| 589 | + | |
| 590 | +# Optional APIs | |
| 591 | +FIRECRAWL_API_KEY=fc-... | |
| 592 | +TAVILY_API_KEY=tvly-... | |
| 593 | +EXA_API_KEY=... | |
| 594 | +SERPAPI_API_KEY=... | |
| 595 | +``` | |
| 596 | + | |
| 597 | +--- | |
| 598 | + | |
| 599 | +## Contributing | |
| 600 | + | |
| 601 | +1. Fork the repository | |
| 602 | +2. Create a feature branch (`git checkout -b feature/my-feature`) | |
| 603 | +3. Run checks before committing: | |
| 604 | + ```bash | |
| 605 | + npm run typecheck && npm run lint && npm test | |
| 606 | + ``` | |
| 607 | +4. Commit with a descriptive message | |
| 608 | +5. Open a Pull Request | |
| 609 | + | |
| 610 | +### Code Conventions | |
| 611 | + | |
| 612 | +| Rule | Enforced By | | |
| 613 | +|------|------------| | |
| 614 | +| TypeScript `strict` mode | `tsconfig.json` | | |
| 615 | +| ESLint (typescript-eslint + react-hooks) | `eslint.config.js` | | |
| 616 | +| Prettier formatting | `.prettierrc` | | |
| 617 | +| Zod validation on all API inputs | `server/routes/validation.ts` | | |
| 618 | +| `logger` instead of `console.log` | ESLint `no-console` rule | | |
| 619 | +| `@/` path alias (client), `@shared/` (shared) | `tsconfig.json` + `vite.config.ts` | | |
| 620 | +| Components grouped by domain | `components/chat/`, `components/explore/`, etc. | | |
| 621 | +| One hook per concern | `useChatSession`, `useChatMutation`, etc. | | |
| 622 | + | |
| 623 | +--- | |
| 624 | + | |
| 625 | +## Authors | |
| 626 | + | |
| 627 | +<table> | |
| 628 | + <tr> | |
| 629 | + <td align="center" width="50%"> | |
| 630 | + <b>Simon-Pierre Boucher</b><br/> | |
| 631 | + <i>Creator & Lead Developer</i><br/><br/> | |
| 632 | + <a href="mailto:contact@spboucher.ai"><img src="https://img.shields.io/badge/Email-contact%40spboucher.ai-8b5cf6?style=flat-square&logo=maildotru&logoColor=white"/></a><br/> | |
| 633 | + <a href="https://www.spboucher.ai"><img src="https://img.shields.io/badge/Website-spboucher.ai-3b82f6?style=flat-square&logo=safari&logoColor=white"/></a><br/> | |
| 634 | + <a href="https://github.com/spboucher-ai"><img src="https://img.shields.io/badge/GitHub-spboucher--ai-181717?style=flat-square&logo=github&logoColor=white"/></a> | |
| 635 | + </td> | |
| 636 | + <td align="center" width="50%"> | |
| 637 | + <b>Claude Opus 4.8</b><br/> | |
| 638 | + <i>AI Pair Programmer</i><br/><br/> | |
| 639 | + <a href="https://claude.ai/claude-code"><img src="https://img.shields.io/badge/Claude_Code-Anthropic-d97706?style=flat-square&logo=anthropic&logoColor=white"/></a><br/> | |
| 640 | + <a href="https://anthropic.com"><img src="https://img.shields.io/badge/Model-Opus_4.8_(1M_ctx)-d97706?style=flat-square"/></a><br/> | |
| 641 | + <img src="https://img.shields.io/badge/Contribution-9_commits,_4000+_lines-22c55e?style=flat-square"/> | |
| 642 | + </td> | |
| 643 | + </tr> | |
| 644 | +</table> | |
| 645 | + | |
| 646 | +> Full codebase audit, architecture restructuring, TypeScript typing, route modularization, hook extraction, ESLint/Prettier/Vitest setup, ErrorBoundary, Zod validation, and README authored via [Claude Code](https://claude.ai/claude-code). | |
| 647 | + | |
| 648 | +--- | |
| 649 | + | |
| 650 | +## Acknowledgments | |
| 651 | + | |
| 652 | +[](https://anthropic.com/) | |
| 653 | +[](https://financialmodelingprep.com/) | |
| 654 | +[](https://firecrawl.dev/) | |
| 655 | +[](https://tavily.com/) | |
| 656 | +[](https://exa.ai/) | |
| 657 | +[](https://radix-ui.com/) | |
| 658 | +[](https://ui.shadcn.com/) | |
| 659 | +[](https://recharts.org/) | |
| 660 | + | |
| 661 | +--- | |
| 662 | + | |
| 663 | +## License | |
| 664 | + | |
| 665 | +[](./LICENSE) | |
| 666 | + | |
| 667 | +--- | |
| 668 | + | |
| 669 | +<div align="center"> | |
| 670 | + | |
| 671 | +<br/> | |
| 672 | + | |
| 673 | +[](https://www.vquant.ai) | |
| 674 | + | |
| 675 | +<br/> | |
| 676 | + | |
| 677 | +*Where AI Meets Institutional-Grade Financial Analysis* | |
| 678 | + | |
| 679 | +<br/> | |
| 680 | + | |
| 681 | +**Built with** | |
| 682 | +[](./) | |
| 683 | +[](./) | |
| 684 | +[](./) | |
| 685 | +[](./) | |
| 686 | +[](./) | |
| 687 | +[](./) | |
| 688 | + | |
| 689 | +</div> | |
added
client/index.html
+66 −0
@@ -0,0 +1,66 @@ | ||
| 1 | +<!DOCTYPE html> | |
| 2 | +<!-- | |
| 3 | + ============================================================================= | |
| 4 | + VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 5 | + ----------------------------------------------------------------------------- | |
| 6 | + File: client/index.html | |
| 7 | + | |
| 8 | + Author: Simon-Pierre Boucher | |
| 9 | + Contact: contact@spboucher.ai | |
| 10 | + Website: https://www.spboucher.ai | |
| 11 | + Demo: https://www.vquant.ai | |
| 12 | + License: MIT (see LICENSE) | |
| 13 | + | |
| 14 | + Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 15 | + ============================================================================= | |
| 16 | +--> | |
| 17 | + | |
| 18 | +<html lang="fr"> | |
| 19 | + <head> | |
| 20 | + <meta charset="UTF-8" /> | |
| 21 | + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" /> | |
| 22 | + | |
| 23 | + <!-- Primary Meta Tags --> | |
| 24 | + <title>VQuant | AI-Powered Financial Intelligence Platform</title> | |
| 25 | + <meta name="title" content="VQuant | AI-Powered Financial Intelligence Platform" /> | |
| 26 | + <meta name="description" content="Plateforme d'analyse financière de niveau institutionnel propulsée par Claude AI. 265+ sources de données, analyses quantitatives avancées, extraction de PDFs, et recherche multi-sources." /> | |
| 27 | + <meta name="keywords" content="analyse financière, IA, Claude AI, FMP, analyse quantitative, trading, investissement, bourse, actions, ETF, Monte Carlo, portfolio optimization" /> | |
| 28 | + <meta name="author" content="VQuant" /> | |
| 29 | + | |
| 30 | + <!-- Canonical URL --> | |
| 31 | + <link rel="canonical" href="https://www.vquant.ai" /> | |
| 32 | + | |
| 33 | + <!-- Open Graph / Facebook --> | |
| 34 | + <meta property="og:type" content="website" /> | |
| 35 | + <meta property="og:url" content="https://www.vquant.ai" /> | |
| 36 | + <meta property="og:title" content="VQuant | AI-Powered Financial Intelligence" /> | |
| 37 | + <meta property="og:description" content="Plateforme d'analyse financière propulsée par IA avec 265+ sources de données et analyses quantitatives avancées" /> | |
| 38 | + <meta property="og:site_name" content="VQuant" /> | |
| 39 | + | |
| 40 | + <!-- Twitter --> | |
| 41 | + <meta property="twitter:card" content="summary_large_image" /> | |
| 42 | + <meta property="twitter:url" content="https://www.vquant.ai" /> | |
| 43 | + <meta property="twitter:title" content="VQuant | AI-Powered Financial Intelligence" /> | |
| 44 | + <meta property="twitter:description" content="Plateforme d'analyse financière propulsée par IA avec 265+ sources de données" /> | |
| 45 | + | |
| 46 | + <!-- Fonts --> | |
| 47 | + <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| 48 | + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| 49 | + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> | |
| 50 | + | |
| 51 | + <!-- Favicon --> | |
| 52 | + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | |
| 53 | + | |
| 54 | + <!-- Desktop app (Electron) title bar adaptation --> | |
| 55 | + <style> | |
| 56 | + @media all { | |
| 57 | + .electron-drag { -webkit-app-region: drag; } | |
| 58 | + .electron-no-drag { -webkit-app-region: no-drag; } | |
| 59 | + } | |
| 60 | + </style> | |
| 61 | + </head> | |
| 62 | + <body> | |
| 63 | + <div id="root"></div> | |
| 64 | + <script type="module" src="/src/main.tsx"></script> | |
| 65 | + </body> | |
| 66 | +</html> | |
| \ No newline at end of file | ||
added
client/src/App.test.tsx
+39 −0
@@ -0,0 +1,39 @@ | ||
| 1 | +/* | |
| 2 | + * ============================================================================= | |
| 3 | + * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + * ----------------------------------------------------------------------------- | |
| 5 | + * File: client/src/App.test.tsx | |
| 6 | + * | |
| 7 | + * Author: Simon-Pierre Boucher | |
| 8 | + * Contact: contact@spboucher.ai | |
| 9 | + * Website: https://www.spboucher.ai | |
| 10 | + * Demo: https://www.vquant.ai | |
| 11 | + * License: MIT (see LICENSE) | |
| 12 | + * | |
| 13 | + * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + * ============================================================================= | |
| 15 | + */ | |
| 16 | + | |
| 17 | +import { describe, it, expect } from "vitest"; | |
| 18 | +import { render, screen } from "@testing-library/react"; | |
| 19 | +import App from "./App"; | |
| 20 | + | |
| 21 | +describe("App", () => { | |
| 22 | + it("renders without crashing", () => { | |
| 23 | + const { container } = render(<App />); | |
| 24 | + expect(container).toBeTruthy(); | |
| 25 | + }); | |
| 26 | + | |
| 27 | + it("renders the home page by default", () => { | |
| 28 | + render(<App />); | |
| 29 | + // The home page has a data-testid="page-home" | |
| 30 | + expect(screen.getByTestId("page-home")).toBeInTheDocument(); | |
| 31 | + }); | |
| 32 | + | |
| 33 | + it("renders the search bar", () => { | |
| 34 | + render(<App />); | |
| 35 | + expect( | |
| 36 | + screen.getByPlaceholderText(/question.*march/i), | |
| 37 | + ).toBeInTheDocument(); | |
| 38 | + }); | |
| 39 | +}); | |
added
client/src/App.tsx
+67 −0
@@ -0,0 +1,67 @@ | ||
| 1 | +/* | |
| 2 | + * ============================================================================= | |
| 3 | + * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + * ----------------------------------------------------------------------------- | |
| 5 | + * File: client/src/App.tsx | |
| 6 | + * | |
| 7 | + * Author: Simon-Pierre Boucher | |
| 8 | + * Contact: contact@spboucher.ai | |
| 9 | + * Website: https://www.spboucher.ai | |
| 10 | + * Demo: https://www.vquant.ai | |
| 11 | + * License: MIT (see LICENSE) | |
| 12 | + * | |
| 13 | + * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + * ============================================================================= | |
| 15 | + */ | |
| 16 | + | |
| 17 | +import { Switch, Route } from "wouter"; | |
| 18 | +import { queryClient } from "./lib/queryClient"; | |
| 19 | +import { QueryClientProvider } from "@tanstack/react-query"; | |
| 20 | +import { Toaster } from "@/components/ui/toaster"; | |
| 21 | +import { TooltipProvider } from "@/components/ui/tooltip"; | |
| 22 | +import { ThemeProvider } from "@/components/theme-provider"; | |
| 23 | +import { ErrorBoundary } from "@/components/error-boundary"; | |
| 24 | +import Home from "@/pages/home"; | |
| 25 | +import Showcase from "@/pages/showcase"; | |
| 26 | +import Admin from "@/pages/admin"; | |
| 27 | +import SharedReport from "@/pages/shared-report"; | |
| 28 | +import Documentation from "@/pages/documentation"; | |
| 29 | +import Privacy from "@/pages/privacy"; | |
| 30 | +import Terms from "@/pages/terms"; | |
| 31 | +import SlidesGenerator from "@/pages/slides-generator"; | |
| 32 | +import Auth from "@/pages/auth"; | |
| 33 | +import NotFound from "@/pages/not-found"; | |
| 34 | + | |
| 35 | +function Router() { | |
| 36 | + return ( | |
| 37 | + <Switch> | |
| 38 | + <Route path="/" component={Home} /> | |
| 39 | + <Route path="/auth" component={Auth} /> | |
| 40 | + <Route path="/showcase" component={Showcase} /> | |
| 41 | + <Route path="/admin" component={Admin} /> | |
| 42 | + <Route path="/share/:shareId" component={SharedReport} /> | |
| 43 | + <Route path="/docs" component={Documentation} /> | |
| 44 | + <Route path="/privacy" component={Privacy} /> | |
| 45 | + <Route path="/terms" component={Terms} /> | |
| 46 | + <Route path="/slides-generator" component={SlidesGenerator} /> | |
| 47 | + <Route component={NotFound} /> | |
| 48 | + </Switch> | |
| 49 | + ); | |
| 50 | +} | |
| 51 | + | |
| 52 | +function App() { | |
| 53 | + return ( | |
| 54 | + <ErrorBoundary> | |
| 55 | + <QueryClientProvider client={queryClient}> | |
| 56 | + <ThemeProvider defaultTheme="light" storageKey="vquant-theme-v2"> | |
| 57 | + <TooltipProvider> | |
| 58 | + <Toaster /> | |
| 59 | + <Router /> | |
| 60 | + </TooltipProvider> | |
| 61 | + </ThemeProvider> | |
| 62 | + </QueryClientProvider> | |
| 63 | + </ErrorBoundary> | |
| 64 | + ); | |
| 65 | +} | |
| 66 | + | |
| 67 | +export default App; | |
added
client/src/components/chat/agent-steps.tsx
+57 −0
@@ -0,0 +1,57 @@ | ||
| 1 | +/* | |
| 2 | + * ============================================================================= | |
| 3 | + * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + * ----------------------------------------------------------------------------- | |
| 5 | + * File: client/src/components/chat/agent-steps.tsx | |
| 6 | + * | |
| 7 | + * Author: Simon-Pierre Boucher | |
| 8 | + * Contact: contact@spboucher.ai | |
| 9 | + * Website: https://www.spboucher.ai | |
| 10 | + * Demo: https://www.vquant.ai | |
| 11 | + * License: MIT (see LICENSE) | |
| 12 | + * | |
| 13 | + * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + * ============================================================================= | |
| 15 | + */ | |
| 16 | + | |
| 17 | +import { Check } from "lucide-react"; | |
| 18 | + | |
| 19 | +export interface AgentStep { | |
| 20 | + tool: string; | |
| 21 | + status: 'pending' | 'running' | 'completed' | 'error'; | |
| 22 | + input?: any; | |
| 23 | + error?: string; | |
| 24 | + duration?: number; | |
| 25 | +} | |
| 26 | + | |
| 27 | +interface AgentStepsProps { | |
| 28 | + steps: AgentStep[]; | |
| 29 | + currentStep?: number; | |
| 30 | +} | |
| 31 | + | |
| 32 | +export function AgentSteps({ steps, currentStep }: AgentStepsProps) { | |
| 33 | + if (steps.length === 0) return null; | |
| 34 | + | |
| 35 | + // Only show completed steps with simple green circle and checkmark | |
| 36 | + const completedSteps = steps.filter(s => s.status === 'completed'); | |
| 37 | + | |
| 38 | + if (completedSteps.length === 0) return null; | |
| 39 | + | |
| 40 | + return ( | |
| 41 | + <div className="flex flex-wrap gap-2 mb-4"> | |
| 42 | + {completedSteps.map((step, index) => ( | |
| 43 | + <div | |
| 44 | + key={`${step.tool}-${index}`} | |
| 45 | + className="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-green-500/10 border border-green-500/20" | |
| 46 | + > | |
| 47 | + <div className="w-4 h-4 rounded-full bg-green-500/20 flex items-center justify-center flex-shrink-0"> | |
| 48 | + <Check className="w-3 h-3 text-green-500" /> | |
| 49 | + </div> | |
| 50 | + <span className="text-xs text-muted-foreground"> | |
| 51 | + {step.tool.replace(/_/g, ' ')} | |
| 52 | + </span> | |
| 53 | + </div> | |
| 54 | + ))} | |
| 55 | + </div> | |
| 56 | + ); | |
| 57 | +} | |
added
client/src/components/chat/conversation-history.tsx
+89 −0
@@ -0,0 +1,89 @@ | ||
| 1 | +/* | |
| 2 | + * ============================================================================= | |
| 3 | + * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + * ----------------------------------------------------------------------------- | |
| 5 | + * File: client/src/components/chat/conversation-history.tsx | |
| 6 | + * | |
| 7 | + * Author: Simon-Pierre Boucher | |
| 8 | + * Contact: contact@spboucher.ai | |
| 9 | + * Website: https://www.spboucher.ai | |
| 10 | + * Demo: https://www.vquant.ai | |
| 11 | + * License: MIT (see LICENSE) | |
| 12 | + * | |
| 13 | + * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + * ============================================================================= | |
| 15 | + */ | |
| 16 | + | |
| 17 | +import { | |
| 18 | + Accordion, | |
| 19 | + AccordionContent, | |
| 20 | + AccordionItem, | |
| 21 | + AccordionTrigger, | |
| 22 | +} from "@/components/ui/accordion"; | |
| 23 | +import { StreamingAnswer } from "@/components/streaming-answer"; | |
| 24 | + | |
| 25 | +interface ConversationMessage { | |
| 26 | + question: string; | |
| 27 | + answer: string; | |
| 28 | +} | |
| 29 | + | |
| 30 | +interface ConversationHistoryProps { | |
| 31 | + history: ConversationMessage[]; | |
| 32 | +} | |
| 33 | + | |
| 34 | +export function ConversationHistory({ history }: ConversationHistoryProps) { | |
| 35 | + if (history.length === 0) { | |
| 36 | + return null; | |
| 37 | + } | |
| 38 | + | |
| 39 | + return ( | |
| 40 | + <div className="w-full max-w-4xl mx-auto mb-8 space-y-3"> | |
| 41 | + {history.map((message, index) => ( | |
| 42 | + <Accordion | |
| 43 | + key={index} | |
| 44 | + type="single" | |
| 45 | + collapsible | |
| 46 | + className="w-full" | |
| 47 | + data-testid={`accordion-conversation-${index}`} | |
| 48 | + > | |
| 49 | + <AccordionItem value={`item-${index}`} className="border rounded-lg"> | |
| 50 | + <AccordionTrigger | |
| 51 | + className="px-4 py-3 hover-elevate no-underline text-left" | |
| 52 | + data-testid={`trigger-conversation-${index}`} | |
| 53 | + > | |
| 54 | + <div className="flex items-start gap-3 w-full pr-4"> | |
| 55 | + <div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-sm font-medium text-primary"> | |
| 56 | + {index + 1} | |
| 57 | + </div> | |
| 58 | + <div className="flex-1 min-w-0"> | |
| 59 | + <p className="text-sm font-medium truncate"> | |
| 60 | + {message.question} | |
| 61 | + </p> | |
| 62 | + </div> | |
| 63 | + </div> | |
| 64 | + </AccordionTrigger> | |
| 65 | + <AccordionContent className="px-4 pb-4"> | |
| 66 | + <div className="pl-11"> | |
| 67 | + <div className="mb-3 text-sm font-medium text-muted-foreground"> | |
| 68 | + Question: | |
| 69 | + </div> | |
| 70 | + <div className="mb-4 p-3 rounded-md bg-muted/50"> | |
| 71 | + <p className="text-sm">{message.question}</p> | |
| 72 | + </div> | |
| 73 | + <div className="mb-3 text-sm font-medium text-muted-foreground"> | |
| 74 | + Answer: | |
| 75 | + </div> | |
| 76 | + <div className="prose prose-sm dark:prose-invert max-w-none"> | |
| 77 | + <StreamingAnswer | |
| 78 | + content={message.answer} | |
| 79 | + isStreaming={false} | |
| 80 | + /> | |
| 81 | + </div> | |
| 82 | + </div> | |
| 83 | + </AccordionContent> | |
| 84 | + </AccordionItem> | |
| 85 | + </Accordion> | |
| 86 | + ))} | |
| 87 | + </div> | |
| 88 | + ); | |
| 89 | +} | |
added
client/src/components/chat/custom-python-figure.tsx
+220 −0
@@ -0,0 +1,220 @@ | ||
| 1 | +/* | |
| 2 | + * ============================================================================= | |
| 3 | + * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + * ----------------------------------------------------------------------------- | |
| 5 | + * File: client/src/components/chat/custom-python-figure.tsx | |
| 6 | + * | |
| 7 | + * Author: Simon-Pierre Boucher | |
| 8 | + * Contact: contact@spboucher.ai | |
| 9 | + * Website: https://www.spboucher.ai | |
| 10 | + * Demo: https://www.vquant.ai | |
| 11 | + * License: MIT (see LICENSE) | |
| 12 | + * | |
| 13 | + * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + * ============================================================================= | |
| 15 | + */ | |
| 16 | + | |
| 17 | +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
| 18 | +import { Badge } from "@/components/ui/badge"; | |
| 19 | +import { useState, useEffect } from "react"; | |
| 20 | +import { ChevronDown, ChevronUp, Download } from "lucide-react"; | |
| 21 | +import { Button } from "@/components/ui/button"; | |
| 22 | + | |
| 23 | +interface FigureBatch { | |
| 24 | + id: string; | |
| 25 | + figures: string[]; // URLs (/figures/xxx.png) or base64 encoded images | |
| 26 | + output?: string; // Text output from Python | |
| 27 | + description?: string; | |
| 28 | +} | |
| 29 | + | |
| 30 | +function figureSrc(figure: string): string { | |
| 31 | + // If it starts with / or http, it's a URL — use directly | |
| 32 | + if (figure.startsWith('/') || figure.startsWith('http')) return figure; | |
| 33 | + // Otherwise treat as base64 | |
| 34 | + return `data:image/png;base64,${figure}`; | |
| 35 | +} | |
| 36 | + | |
| 37 | +interface CustomPythonFigureGalleryProps { | |
| 38 | + figureBatches: FigureBatch[]; // Array of figure batches | |
| 39 | +} | |
| 40 | + | |
| 41 | +export function CustomPythonFigure({ figureBatches }: CustomPythonFigureGalleryProps) { | |
| 42 | + // Initialize with all batches expanded | |
| 43 | + const [expandedBatches, setExpandedBatches] = useState<Set<string>>( | |
| 44 | + new Set(figureBatches.map(b => b.id)) | |
| 45 | + ); | |
| 46 | + | |
| 47 | + // When new batches are added, expand them automatically | |
| 48 | + useEffect(() => { | |
| 49 | + const allIds = new Set(figureBatches.map(b => b.id)); | |
| 50 | + setExpandedBatches(allIds); | |
| 51 | + }, [figureBatches.length]); // Update when number of batches changes | |
| 52 | + | |
| 53 | + if (!figureBatches || figureBatches.length === 0) { | |
| 54 | + return null; | |
| 55 | + } | |
| 56 | + | |
| 57 | + const totalFigures = figureBatches.reduce((sum, batch) => sum + batch.figures.length, 0); | |
| 58 | + | |
| 59 | + const toggleBatch = (batchId: string) => { | |
| 60 | + setExpandedBatches(prev => { | |
| 61 | + const newSet = new Set(prev); | |
| 62 | + if (newSet.has(batchId)) { | |
| 63 | + newSet.delete(batchId); | |
| 64 | + } else { | |
| 65 | + newSet.add(batchId); | |
| 66 | + } | |
| 67 | + return newSet; | |
| 68 | + }); | |
| 69 | + }; | |
| 70 | + | |
| 71 | + return ( | |
| 72 | + <Card className="w-full"> | |
| 73 | + <CardHeader> | |
| 74 | + <CardTitle className="flex items-center gap-2"> | |
| 75 | + <span className="text-2xl">🐍</span> | |
| 76 | + Galerie d'Analyses Python | |
| 77 | + <Badge variant="secondary" className="ml-auto"> | |
| 78 | + {totalFigures} figure{totalFigures > 1 ? 's' : ''} | |
| 79 | + </Badge> | |
| 80 | + </CardTitle> | |
| 81 | + <CardDescription> | |
| 82 | + {figureBatches.length} analyse{figureBatches.length > 1 ? 's' : ''} Python générée{figureBatches.length > 1 ? 's' : ''} | |
| 83 | + </CardDescription> | |
| 84 | + </CardHeader> | |
| 85 | + <CardContent className="space-y-4"> | |
| 86 | + {figureBatches.map((batch, batchIndex) => { | |
| 87 | + const isExpanded = expandedBatches.has(batch.id); | |
| 88 | + const batchNumber = batchIndex + 1; | |
| 89 | + | |
| 90 | + return ( | |
| 91 | + <div key={batch.id} className="border rounded-lg overflow-hidden"> | |
| 92 | + {/* Batch Header - Collapsible */} | |
| 93 | + <div | |
| 94 | + className="flex items-center justify-between p-4 bg-muted/30 cursor-pointer hover:bg-muted/50 transition-colors" | |
| 95 | + onClick={() => toggleBatch(batch.id)} | |
| 96 | + > | |
| 97 | + <div className="flex items-center gap-3"> | |
| 98 | + <Badge variant="outline">#{batchNumber}</Badge> | |
| 99 | + <div> | |
| 100 | + <div className="font-semibold text-sm"> | |
| 101 | + {batch.description || `Analyse Python #${batchNumber}`} | |
| 102 | + </div> | |
| 103 | + <div className="text-xs text-muted-foreground"> | |
| 104 | + {batch.figures.length} figure{batch.figures.length > 1 ? 's' : ''} | |
| 105 | + {batch.output && ` • ${batch.output.split('\n').length} lignes de résultats`} | |
| 106 | + </div> | |
| 107 | + </div> | |
| 108 | + </div> | |
| 109 | + <Button variant="ghost" size="sm"> | |
| 110 | + {isExpanded ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />} | |
| 111 | + </Button> | |
| 112 | + </div> | |
| 113 | + | |
| 114 | + {/* Batch Content - Expandable */} | |
| 115 | + {isExpanded && ( | |
| 116 | + <div className="p-4 space-y-4"> | |
| 117 | + {/* Text output if available */} | |
| 118 | + {batch.output && batch.output.trim() && ( | |
| 119 | + <div className="bg-muted/50 p-4 rounded-lg"> | |
| 120 | + <h4 className="font-semibold mb-2 text-sm">📊 Résultats</h4> | |
| 121 | + <pre className="text-sm font-mono whitespace-pre-wrap overflow-x-auto"> | |
| 122 | + {batch.output} | |
| 123 | + </pre> | |
| 124 | + </div> | |
| 125 | + )} | |
| 126 | + | |
| 127 | + {/* Figures Grid */} | |
| 128 | + <div className="space-y-3"> | |
| 129 | + <div className="flex items-center justify-between"> | |
| 130 | + <h4 className="font-semibold text-sm"> | |
| 131 | + 📈 Graphique{batch.figures.length > 1 ? 's' : ''} | |
| 132 | + </h4> | |
| 133 | + {batch.figures.length > 1 && ( | |
| 134 | + <Button | |
| 135 | + variant="outline" | |
| 136 | + size="sm" | |
| 137 | + onClick={(e) => { | |
| 138 | + e.stopPropagation(); | |
| 139 | + // Download all figures as zip would be nice, but for now just show button | |
| 140 | + }} | |
| 141 | + className="text-xs" | |
| 142 | + > | |
| 143 | + <Download className="w-3 h-3 mr-1" /> | |
| 144 | + Tout télécharger | |
| 145 | + </Button> | |
| 146 | + )} | |
| 147 | + </div> | |
| 148 | + | |
| 149 | + <div className={`grid gap-4 ${batch.figures.length > 1 ? 'grid-cols-1 lg:grid-cols-2' : 'grid-cols-1'}`}> | |
| 150 | + {batch.figures.map((figure, figIndex) => { | |
| 151 | + const figureId = `${batch.id}-${figIndex}`; | |
| 152 | + | |
| 153 | + return ( | |
| 154 | + <div key={figIndex} className="border rounded-lg overflow-hidden bg-card group relative"> | |
| 155 | + {/* Figure ID badge for reference */} | |
| 156 | + <div className="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity"> | |
| 157 | + <Badge variant="secondary" className="text-xs font-mono"> | |
| 158 | + {figureId} | |
| 159 | + </Badge> | |
| 160 | + </div> | |
| 161 | + | |
| 162 | + <img | |
| 163 | + src={figureSrc(figure)} | |
| 164 | + alt={`Figure ${batchNumber}.${figIndex + 1}`} | |
| 165 | + className="w-full h-auto cursor-pointer" | |
| 166 | + style={{ maxHeight: '600px', objectFit: 'contain' }} | |
| 167 | + onError={(e) => { | |
| 168 | + const img = e.currentTarget; | |
| 169 | + if (!img.dataset.retried) { | |
| 170 | + img.dataset.retried = 'true'; | |
| 171 | + // Retry with cache-busted URL | |
| 172 | + const src = figureSrc(figure); | |
| 173 | + img.src = src.includes('?') ? `${src}&t=${Date.now()}` : `${src}?t=${Date.now()}`; | |
| 174 | + } else { | |
| 175 | + img.style.display = 'none'; | |
| 176 | + const fallback = img.parentElement?.querySelector('.figure-fallback'); | |
| 177 | + if (fallback) (fallback as HTMLElement).style.display = 'flex'; | |
| 178 | + } | |
| 179 | + }} | |
| 180 | + onClick={() => { | |
| 181 | + const win = window.open(); | |
| 182 | + if (win) { | |
| 183 | + win.document.write(`<img src="${figureSrc(figure)}" style="max-width: 100%; height: auto;" />`); | |
| 184 | + win.document.title = `Figure ${batchNumber}.${figIndex + 1}`; | |
| 185 | + } | |
| 186 | + }} | |
| 187 | + /> | |
| 188 | + <div className="figure-fallback items-center justify-center p-8 bg-muted/30" style={{ display: 'none' }}> | |
| 189 | + <p className="text-sm text-muted-foreground">Figure non disponible</p> | |
| 190 | + </div> | |
| 191 | + | |
| 192 | + {/* Figure info footer */} | |
| 193 | + <div className="p-2 bg-muted/30 border-t flex items-center justify-between"> | |
| 194 | + <div className="text-xs text-muted-foreground"> | |
| 195 | + Figure {batchNumber}.{figIndex + 1} | |
| 196 | + </div> | |
| 197 | + <a | |
| 198 | + href={figureSrc(figure)} | |
| 199 | + download={`vibequant-figure-${figureId}.png`} | |
| 200 | + className="text-xs text-primary hover:underline flex items-center gap-1" | |
| 201 | + onClick={(e) => e.stopPropagation()} | |
| 202 | + > | |
| 203 | + <Download className="w-3 h-3" /> | |
| 204 | + Télécharger | |
| 205 | + </a> | |
| 206 | + </div> | |
| 207 | + </div> | |
| 208 | + ); | |
| 209 | + })} | |
| 210 | + </div> | |
| 211 | + </div> | |
| 212 | + </div> | |
| 213 | + )} | |
| 214 | + </div> | |
| 215 | + ); | |
| 216 | + })} | |
| 217 | + </CardContent> | |
| 218 | + </Card> | |
| 219 | + ); | |
| 220 | +} | |
added
client/src/components/chat/download-buttons.tsx
+217 −0
@@ -0,0 +1,217 @@ | ||
| 1 | +/* | |
| 2 | + * ============================================================================= | |
| 3 | + * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform | |
| 4 | + * ----------------------------------------------------------------------------- | |
| 5 | + * File: client/src/components/chat/download-buttons.tsx | |
| 6 | + * | |
| 7 | + * Author: Simon-Pierre Boucher | |
| 8 | + * Contact: contact@spboucher.ai | |
| 9 | + * Website: https://www.spboucher.ai | |
| 10 | + * Demo: https://www.vquant.ai | |
| 11 | + * License: MIT (see LICENSE) | |
| 12 | + * | |
| 13 | + * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. | |
| 14 | + * ============================================================================= | |
| 15 | + */ | |
| 16 | + | |
| 17 | +import { FileText, FileDown, FileType, Loader2, Presentation } from "lucide-react"; | |
| 18 | +import { Button } from "@/components/ui/button"; | |
| 19 | +import { useState } from "react"; | |
| 20 | +import { useToast } from "@/hooks/use-toast"; | |
| 21 | +import { useLocation } from "wouter"; | |
| 22 | + | |
| 23 | +interface DownloadButtonsProps { | |
| 24 | + question: string; | |
| 25 | + answer: string; | |
| 26 | + contentRef?: React.RefObject<HTMLElement>; | |
| 27 | + figureUrls?: string[]; | |
| 28 | +} | |
| 29 | + | |
| 30 | +export function DownloadButtons({ question, answer, contentRef, figureUrls = [] }: DownloadButtonsProps) { | |
| 31 | + const [isGeneratingPDF, setIsGeneratingPDF] = useState(false); | |
| 32 | + const [isGeneratingDOCX, setIsGeneratingDOCX] = useState(false); | |
| 33 | + const { toast } = useToast(); | |
| 34 | + const [, setLocation] = useLocation(); | |
| 35 | + | |
| 36 | + const handleDownloadMarkdown = () => { | |
| 37 | + const content = answer; | |
| 38 | + const blob = new Blob([content], { type: 'text/markdown' }); | |
| 39 | + const url = URL.createObjectURL(blob); | |
| 40 | + const a = document.createElement('a'); | |
| 41 | + a.href = url; | |
| 42 | + a.download = `vibequant-${Date.now()}.md`; | |
| 43 | + document.body.appendChild(a); | |
| 44 | + a.click(); | |
| 45 | + document.body.removeChild(a); | |
| 46 | + URL.revokeObjectURL(url); | |
| 47 | + | |
| 48 | + toast({ | |
| 49 | + title: "Téléchargement réussi", | |
| 50 | + description: "Le fichier Markdown a été téléchargé avec succès.", | |
| 51 | + }); | |
| 52 | + }; | |
| 53 | + | |
| 54 | + const handleDownloadPDF = async () => { | |
| 55 | + setIsGeneratingPDF(true); | |
| 56 | + | |
| 57 | + try { | |
| 58 | + const response = await fetch('/api/generate-pdf', { | |
| 59 | + method: 'POST', | |
| 60 | + headers: { | |
| 61 | + 'Content-Type': 'application/json', | |
| 62 | + }, | |
| 63 | + body: JSON.stringify({ | |
| 64 | + question, | |
| 65 | + answer | |
| 66 | + }), | |
| 67 | + }); | |
| 68 | + | |
| 69 | + if (!response.ok) { | |
| 70 | + throw new Error('Failed to generate PDF'); | |
| 71 | + } | |
| 72 | + | |
| 73 | + const blob = await response.blob(); | |
| 74 | + const url = URL.createObjectURL(blob); | |
| 75 | + const a = document.createElement('a'); | |
| 76 | + a.href = url; | |
| 77 | + a.download = `vibequant-${Date.now()}.pdf`; | |
| 78 | + document.body.appendChild(a); | |
| 79 | + a.click(); | |
| 80 | + document.body.removeChild(a); | |
| 81 | + URL.revokeObjectURL(url); | |
| 82 | + | |
| 83 | + toast({ | |
| 84 | + title: "PDF généré avec succès", | |
| 85 | + description: "Le rapport PDF a été téléchargé.", | |
| 86 | + }); | |
| 87 | + } catch (error) { | |
| 88 | + console.error('Error generating PDF:', error); | |
| 89 | + toast({ | |
| 90 | + title: "Erreur de génération", | |
| 91 | + description: "Une erreur s'est produite lors de la génération du PDF. Veuillez réessayer.", | |
| 92 | + variant: "destructive", | |
| 93 | + }); | |
| 94 | + } finally { | |
| 95 | + setIsGeneratingPDF(false); | |
| 96 | + } | |
| 97 | + }; | |
| 98 | + | |
| 99 | + const handleDownloadDOCX = async () => { | |
| 100 | + setIsGeneratingDOCX(true); | |
| 101 | + | |
| 102 | + try { | |
| 103 | + const response = await fetch('/api/generate-docx', { | |
| 104 | + method: 'POST', | |
| 105 | + headers: { | |
| 106 | + 'Content-Type': 'application/json', | |
| 107 | + }, | |
| 108 | + body: JSON.stringify({ | |
| 109 | + question, | |
| 110 | + answer | |
| 111 | + }), | |
| 112 | + }); | |
| 113 | + | |
| 114 | + if (!response.ok) { | |
| 115 | + throw new Error('Failed to generate Word document'); | |
| 116 | + } | |
| 117 | + | |
| 118 | + const blob = await response.blob(); | |
| 119 | + const url = URL.createObjectURL(blob); | |
| 120 | + const a = document.createElement('a'); | |
| 121 | + a.href = url; | |
| 122 | + a.download = `vibequant-${Date.now()}.docx`; | |
| 123 | + document.body.appendChild(a); | |
| 124 | + a.click(); | |
| 125 | + document.body.removeChild(a); | |
| 126 | + URL.revokeObjectURL(url); | |
| 127 | + | |
| 128 | + toast({ | |
| 129 | + title: "Word généré avec succès", | |
| 130 | + description: "Le document Word a été téléchargé.", | |
| 131 | + }); | |
| 132 | + } catch (error) { | |
| 133 | + console.error('Error generating DOCX:', error); | |
| 134 | + toast({ | |
| 135 | + title: "Erreur de génération", | |
| 136 | + description: "Une erreur s'est produite lors de la génération du Word. Veuillez réessayer.", | |
| 137 | + variant: "destructive", | |
| 138 | + }); | |
| 139 | + } finally { | |
| 140 | + setIsGeneratingDOCX(false); | |
| 141 | + } | |
| 142 | + }; | |
| 143 | + | |
| 144 | + const handleConvertToSlides = () => { | |
| 145 | + // Store content in sessionStorage to pass to slides generator page | |
| 146 | + sessionStorage.setItem('slidesContent', JSON.stringify({ question, answer, figureUrls })); | |
| 147 | + setLocation('/slides-generator'); | |
| 148 | + | |
| 149 | + toast({ | |
| 150 | + title: "Redirection...", | |
| 151 | + description: "Ouverture du générateur de slides", | |
| 152 | + }); | |
| 153 | + }; | |
| 154 | + | |
| 155 | + return ( | |
| 156 | + <> | |
| 157 | + <Button | |
| 158 | + variant="outline" | |
| 159 | + size="sm" | |
| 160 | + onClick={handleDownloadMarkdown} | |
| 161 | + className="gap-2 hover:bg-muted transition-colors" | |
| 162 | + disabled={isGeneratingPDF} | |
| 163 | + > | |
| 164 | + <FileText className="w-4 h-4" /> | |
| 165 | + <span className="hidden sm:inline">Télécharger MD</span> | |
| 166 | + </Button> | |
| 167 | + <Button | |
| 168 | + variant="outline" | |
| 169 | + size="sm" | |
| 170 | + onClick={handleDownloadPDF} | |
| 171 | + className="gap-2 hover:bg-muted transition-colors" | |
| 172 | + disabled={isGeneratingPDF} | |
| 173 | + > | |
| 174 | + {isGeneratingPDF ? ( | |
| 175 | + <> | |
| 176 | + <Loader2 className="w-4 h-4 animate-spin" /> | |
| 177 | + <span className="hidden sm:inline">Génération...</span> | |
| 178 | + </> | |
| 179 | + ) : ( | |
| 180 | + <> | |
| 181 | + <FileDown className="w-4 h-4" /> | |
| 182 | + <span className="hidden sm:inline">Télécharger PDF</span> | |
| 183 | + </> | |
| 184 | + )} | |
| 185 | + </Button> | |
| 186 | + <Button | |
| 187 | + variant="outline" | |
| 188 | + size="sm" | |
| 189 | + onClick={handleDownloadDOCX} | |
| 190 | + className="gap-2 hover:bg-muted transition-colors" | |
| 191 | + disabled={isGeneratingDOCX} | |
| 192 | + > | |
| 193 | + {isGeneratingDOCX ? ( | |
| 194 | + <> | |
| 195 | + <Loader2 className="w-4 h-4 animate-spin" /> | |
| 196 | + <span className="hidden sm:inline">Génération...</span> | |
| 197 | + </> | |
| 198 | + ) : ( | |
| 199 | + <> | |
| 200 | + <FileType className="w-4 h-4" /> | |
| 201 | + <span className="hidden sm:inline">Télécharger Word</span> | |
| 202 | + </> | |
| 203 | + )} | |
| 204 | + </Button> | |
| 205 | + <Button | |
| 206 | + variant="outline" | |
| 207 | + size="sm" | |
| 208 | + onClick={handleConvertToSlides} | |
| 209 | + className="gap-2 hover:bg-muted transition-colors" | |
| 210 | + disabled={isGeneratingPDF || isGeneratingDOCX || !question || !answer} | |
| 211 | + > | |
| 212 | + <Presentation className="w-4 h-4" /> | |
| 213 | + <span className="hidden sm:inline">Convertir en Slides</span> | |
| 214 | + </Button> | |
| 215 | + </> | |
| 216 | + ); | |
| 217 | +} | |
added
client/src/components/chat/empty-state.tsx
+422 −0
Large diff (422 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/error-state.tsx
+56 −0
Large diff (56 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/monte-carlo-results.tsx
+226 −0
Large diff (226 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/result-card.tsx
+73 −0
Large diff (73 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/search-bar.tsx
+85 −0
Large diff (85 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/searching-widget.tsx
+106 −0
Large diff (106 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/session-history.tsx
+89 −0
Large diff (89 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/share-button.tsx
+198 −0
Large diff (198 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/thinking-widget.tsx
+64 −0
Large diff (64 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/chat/tool-results.tsx
+30 −0
Large diff (30 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/error-boundary.tsx
+83 −0
Large diff (83 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/analyst-view.tsx
+324 −0
Large diff (324 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/financial-statements-view.tsx
+289 −0
Large diff (289 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/market-overview.tsx
+361 −0
Large diff (361 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/metrics-view.tsx
+324 −0
Large diff (324 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/news-view.tsx
+157 −0
Large diff (157 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/ownership-view.tsx
+207 −0
Large diff (207 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/explore/stock-header.tsx
+293 −0
Large diff (293 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/financial/advanced-price-chart.tsx
+535 −0
Large diff (535 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/financial/options-pricing-card.tsx
+246 −0
Large diff (246 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/market-ticker.tsx
+131 −0
Large diff (131 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/streaming-answer.tsx
+410 −0
Large diff (410 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/theme-color-picker.tsx
+80 −0
Large diff (80 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/theme-provider.tsx
+149 −0
Large diff (149 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/theme-toggle.tsx
+40 −0
Large diff (40 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/accordion.tsx
+72 −0
Large diff (72 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/alert-dialog.tsx
+155 −0
Large diff (155 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/alert.tsx
+75 −0
Large diff (75 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/aspect-ratio.tsx
+21 −0
Large diff (21 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/avatar.tsx
+67 −0
Large diff (67 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/badge.tsx
+56 −0
Large diff (56 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/breadcrumb.tsx
+131 −0
Large diff (131 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/button.tsx
+78 −0
Large diff (78 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/calendar.tsx
+84 −0
Large diff (84 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/card.tsx
+101 −0
Large diff (101 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/carousel.tsx
+276 −0
Large diff (276 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/chart.tsx
+381 −0
Large diff (381 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/checkbox.tsx
+44 −0
Large diff (44 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/collapsible.tsx
+27 −0
Large diff (27 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/command.tsx
+167 −0
Large diff (167 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/context-menu.tsx
+214 −0
Large diff (214 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/dialog.tsx
+138 −0
Large diff (138 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/drawer.tsx
+134 −0
Large diff (134 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/dropdown-menu.tsx
+214 −0
Large diff (214 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/form.tsx
+194 −0
Large diff (194 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/hover-card.tsx
+45 −0
Large diff (45 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/input-otp.tsx
+85 −0
Large diff (85 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/input.tsx
+39 −0
Large diff (39 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/label.tsx
+40 −0
Large diff (40 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/menubar.tsx
+272 −0
Large diff (272 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/navigation-menu.tsx
+144 −0
Large diff (144 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/pagination.tsx
+133 −0
Large diff (133 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/popover.tsx
+45 −0
Large diff (45 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/progress.tsx
+44 −0
Large diff (44 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/radio-group.tsx
+58 −0
Large diff (58 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/resizable.tsx
+61 −0
Large diff (61 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/scroll-area.tsx
+62 −0
Large diff (62 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/select.tsx
+176 −0
Large diff (176 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/separator.tsx
+45 −0
Large diff (45 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/sheet.tsx
+156 −0
Large diff (156 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/sidebar.tsx
+743 −0
Large diff (743 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/skeleton.tsx
+31 −0
Large diff (31 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/slider.tsx
+42 −0
Large diff (42 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/switch.tsx
+43 −0
Large diff (43 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/table.tsx
+133 −0
Large diff (133 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/tabs.tsx
+69 −0
Large diff (69 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/textarea.tsx
+38 −0
Large diff (38 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/toast.tsx
+143 −0
Large diff (143 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/toaster.tsx
+49 −0
Large diff (49 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/toggle-group.tsx
+77 −0
Large diff (77 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/toggle.tsx
+59 −0
Large diff (59 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/components/ui/tooltip.tsx
+46 −0
Large diff (46 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/config.test.ts
+48 −0
Large diff (48 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/config.ts
+67 −0
Large diff (67 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/hooks/use-mobile.tsx
+35 −0
Large diff (35 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/hooks/use-toast.ts
+207 −0
Large diff (207 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/hooks/useAnalytics.ts
+110 −0
Large diff (110 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/hooks/useChatMutation.ts
+114 −0
Large diff (114 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/hooks/useChatSession.test.tsx
+127 −0
Large diff (127 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/hooks/useChatSession.ts
+356 −0
Large diff (356 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/index.css
+541 −0
Large diff (541 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/lib/queryClient.test.ts
+49 −0
Large diff (49 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/lib/queryClient.ts
+74 −0
Large diff (74 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/lib/utils.test.ts
+41 −0
Large diff (41 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/lib/utils.ts
+22 −0
Large diff (22 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/main.tsx
+21 −0
Large diff (21 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/admin.tsx
+791 −0
Large diff (791 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/auth.tsx
+331 −0
Large diff (331 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/documentation.tsx
+730 −0
Large diff (730 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/explore.tsx
+492 −0
Large diff (492 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/home.tsx
+263 −0
Large diff (263 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/not-found.tsx
+37 −0
Large diff (37 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/privacy.tsx
+143 −0
Large diff (143 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/shared-report.tsx
+222 −0
Large diff (222 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/showcase.tsx
+373 −0
Large diff (373 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/slides-generator.tsx
+320 −0
Large diff (320 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/pages/terms.tsx
+197 −0
Large diff (197 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/test/setup.ts
+17 −0
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/utils/chartCapture.ts
+93 −0
Large diff (93 lines) is not loaded by default to keep the page fast.Load diff
added
client/src/utils/pdfGenerator.ts
+655 −0
Large diff (655 lines) is not loaded by default to keep the page fast.Load diff
added
desktop/create-icon.sh
+95 −0
Large diff (95 lines) is not loaded by default to keep the page fast.Load diff
added
desktop/icons/VQuant.iconset/icon_128x128.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_128x128@2x.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_16x16.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_16x16@2x.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_256x256.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_256x256@2x.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_32x32.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_32x32@2x.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_512x512.png
+0 −0
Binary file not shown.
added
desktop/icons/VQuant.iconset/icon_512x512@2x.png
+0 −0
Binary file not shown.
added
desktop/icons/icon.icns
+0 −0
Binary file not shown.
added
desktop/main.js
+652 −0
Large diff (652 lines) is not loaded by default to keep the page fast.Load diff
added
desktop/package-lock.json
+612 −0
Large diff (612 lines) is not loaded by default to keep the page fast.Load diff
Diff truncated — file too large.