SPB Git

spb/spboucher.ai

Public

spboucher.ai — personal website of Simon-Pierre Boucher.

Initial release: spboucher.ai personal website

Multi-page academic + developer site (Next.js 16 App Router, Tailwind CSS v4,
shadcn/ui-style components, Framer Motion): home, research, teaching, Zyquo
macOS apps portfolio, CV, and a health endpoint. Dark mode, responsive,
accessible. Deployed on node m2u64 (PM2, port 3100) behind ngrok at
https://www.spboucher.ai.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Jul 31, 2026) Browse files

33 changed files +3,881 −0

added .gitignore +31 −0
@@ -0,0 +1,31 @@
1 +# .gitignore
2 +# spboucher.ai Web
3 +# Author: Simon-Pierre Boucher
4 +# Mail: contact@spboucher.ai
5 +
6 +# dependencies
7 +/node_modules
8 +
9 +# next.js
10 +/.next/
11 +/out/
12 +
13 +# production
14 +/build
15 +
16 +# misc
17 +.DS_Store
18 +*.pem
19 +
20 +# debug
21 +npm-debug.log*
22 +
23 +# env files
24 +.env*
25 +
26 +# typescript
27 +*.tsbuildinfo
28 +next-env.d.ts
29 +
30 +# local tooling settings
31 +.claude/settings.local.json
added CLAUDE.md +191 −0
@@ -0,0 +1,191 @@
1 +<!--
2 + CLAUDE.md
3 + spboucher.ai — Personal Website
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +-->
7 +
8 +# CLAUDE.md — Personal Website (www.spboucher.ai)
9 +
10 +This file provides guidance to Claude Code when working on this repository.
11 +
12 +## Project Overview
13 +
14 +Personal academic and developer website for **Simon-Pierre Boucher**, Professor in the Department of Administrative Sciences at Université du Québec en Outaouais (UQO). The site presents his academic profile (research, publications, teaching) and a portfolio of macOS applications he develops (the Zyquo suite).
15 +
16 +- **Domain:** www.spboucher.ai
17 +- **Hosting:** Served locally on node `m2u64` and exposed publicly via **ngrok**
18 +- **Stack:** React / Next.js, Tailwind CSS, shadcn/ui, Framer Motion
19 +- **Language of the site:** English
20 +
21 +## Mandatory File Header Convention
22 +
23 +**Every source file created or modified in this project (`.tsx`, `.ts`, `.jsx`, `.js`, `.css`, `.md`, config files, etc.) MUST begin with a header comment in this exact format:**
24 +
25 +```
26 +<!--
27 + FILENAME.ext
28 + spboucher.ai Web
29 + Author: Simon-Pierre Boucher
30 + Mail: contact@spboucher.ai
31 +-->
32 +```
33 +
34 +Adapt the comment syntax to the file type:
35 +
36 +- **HTML / Markdown / MDX:** `<!-- ... -->`
37 +- **TS / TSX / JS / JSX / CSS:**
38 + ```ts
39 + /*
40 + FILENAME.tsx
41 + spboucher.ai Web
42 + Author: Simon-Pierre Boucher
43 + Mail: contact@spboucher.ai
44 + */
45 + ```
46 +- **Shell / YAML / Python:** `#` prefixed lines with the same four fields.
47 +
48 +Never omit this header. When editing an existing file that lacks it, add it.
49 +
50 +## Dev & Deployment
51 +
52 +```bash
53 +npm install # install dependencies
54 +npm run dev # local dev server (default: http://localhost:3000)
55 +npm run build # production build
56 +npm run start # serve production build
57 +```
58 +
59 +### ngrok exposure (node m2u64)
60 +
61 +The site runs on node `m2u64` and is exposed with ngrok bound to the custom domain:
62 +
63 +```bash
64 +ngrok http --domain=www.spboucher.ai 3000
65 +```
66 +
67 +- Keep the production build (`npm run build && npm run start`) behind ngrok, not the dev server.
68 +- If the tunnel drops, restart the ngrok command above on m2u64.
69 +
70 +## Site Structure
71 +
72 +```
73 +/ Home (hero, bio, highlights)
74 +/research Publications, working papers, presentations
75 +/teaching Courses taught
76 +/apps Zyquo macOS app portfolio
77 +/cv CV summary + contact
78 +```
79 +
80 +## Homepage Content (use this copy)
81 +
82 +### Hero
83 +
84 +- **Name:** Simon-Pierre Boucher
85 +- **Title:** Professor — Department of Administrative Sciences
86 +- **Institution:** Université du Québec en Outaouais (UQO), Gatineau — Pavillon Alexandre-Taché
87 +- **Tagline suggestion:** "Financial econometrics researcher and macOS/AI developer."
88 +- **Contact:**
89 + - Academic: simon-pierre.boucher@uqo.ca
90 + - Personal: contact@spboucher.ai
91 + - GitHub: https://github.com/spboucher-ai
92 +- **Languages:** Bilingual — French and English
93 +
94 +### About / Bio
95 +
96 +Simon-Pierre Boucher is a Professor in the Department of Administrative Sciences at Université du Québec en Outaouais (UQO). His research focuses on financial econometrics, commodity markets, monetary policy announcements, high-frequency finance, volatility modelling, textual analysis, and financialization. He is completing a Ph.D. in Business Administration (Finance and Insurance) at Université Laval under the supervision of Prof. Marie-Hélène Gagnon and Prof. Gabriel Power. Alongside his academic work, he builds native macOS applications focused on AI and local LLMs (the Zyquo suite).
97 +
98 +### Education
99 +
100 +- **Ph.D. in Business Administration (Finance and Insurance)** — Université Laval, 2020–Present
101 +- **M.Sc. in Business Administration (Finance)** — Université Laval, 2017–2019 (Thesis: *Impact of Commuting Times on Residential Property Values: Evidence from the Province of Québec*)
102 +- **B.B.A. in Business Administration (Finance)** — Université Laval, 2013–2017
103 +
104 +### Research Highlights
105 +
106 +**Publication:**
107 +- Boucher, S.-P., Gagnon, M.-H., & Power, G. J. (2025). *Speculative Trading in Energy Markets: Evidence from Macroeconomic Surprises.* The Energy Journal.
108 +
109 +**Working papers:**
110 +1. *Has Financialization Changed the Impact of Macro Announcements on U.S. Commodity Markets?* — SSRN Working Paper, May 2022 — https://ssrn.com/abstract=400929
111 +2. *Modelling Volatility Dynamics Between Commodity ETFs and Their Net Asset Value using BVAR and HAR Models* — January 2023
112 +3. *Returns and Volatility Around FOMC Announcements: A High-Frequency Analysis of Policy Tone and Novelty* — November 2023
113 +
114 +**Conference presentations:** CRREP Research Day (2022, 2023); Canadian Economics Association 61st & 62nd Annual Conferences (2022, 2023); 7th Winter Workshop on Commodity Markets, Mont-Tremblant (2024).
115 +
116 +### Teaching (Université Laval — Lecturer, 2021–Present)
117 +
118 +| Code | Course | Level | Terms |
119 +|------|--------|-------|-------|
120 +| GSF-3100 | Capital Markets | Undergraduate | F2021, W2021, F2022, W2023 |
121 +| GSF-6053 | Financial Econometrics I | Graduate | W2022, W2025 |
122 +| GSF-1500 | Financial Management | Undergraduate | S2022 |
123 +| GSF-6028 | Financial Theory | Graduate | W2024 |
124 +
125 +Teaching Assistant at Université Laval (2018–2021): GSF-2101 Portfolio Management, GSF-2102 Corporate Finance, GSF-6008 Corporate Finance, GSF-6025 Financial Strategies & Policies I, GSF-6028 Financial Theory.
126 +
127 +### Technical Skills (for a skills section)
128 +
129 +- **Languages & frameworks:** Python, TypeScript/JavaScript, R, MATLAB, SAS/STATA, SQL, Julia, C++, Swift/SwiftUI, LaTeX, React/Next.js, Node.js, FastAPI, Flask, Streamlit, Tailwind CSS, shadcn/ui, Framer Motion
130 +- **Data & econometrics:** Pandas, NumPy, statsmodels, scikit-learn; VAR, BVAR, HAR, GARCH, DCC-GARCH; event studies, high-frequency analysis, realized volatility; FRED, FMP, EODHD, Yahoo Finance, World Bank, IMF, OECD
131 +- **AI, LLMs & agents:** Claude/Anthropic SDK, OpenAI API, OpenRouter, Vercel AI SDK, Ollama, llama.cpp, GGUF, Hugging Face; embeddings, RAG, vector databases; tool-augmented agents; LoRA, QLoRA, SFT fine-tuning
132 +
133 +## Apps Page — Zyquo macOS Suite (REQUIRED)
134 +
135 +The `/apps` page is a portfolio of six native macOS applications. **Each app card MUST display: the app icon, the name, a short description, and three buttons/links (GitHub Repo, Release, Download DMG).**
136 +
137 +App icons: store them in `/public/icons/` (e.g. `/public/icons/zyquo-cloud.png`). If an icon file is missing, fetch it from the corresponding GitHub repo (app icon asset) or use a styled placeholder with the app's emoji until the real icon is added — but the icon slot must always be present in the layout.
138 +
139 +### 1. ☁️ Zyquo Cloud — Multi-provider AI chat
140 +Native macOS chat client supporting multiple AI providers (Anthropic, OpenAI, and more) in a single unified interface.
141 +- Repo: https://github.com/spboucher-ai/zyquo-cloud
142 +- Release: https://github.com/spboucher-ai/zyquo-cloud/releases/tag/v1.0.0
143 +- DMG: https://github.com/spboucher-ai/zyquo-cloud/releases/latest/download/ZyquoCloud.dmg
144 +
145 +### 2. 💻 Zyquo Local — 100% local LLMs (MLX)
146 +Run large language models entirely on-device with Apple MLX. Private, offline, no API keys required.
147 +- Repo: https://github.com/spboucher-ai/zyquo-local
148 +- Release: https://github.com/spboucher-ai/zyquo-local/releases/tag/v1.0.0
149 +- DMG: https://github.com/spboucher-ai/zyquo-local/releases/latest/download/ZyquoLocal.dmg
150 +
151 +### 3. 🤖 Zyquo Agent — Autonomous agent for the Mac
152 +An autonomous AI agent that can act on your Mac: tool calling, task automation, and multi-step workflows.
153 +- Repo: https://github.com/spboucher-ai/zyquo-agent
154 +- Release: https://github.com/spboucher-ai/zyquo-agent/releases/tag/v1.0.0
155 +- DMG: https://github.com/spboucher-ai/zyquo-agent/releases/latest/download/ZyquoAgent.dmg
156 +
157 +### 4. 🌍 Zyquo Atlas — AI-native web browser
158 +A web browser built around AI from the ground up: page understanding, summarization, and assisted browsing.
159 +- Repo: https://github.com/spboucher-ai/zyquo-atlas
160 +- Release: https://github.com/spboucher-ai/zyquo-atlas/releases/tag/v1.0.0
161 +- DMG: https://github.com/spboucher-ai/zyquo-atlas/releases/latest/download/ZyquoAtlas.dmg
162 +
163 +### 5. ⚒️ Zyquo MLX — On-device model forge
164 +Fine-tune and quantize models directly on Apple Silicon: LoRA/QLoRA fine-tuning, quantization, and model management with MLX.
165 +- Repo: https://github.com/spboucher-ai/zyquo-mlx
166 +- Release: https://github.com/spboucher-ai/zyquo-mlx/releases/tag/v1.0.0
167 +- DMG: https://github.com/spboucher-ai/zyquo-mlx/releases/latest/download/ZyquoMLX.dmg
168 +
169 +### 6. 🔀 Zyquo Router — Local LLM gateway
170 +A local gateway that routes requests across LLM providers and local models through a single unified endpoint.
171 +- Repo: https://github.com/spboucher-ai/zyquo-router
172 +- Release: https://github.com/spboucher-ai/zyquo-router/releases/tag/v1.0.0
173 +- DMG: https://github.com/spboucher-ai/zyquo-router/releases/latest/download/ZyquoRouter.dmg
174 +
175 +## Design Guidelines
176 +
177 +- Clean, professional academic aesthetic with a modern developer edge.
178 +- Dark mode support.
179 +- Use Tailwind CSS + shadcn/ui components; Framer Motion for subtle animations only.
180 +- Fully responsive (mobile-first).
181 +- App cards on `/apps` in a responsive grid (1 col mobile, 2 cols tablet, 3 cols desktop).
182 +- Semantic HTML, accessible (alt text on all app icons).
183 +
184 +## Rules for Claude
185 +
186 +1. **Always add the file header comment** (see "Mandatory File Header Convention") at the top of every file you create or edit.
187 +2. The entire site content is in **English**.
188 +3. Never remove or alter the app download links (Repo / Release / DMG) — they must remain exactly as listed above.
189 +4. Every Zyquo app card must include its icon and description.
190 +5. Keep the ngrok/m2u64 deployment instructions in the README up to date if ports or commands change.
191 +6. Prefer server components (Next.js App Router) where possible; client components only when interactivity requires it.
added README.md +171 −0
@@ -0,0 +1,171 @@
1 +<!--
2 + README.md
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +-->
7 +
8 +<div align="center">
9 +
10 +# 🎓 spboucher.ai
11 +
12 +### Personal website of **Simon-Pierre Boucher**
13 +
14 +*Financial econometrics researcher and macOS/AI developer.*
15 +
16 +Professor — Department of Administrative Sciences, Université du Québec en Outaouais (UQO)
17 +
18 +<br/>
19 +
20 +[![Website](https://img.shields.io/website?url=https%3A%2F%2Fwww.spboucher.ai&up_message=online&down_message=offline&label=www.spboucher.ai&style=for-the-badge)](https://www.spboucher.ai)
21 +[![Next.js](https://img.shields.io/badge/Next.js-16-000000?style=for-the-badge&logo=nextdotjs&logoColor=white)](https://nextjs.org)
22 +[![React](https://img.shields.io/badge/React-19-61DAFB?style=for-the-badge&logo=react&logoColor=black)](https://react.dev)
23 +[![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org)
24 +
25 +[![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-4-06B6D4?style=flat-square&logo=tailwindcss&logoColor=white)](https://tailwindcss.com)
26 +[![shadcn/ui](https://img.shields.io/badge/shadcn%2Fui-components-18181B?style=flat-square)](https://ui.shadcn.com)
27 +[![Framer Motion](https://img.shields.io/badge/Framer_Motion-12-0055FF?style=flat-square&logo=framer&logoColor=white)](https://www.framer.com/motion/)
28 +[![PM2](https://img.shields.io/badge/PM2-managed-2B037A?style=flat-square&logo=pm2&logoColor=white)](https://pm2.keymetrics.io)
29 +[![ngrok](https://img.shields.io/badge/ngrok-tunnel-1F1E37?style=flat-square&logo=ngrok&logoColor=white)](https://ngrok.com)
30 +[![Made in Québec](https://img.shields.io/badge/Made_in-Québec_🇨🇦-1E40AF?style=flat-square)](https://uqo.ca)
31 +
32 +<br/>
33 +
34 +**[🏠 Home](https://www.spboucher.ai)** ·
35 +**[🔬 Research](https://www.spboucher.ai/research)** ·
36 +**[📚 Teaching](https://www.spboucher.ai/teaching)** ·
37 +**[🖥️ Apps](https://www.spboucher.ai/apps)** ·
38 +**[📄 CV](https://www.spboucher.ai/cv)**
39 +
40 +</div>
41 +
42 +---
43 +
44 +## 👋 About
45 +
46 +This repository powers **[www.spboucher.ai](https://www.spboucher.ai)** — the personal academic and developer website of Simon-Pierre Boucher. It presents his academic profile (research in financial econometrics, commodity markets, monetary policy announcements, high-frequency finance, volatility modelling, textual analysis, and financialization) alongside the **Zyquo suite**: six native macOS applications focused on AI and local LLMs.
47 +
48 +## ✨ Features
49 +
50 +- ⚡ **Next.js 16 App Router** — server components everywhere, static prerendering on every page
51 +- 🎨 **Tailwind CSS v4 + shadcn/ui-style components** — clean academic aesthetic with a modern developer edge
52 +- 🌗 **Dark mode** — system-aware with a manual toggle, no flash on load
53 +- 🎬 **Framer Motion** — subtle scroll reveals, with `prefers-reduced-motion` and no-JS fallbacks
54 +- 📱 **Fully responsive** — mobile-first, 1/2/3-column app grid
55 +- ♿ **Accessible** — semantic HTML, alt text on all app icons, keyboard-friendly nav
56 +- 💚 **Health endpoint** — `GET /api/health` for uptime monitoring
57 +
58 +## 🗺️ Site Map
59 +
60 +| Route | Content |
61 +|-------|---------|
62 +| `/` | Hero, bio, education, research highlight, technical skills |
63 +| `/research` | Publications, working papers, conference presentations |
64 +| `/teaching` | Courses taught (lecturer & teaching assistant, Université Laval) |
65 +| `/apps` | Zyquo macOS app portfolio |
66 +| `/cv` | CV summary + contact |
67 +| `/api/health` | JSON health check |
68 +
69 +## 🖥️ The Zyquo macOS Suite
70 +
71 +Six native macOS applications for AI and local LLMs — Swift/SwiftUI, built for Apple Silicon.
72 +
73 +| | App | Description | Links |
74 +|---|-----|-------------|-------|
75 +| <img src="public/icons/zyquo-cloud.png" width="48" alt="Zyquo Cloud icon"/> | **☁️ Zyquo Cloud** | Multi-provider AI chat — Anthropic, OpenAI, and more in one unified interface | [Repo](https://github.com/spboucher-ai/zyquo-cloud) · [Release](https://github.com/spboucher-ai/zyquo-cloud/releases/tag/v1.0.0) · [DMG](https://github.com/spboucher-ai/zyquo-cloud/releases/latest/download/ZyquoCloud.dmg) |
76 +| <img src="public/icons/zyquo-local.png" width="48" alt="Zyquo Local icon"/> | **💻 Zyquo Local** | 100% local LLMs with Apple MLX — private, offline, no API keys | [Repo](https://github.com/spboucher-ai/zyquo-local) · [Release](https://github.com/spboucher-ai/zyquo-local/releases/tag/v1.0.0) · [DMG](https://github.com/spboucher-ai/zyquo-local/releases/latest/download/ZyquoLocal.dmg) |
77 +| <img src="public/icons/zyquo-agent.png" width="48" alt="Zyquo Agent icon"/> | **🤖 Zyquo Agent** | Autonomous AI agent for the Mac — tool calling, automation, multi-step workflows | [Repo](https://github.com/spboucher-ai/zyquo-agent) · [Release](https://github.com/spboucher-ai/zyquo-agent/releases/tag/v1.0.0) · [DMG](https://github.com/spboucher-ai/zyquo-agent/releases/latest/download/ZyquoAgent.dmg) |
78 +| <img src="public/icons/zyquo-atlas.png" width="48" alt="Zyquo Atlas icon"/> | **🌍 Zyquo Atlas** | AI-native web browser — page understanding, summarization, assisted browsing | [Repo](https://github.com/spboucher-ai/zyquo-atlas) · [Release](https://github.com/spboucher-ai/zyquo-atlas/releases/tag/v1.0.0) · [DMG](https://github.com/spboucher-ai/zyquo-atlas/releases/latest/download/ZyquoAtlas.dmg) |
79 +| <img src="public/icons/zyquo-mlx.png" width="48" alt="Zyquo MLX icon"/> | **⚒️ Zyquo MLX** | On-device model forge — LoRA/QLoRA fine-tuning, quantization, model management | [Repo](https://github.com/spboucher-ai/zyquo-mlx) · [Release](https://github.com/spboucher-ai/zyquo-mlx/releases/tag/v1.0.0) · [DMG](https://github.com/spboucher-ai/zyquo-mlx/releases/latest/download/ZyquoMLX.dmg) |
80 +| <img src="public/icons/zyquo-router.png" width="48" alt="Zyquo Router icon"/> | **🔀 Zyquo Router** | Local LLM gateway — one unified endpoint across providers and local models | [Repo](https://github.com/spboucher-ai/zyquo-router) · [Release](https://github.com/spboucher-ai/zyquo-router/releases/tag/v1.0.0) · [DMG](https://github.com/spboucher-ai/zyquo-router/releases/latest/download/ZyquoRouter.dmg) |
81 +
82 +## 🚀 Quick Start
83 +
84 +```bash
85 +git clone https://github.com/spboucher-ai/spboucher.ai.git
86 +cd spboucher.ai
87 +
88 +npm install # install dependencies
89 +npm run dev # local dev server → http://localhost:3000
90 +npm run build # production build
91 +npm run start # serve production build
92 +npm run typecheck # TypeScript check
93 +```
94 +
95 +## 🌐 Production Deployment (node m2u64)
96 +
97 +The site runs on cluster node `m2u64` and is exposed publicly with **ngrok** bound to the custom domain. On m2u64 the app listens on **port 3100** (port 3000 is already in use on that node); both the app and the tunnel run under **PM2** for auto-restart.
98 +
99 +```mermaid
100 +flowchart LR
101 + A["🌍 Visitor"] --> B["🔒 ngrok edge<br/>www.spboucher.ai"]
102 + B --> C["🖥️ m2u64 : 3100<br/>next start (PM2: spboucher)"]
103 + C --> D["💚 /api/health"]
104 +```
105 +
106 +```bash
107 +# on m2u64
108 +npm ci && npm run build
109 +pm2 start scripts/start-production.sh --name spboucher --interpreter /bin/zsh
110 +pm2 start "ngrok http --url=www.spboucher.ai 3100" --name spboucher-ngrok
111 +pm2 save
112 +```
113 +
114 +- Always serve the production build (`npm run build && npm run start`) behind ngrok — never the dev server.
115 +- If the tunnel drops, restart it: `pm2 restart spboucher-ngrok` (or run `ngrok http --url=www.spboucher.ai 3100` manually).
116 +- Health check: `GET /api/health` → `{"status":"ok","service":"spboucher-ai"}`.
117 +
118 +## 📁 Project Structure
119 +
120 +```
121 +app/
122 +├── layout.tsx Root layout (theme init, header, footer)
123 +├── page.tsx Home — hero, bio, education, highlight, skills
124 +├── research/page.tsx Publications, working papers, presentations
125 +├── teaching/page.tsx Courses taught
126 +├── apps/page.tsx Zyquo macOS app portfolio
127 +├── cv/page.tsx CV summary + contact
128 +├── api/health/route.ts Health check endpoint
129 +└── globals.css Tailwind v4 theme tokens (light/dark)
130 +
131 +components/
132 +├── site-header.tsx Sticky nav + mobile menu
133 +├── site-footer.tsx Footer
134 +├── theme-toggle.tsx Dark mode toggle
135 +├── reveal.tsx Framer Motion scroll reveal
136 +├── app-card.tsx Zyquo app card (icon, description, 3 links)
137 +└── ui/ shadcn/ui-style primitives (button, card, badge)
138 +
139 +lib/
140 +├── apps.ts Zyquo suite data (single source of truth)
141 +└── utils.ts cn() class merger
142 +
143 +public/icons/ Zyquo app icons (fetched from the app repos)
144 +scripts/ Production start script (PM2)
145 +```
146 +
147 +## 📐 Conventions
148 +
149 +- 📝 Every source file starts with the mandatory project header comment (see `CLAUDE.md`)
150 +- 🇬🇧 All site content is in English
151 +- 🔗 App download links on `/apps` (Repo / Release / DMG) must remain exactly as specified in `CLAUDE.md`
152 +- 🧩 Server components by default; client components only where interactivity requires it
153 +
154 +## 📫 Contact
155 +
156 +| | |
157 +|---|---|
158 +| 🎓 Academic | [simon-pierre.boucher@uqo.ca](mailto:simon-pierre.boucher@uqo.ca) |
159 +| ✉️ Personal | [contact@spboucher.ai](mailto:contact@spboucher.ai) |
160 +| 🐙 GitHub | [@spboucher-ai](https://github.com/spboucher-ai) |
161 +| 📍 Office | UQO, Gatineau — Pavillon Alexandre-Taché |
162 +
163 +---
164 +
165 +<div align="center">
166 +
167 +**© Simon-Pierre Boucher** — Université du Québec en Outaouais (UQO)
168 +
169 +*Built with Next.js · Tailwind CSS · shadcn/ui · Framer Motion — served from a Mac, tunneled to the world.* 🚇
170 +
171 +</div>
added app/api/health/route.ts +12 −0
@@ -0,0 +1,12 @@
1 +/*
2 + route.ts
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import { NextResponse } from "next/server";
9 +
10 +export function GET() {
11 + return NextResponse.json({ status: "ok", service: "spboucher-ai" });
12 +}
added app/apps/page.tsx +42 −0
@@ -0,0 +1,42 @@
1 +/*
2 + page.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import type { Metadata } from "next";
9 +
10 +import { AppCard } from "@/components/app-card";
11 +import { Reveal } from "@/components/reveal";
12 +import { zyquoApps } from "@/lib/apps";
13 +
14 +export const metadata: Metadata = {
15 + title: "Apps — Zyquo macOS Suite",
16 + description:
17 + "The Zyquo suite: six native macOS applications for AI and local LLMs — multi-provider chat, on-device MLX models, autonomous agents, an AI-native browser, model fine-tuning, and LLM routing.",
18 +};
19 +
20 +export default function AppsPage() {
21 + return (
22 + <div className="mx-auto max-w-5xl px-4 py-16 sm:px-6">
23 + <Reveal>
24 + <h1 className="text-3xl font-bold tracking-tight sm:text-4xl">
25 + Zyquo macOS Suite
26 + </h1>
27 + <p className="mt-3 max-w-2xl text-muted-foreground">
28 + Six native macOS applications focused on AI and local LLMs — built
29 + with Swift/SwiftUI for Apple Silicon.
30 + </p>
31 + </Reveal>
32 +
33 + <div className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
34 + {zyquoApps.map((app, i) => (
35 + <Reveal key={app.slug} delay={i * 0.06} className="h-full">
36 + <AppCard app={app} />
37 + </Reveal>
38 + ))}
39 + </div>
40 + </div>
41 + );
42 +}
added app/cv/page.tsx +177 −0
@@ -0,0 +1,177 @@
1 +/*
2 + page.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import type { Metadata } from "next";
9 +import { Github, Mail, MapPin } from "lucide-react";
10 +
11 +import { Badge } from "@/components/ui/badge";
12 +import {
13 + Card,
14 + CardContent,
15 + CardDescription,
16 + CardHeader,
17 + CardTitle,
18 +} from "@/components/ui/card";
19 +import { Reveal } from "@/components/reveal";
20 +
21 +export const metadata: Metadata = {
22 + title: "CV",
23 + description:
24 + "CV summary and contact information for Simon-Pierre Boucher — Professor, Department of Administrative Sciences, Université du Québec en Outaouais (UQO).",
25 +};
26 +
27 +const positions = [
28 + {
29 + role: "Professor — Department of Administrative Sciences",
30 + org: "Université du Québec en Outaouais (UQO), Gatineau",
31 + period: "Present",
32 + },
33 + {
34 + role: "Lecturer",
35 + org: "Université Laval",
36 + period: "2021–Present",
37 + },
38 + {
39 + role: "Teaching Assistant",
40 + org: "Université Laval",
41 + period: "2018–2021",
42 + },
43 +];
44 +
45 +const education = [
46 + {
47 + degree: "Ph.D. in Business Administration (Finance and Insurance)",
48 + school: "Université Laval",
49 + period: "2020–Present",
50 + },
51 + {
52 + degree: "M.Sc. in Business Administration (Finance)",
53 + school: "Université Laval",
54 + period: "2017–2019",
55 + },
56 + {
57 + degree: "B.B.A. in Business Administration (Finance)",
58 + school: "Université Laval",
59 + period: "2013–2017",
60 + },
61 +];
62 +
63 +export default function CvPage() {
64 + return (
65 + <div className="mx-auto max-w-5xl px-4 py-16 sm:px-6">
66 + <Reveal>
67 + <h1 className="text-3xl font-bold tracking-tight sm:text-4xl">CV</h1>
68 + <p className="mt-3 max-w-2xl text-muted-foreground">
69 + Professor in the Department of Administrative Sciences at Université
70 + du Québec en Outaouais (UQO). Research in financial econometrics,
71 + commodity markets, monetary policy announcements, high-frequency
72 + finance, volatility modelling, textual analysis, and
73 + financialization.
74 + </p>
75 + </Reveal>
76 +
77 + {/* Contact */}
78 + <section className="mt-12">
79 + <Reveal>
80 + <h2 className="text-xl font-semibold tracking-tight">Contact</h2>
81 + <Card className="mt-4">
82 + <CardContent className="grid gap-3 p-6 text-sm sm:grid-cols-2">
83 + <a
84 + href="mailto:simon-pierre.boucher@uqo.ca"
85 + className="inline-flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"
86 + >
87 + <Mail className="h-4 w-4 text-primary" aria-hidden="true" />
88 + simon-pierre.boucher@uqo.ca (academic)
89 + </a>
90 + <a
91 + href="mailto:contact@spboucher.ai"
92 + className="inline-flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"
93 + >
94 + <Mail className="h-4 w-4 text-primary" aria-hidden="true" />
95 + contact@spboucher.ai (personal)
96 + </a>
97 + <a
98 + href="https://github.com/spboucher-ai"
99 + target="_blank"
100 + rel="noopener noreferrer"
101 + className="inline-flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"
102 + >
103 + <Github className="h-4 w-4 text-primary" aria-hidden="true" />
104 + github.com/spboucher-ai
105 + </a>
106 + <span className="inline-flex items-center gap-2 text-muted-foreground">
107 + <MapPin className="h-4 w-4 text-primary" aria-hidden="true" />
108 + UQO, Gatineau — Pavillon Alexandre-Taché
109 + </span>
110 + </CardContent>
111 + </Card>
112 + </Reveal>
113 + </section>
114 +
115 + {/* Positions */}
116 + <section className="mt-12">
117 + <Reveal>
118 + <h2 className="text-xl font-semibold tracking-tight">Positions</h2>
119 + </Reveal>
120 + <div className="mt-4 space-y-4">
121 + {positions.map((p, i) => (
122 + <Reveal key={p.role} delay={i * 0.06}>
123 + <Card>
124 + <CardHeader className="flex-row items-start justify-between space-y-0">
125 + <div>
126 + <CardTitle className="text-base leading-snug">
127 + {p.role}
128 + </CardTitle>
129 + <CardDescription className="mt-1">{p.org}</CardDescription>
130 + </div>
131 + <Badge variant="outline">{p.period}</Badge>
132 + </CardHeader>
133 + </Card>
134 + </Reveal>
135 + ))}
136 + </div>
137 + </section>
138 +
139 + {/* Education */}
140 + <section className="mt-12">
141 + <Reveal>
142 + <h2 className="text-xl font-semibold tracking-tight">Education</h2>
143 + </Reveal>
144 + <div className="mt-4 space-y-4">
145 + {education.map((item, i) => (
146 + <Reveal key={item.degree} delay={i * 0.06}>
147 + <Card>
148 + <CardHeader className="flex-row items-start justify-between space-y-0">
149 + <div>
150 + <CardTitle className="text-base leading-snug">
151 + {item.degree}
152 + </CardTitle>
153 + <CardDescription className="mt-1">
154 + {item.school}
155 + </CardDescription>
156 + </div>
157 + <Badge variant="outline">{item.period}</Badge>
158 + </CardHeader>
159 + </Card>
160 + </Reveal>
161 + ))}
162 + </div>
163 + </section>
164 +
165 + {/* Languages */}
166 + <section className="mt-12">
167 + <Reveal>
168 + <h2 className="text-xl font-semibold tracking-tight">Languages</h2>
169 + <div className="mt-4 flex gap-2">
170 + <Badge variant="secondary">French — Bilingual</Badge>
171 + <Badge variant="secondary">English — Bilingual</Badge>
172 + </div>
173 + </Reveal>
174 + </section>
175 + </div>
176 + );
177 +}
added app/globals.css +77 −0
@@ -0,0 +1,77 @@
1 +/*
2 + globals.css
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +@import "tailwindcss";
9 +
10 +@custom-variant dark (&:where(.dark, .dark *));
11 +
12 +:root {
13 + --background: oklch(0.985 0.002 247.839);
14 + --foreground: oklch(0.208 0.042 265.755);
15 + --card: oklch(1 0 0);
16 + --card-foreground: oklch(0.208 0.042 265.755);
17 + --muted: oklch(0.968 0.007 247.896);
18 + --muted-foreground: oklch(0.446 0.043 257.281);
19 + --primary: oklch(0.488 0.243 264.376);
20 + --primary-foreground: oklch(0.984 0.003 247.858);
21 + --secondary: oklch(0.929 0.013 255.508);
22 + --secondary-foreground: oklch(0.208 0.042 265.755);
23 + --accent: oklch(0.929 0.013 255.508);
24 + --accent-foreground: oklch(0.208 0.042 265.755);
25 + --border: oklch(0.869 0.022 252.894);
26 + --ring: oklch(0.488 0.243 264.376);
27 +}
28 +
29 +.dark {
30 + --background: oklch(0.179 0.025 265.755);
31 + --foreground: oklch(0.929 0.013 255.508);
32 + --card: oklch(0.218 0.028 265.755);
33 + --card-foreground: oklch(0.929 0.013 255.508);
34 + --muted: oklch(0.255 0.03 262.881);
35 + --muted-foreground: oklch(0.704 0.04 256.788);
36 + --primary: oklch(0.707 0.165 254.624);
37 + --primary-foreground: oklch(0.179 0.025 265.755);
38 + --secondary: oklch(0.279 0.041 260.031);
39 + --secondary-foreground: oklch(0.929 0.013 255.508);
40 + --accent: oklch(0.279 0.041 260.031);
41 + --accent-foreground: oklch(0.929 0.013 255.508);
42 + --border: oklch(0.323 0.038 262.881);
43 + --ring: oklch(0.707 0.165 254.624);
44 +}
45 +
46 +@theme inline {
47 + --color-background: var(--background);
48 + --color-foreground: var(--foreground);
49 + --color-card: var(--card);
50 + --color-card-foreground: var(--card-foreground);
51 + --color-muted: var(--muted);
52 + --color-muted-foreground: var(--muted-foreground);
53 + --color-primary: var(--primary);
54 + --color-primary-foreground: var(--primary-foreground);
55 + --color-secondary: var(--secondary);
56 + --color-secondary-foreground: var(--secondary-foreground);
57 + --color-accent: var(--accent);
58 + --color-accent-foreground: var(--accent-foreground);
59 + --color-border: var(--border);
60 + --color-ring: var(--ring);
61 + --font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
62 +}
63 +
64 +body {
65 + background-color: var(--background);
66 + color: var(--foreground);
67 + font-family: var(--font-sans);
68 +}
69 +
70 +/* Framer Motion reveals bake opacity:0 into SSR inline styles; guarantee
71 + visibility when animations cannot run (reduced motion or JS disabled). */
72 +@media (prefers-reduced-motion: reduce) {
73 + [data-reveal] {
74 + opacity: 1 !important;
75 + transform: none !important;
76 + }
77 +}
added app/layout.tsx +71 −0
@@ -0,0 +1,71 @@
1 +/*
2 + layout.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import type { Metadata } from "next";
9 +import { Inter } from "next/font/google";
10 +
11 +import { SiteHeader } from "@/components/site-header";
12 +import { SiteFooter } from "@/components/site-footer";
13 +
14 +import "./globals.css";
15 +
16 +const inter = Inter({
17 + subsets: ["latin"],
18 + variable: "--font-inter",
19 +});
20 +
21 +export const metadata: Metadata = {
22 + metadataBase: new URL("https://www.spboucher.ai"),
23 + title: {
24 + default: "Simon-Pierre Boucher — Professor & macOS/AI Developer",
25 + template: "%s — Simon-Pierre Boucher",
26 + },
27 + description:
28 + "Simon-Pierre Boucher is a Professor in the Department of Administrative Sciences at Université du Québec en Outaouais (UQO). Financial econometrics researcher and macOS/AI developer.",
29 + openGraph: {
30 + title: "Simon-Pierre Boucher",
31 + description:
32 + "Financial econometrics researcher and macOS/AI developer. Professor at Université du Québec en Outaouais (UQO).",
33 + url: "https://www.spboucher.ai",
34 + siteName: "Simon-Pierre Boucher",
35 + locale: "en_US",
36 + type: "website",
37 + },
38 +};
39 +
40 +// Applies the saved (or system) theme before first paint to avoid a flash
41 +// of the wrong color scheme.
42 +const themeInitScript = `
43 +try {
44 + var t = localStorage.getItem("theme");
45 + if (t === "dark" || (!t && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
46 + document.documentElement.classList.add("dark");
47 + }
48 +} catch (e) {}
49 +`;
50 +
51 +export default function RootLayout({
52 + children,
53 +}: Readonly<{
54 + children: React.ReactNode;
55 +}>) {
56 + return (
57 + <html lang="en" suppressHydrationWarning>
58 + <head>
59 + <script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
60 + <noscript>
61 + <style>{`[data-reveal]{opacity:1!important;transform:none!important}`}</style>
62 + </noscript>
63 + </head>
64 + <body className={`${inter.variable} flex min-h-screen flex-col antialiased`}>
65 + <SiteHeader />
66 + <main className="flex-1">{children}</main>
67 + <SiteFooter />
68 + </body>
69 + </html>
70 + );
71 +}
added app/page.tsx +283 −0
@@ -0,0 +1,283 @@
1 +/*
2 + page.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import Link from "next/link";
9 +import { ArrowRight, Github, GraduationCap, Mail } from "lucide-react";
10 +
11 +import { Badge } from "@/components/ui/badge";
12 +import { buttonVariants } from "@/components/ui/button";
13 +import {
14 + Card,
15 + CardContent,
16 + CardDescription,
17 + CardHeader,
18 + CardTitle,
19 +} from "@/components/ui/card";
20 +import { Reveal } from "@/components/reveal";
21 +import { cn } from "@/lib/utils";
22 +
23 +const education = [
24 + {
25 + degree: "Ph.D. in Business Administration (Finance and Insurance)",
26 + school: "Université Laval",
27 + period: "2020–Present",
28 + },
29 + {
30 + degree: "M.Sc. in Business Administration (Finance)",
31 + school: "Université Laval",
32 + period: "2017–2019",
33 + note: "Thesis: Impact of Commuting Times on Residential Property Values: Evidence from the Province of Québec",
34 + },
35 + {
36 + degree: "B.B.A. in Business Administration (Finance)",
37 + school: "Université Laval",
38 + period: "2013–2017",
39 + },
40 +];
41 +
42 +const skillGroups = [
43 + {
44 + title: "Languages & frameworks",
45 + skills: [
46 + "Python",
47 + "TypeScript/JavaScript",
48 + "R",
49 + "MATLAB",
50 + "SAS/STATA",
51 + "SQL",
52 + "Julia",
53 + "C++",
54 + "Swift/SwiftUI",
55 + "LaTeX",
56 + "React/Next.js",
57 + "Node.js",
58 + "FastAPI",
59 + "Flask",
60 + "Streamlit",
61 + "Tailwind CSS",
62 + "shadcn/ui",
63 + "Framer Motion",
64 + ],
65 + },
66 + {
67 + title: "Data & econometrics",
68 + skills: [
69 + "Pandas",
70 + "NumPy",
71 + "statsmodels",
72 + "scikit-learn",
73 + "VAR",
74 + "BVAR",
75 + "HAR",
76 + "GARCH",
77 + "DCC-GARCH",
78 + "Event studies",
79 + "High-frequency analysis",
80 + "Realized volatility",
81 + "FRED",
82 + "FMP",
83 + "EODHD",
84 + "Yahoo Finance",
85 + "World Bank",
86 + "IMF",
87 + "OECD",
88 + ],
89 + },
90 + {
91 + title: "AI, LLMs & agents",
92 + skills: [
93 + "Claude/Anthropic SDK",
94 + "OpenAI API",
95 + "OpenRouter",
96 + "Vercel AI SDK",
97 + "Ollama",
98 + "llama.cpp",
99 + "GGUF",
100 + "Hugging Face",
101 + "Embeddings",
102 + "RAG",
103 + "Vector databases",
104 + "Tool-augmented agents",
105 + "LoRA",
106 + "QLoRA",
107 + "SFT fine-tuning",
108 + ],
109 + },
110 +];
111 +
112 +export default function HomePage() {
113 + return (
114 + <div className="mx-auto max-w-5xl px-4 sm:px-6">
115 + {/* Hero */}
116 + <section className="py-16 sm:py-24">
117 + <Reveal>
118 + <p className="text-sm font-medium text-primary">
119 + Professor — Department of Administrative Sciences
120 + </p>
121 + <h1 className="mt-3 text-4xl font-bold tracking-tight sm:text-5xl">
122 + Simon-Pierre Boucher
123 + </h1>
124 + <p className="mt-4 max-w-2xl text-lg text-muted-foreground">
125 + Financial econometrics researcher and macOS/AI developer.
126 + </p>
127 + <p className="mt-2 text-sm text-muted-foreground">
128 + Université du Québec en Outaouais (UQO), Gatineau — Pavillon
129 + Alexandre-Taché · Bilingual — French and English
130 + </p>
131 + <div className="mt-8 flex flex-wrap gap-3">
132 + <Link href="/research" className={cn(buttonVariants())}>
133 + View Research
134 + <ArrowRight aria-hidden="true" />
135 + </Link>
136 + <Link
137 + href="/apps"
138 + className={cn(buttonVariants({ variant: "secondary" }))}
139 + >
140 + Zyquo macOS Apps
141 + </Link>
142 + <a
143 + href="https://github.com/spboucher-ai"
144 + target="_blank"
145 + rel="noopener noreferrer"
146 + className={cn(buttonVariants({ variant: "outline" }))}
147 + >
148 + <Github aria-hidden="true" />
149 + GitHub
150 + </a>
151 + </div>
152 + <div className="mt-6 flex flex-col gap-1 text-sm text-muted-foreground sm:flex-row sm:gap-6">
153 + <a
154 + href="mailto:simon-pierre.boucher@uqo.ca"
155 + className="inline-flex items-center gap-2 transition-colors hover:text-foreground"
156 + >
157 + <Mail className="h-4 w-4" aria-hidden="true" />
158 + simon-pierre.boucher@uqo.ca
159 + </a>
160 + <a
161 + href="mailto:contact@spboucher.ai"
162 + className="inline-flex items-center gap-2 transition-colors hover:text-foreground"
163 + >
164 + <Mail className="h-4 w-4" aria-hidden="true" />
165 + contact@spboucher.ai
166 + </a>
167 + </div>
168 + </Reveal>
169 + </section>
170 +
171 + {/* About */}
172 + <section className="border-t border-border py-16">
173 + <Reveal>
174 + <h2 className="text-2xl font-semibold tracking-tight">About</h2>
175 + <p className="mt-4 max-w-3xl leading-relaxed text-muted-foreground">
176 + Simon-Pierre Boucher is a Professor in the Department of
177 + Administrative Sciences at Université du Québec en Outaouais (UQO).
178 + His research focuses on financial econometrics, commodity markets,
179 + monetary policy announcements, high-frequency finance, volatility
180 + modelling, textual analysis, and financialization. He is completing
181 + a Ph.D. in Business Administration (Finance and Insurance) at
182 + Université Laval under the supervision of Prof. Marie-Hélène Gagnon
183 + and Prof. Gabriel Power. Alongside his academic work, he builds
184 + native macOS applications focused on AI and local LLMs (the Zyquo
185 + suite).
186 + </p>
187 + </Reveal>
188 + </section>
189 +
190 + {/* Education */}
191 + <section className="border-t border-border py-16">
192 + <Reveal>
193 + <h2 className="text-2xl font-semibold tracking-tight">Education</h2>
194 + </Reveal>
195 + <div className="mt-6 grid gap-4 sm:grid-cols-3">
196 + {education.map((item, i) => (
197 + <Reveal key={item.degree} delay={i * 0.08}>
198 + <Card className="h-full">
199 + <CardHeader>
200 + <GraduationCap
201 + className="h-5 w-5 text-primary"
202 + aria-hidden="true"
203 + />
204 + <CardTitle className="text-base leading-snug">
205 + {item.degree}
206 + </CardTitle>
207 + <CardDescription>
208 + {item.school} · {item.period}
209 + </CardDescription>
210 + </CardHeader>
211 + {item.note && (
212 + <CardContent>
213 + <p className="text-sm italic text-muted-foreground">
214 + {item.note}
215 + </p>
216 + </CardContent>
217 + )}
218 + </Card>
219 + </Reveal>
220 + ))}
221 + </div>
222 + </section>
223 +
224 + {/* Research highlight */}
225 + <section className="border-t border-border py-16">
226 + <Reveal>
227 + <h2 className="text-2xl font-semibold tracking-tight">
228 + Research Highlight
229 + </h2>
230 + <Card className="mt-6">
231 + <CardHeader>
232 + <Badge className="w-fit">Publication · 2025</Badge>
233 + <CardTitle className="text-lg leading-snug">
234 + Speculative Trading in Energy Markets: Evidence from
235 + Macroeconomic Surprises
236 + </CardTitle>
237 + <CardDescription>
238 + Boucher, S.-P., Gagnon, M.-H., &amp; Power, G. J. —{" "}
239 + <em>The Energy Journal</em>
240 + </CardDescription>
241 + </CardHeader>
242 + <CardContent>
243 + <Link
244 + href="/research"
245 + className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline"
246 + >
247 + All publications, working papers &amp; presentations
248 + <ArrowRight className="h-4 w-4" aria-hidden="true" />
249 + </Link>
250 + </CardContent>
251 + </Card>
252 + </Reveal>
253 + </section>
254 +
255 + {/* Skills */}
256 + <section className="border-t border-border py-16">
257 + <Reveal>
258 + <h2 className="text-2xl font-semibold tracking-tight">
259 + Technical Skills
260 + </h2>
261 + </Reveal>
262 + <div className="mt-6 grid gap-4 md:grid-cols-3">
263 + {skillGroups.map((group, i) => (
264 + <Reveal key={group.title} delay={i * 0.08}>
265 + <Card className="h-full">
266 + <CardHeader>
267 + <CardTitle className="text-base">{group.title}</CardTitle>
268 + </CardHeader>
269 + <CardContent className="flex flex-wrap gap-1.5">
270 + {group.skills.map((skill) => (
271 + <Badge key={skill} variant="secondary">
272 + {skill}
273 + </Badge>
274 + ))}
275 + </CardContent>
276 + </Card>
277 + </Reveal>
278 + ))}
279 + </div>
280 + </section>
281 + </div>
282 + );
283 +}
added app/research/page.tsx +156 −0
@@ -0,0 +1,156 @@
1 +/*
2 + page.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import type { Metadata } from "next";
9 +import { ExternalLink } from "lucide-react";
10 +
11 +import { Badge } from "@/components/ui/badge";
12 +import {
13 + Card,
14 + CardContent,
15 + CardDescription,
16 + CardHeader,
17 + CardTitle,
18 +} from "@/components/ui/card";
19 +import { Reveal } from "@/components/reveal";
20 +
21 +export const metadata: Metadata = {
22 + title: "Research",
23 + description:
24 + "Publications, working papers, and conference presentations by Simon-Pierre Boucher — financial econometrics, commodity markets, monetary policy, and high-frequency finance.",
25 +};
26 +
27 +const workingPapers = [
28 + {
29 + title:
30 + "Has Financialization Changed the Impact of Macro Announcements on U.S. Commodity Markets?",
31 + venue: "SSRN Working Paper",
32 + date: "May 2022",
33 + link: "https://ssrn.com/abstract=400929",
34 + },
35 + {
36 + title:
37 + "Modelling Volatility Dynamics Between Commodity ETFs and Their Net Asset Value using BVAR and HAR Models",
38 + venue: "Working Paper",
39 + date: "January 2023",
40 + },
41 + {
42 + title:
43 + "Returns and Volatility Around FOMC Announcements: A High-Frequency Analysis of Policy Tone and Novelty",
44 + venue: "Working Paper",
45 + date: "November 2023",
46 + },
47 +];
48 +
49 +const presentations = [
50 + { event: "CRREP Research Day", detail: "2022, 2023" },
51 + {
52 + event: "Canadian Economics Association — 61st & 62nd Annual Conferences",
53 + detail: "2022, 2023",
54 + },
55 + {
56 + event: "7th Winter Workshop on Commodity Markets",
57 + detail: "Mont-Tremblant, 2024",
58 + },
59 +];
60 +
61 +export default function ResearchPage() {
62 + return (
63 + <div className="mx-auto max-w-5xl px-4 py-16 sm:px-6">
64 + <Reveal>
65 + <h1 className="text-3xl font-bold tracking-tight sm:text-4xl">
66 + Research
67 + </h1>
68 + <p className="mt-3 max-w-2xl text-muted-foreground">
69 + Financial econometrics, commodity markets, monetary policy
70 + announcements, high-frequency finance, volatility modelling, textual
71 + analysis, and financialization.
72 + </p>
73 + </Reveal>
74 +
75 + {/* Publication */}
76 + <section className="mt-12">
77 + <Reveal>
78 + <h2 className="text-xl font-semibold tracking-tight">Publication</h2>
79 + <Card className="mt-4">
80 + <CardHeader>
81 + <Badge className="w-fit">The Energy Journal · 2025</Badge>
82 + <CardTitle className="text-lg leading-snug">
83 + Speculative Trading in Energy Markets: Evidence from
84 + Macroeconomic Surprises
85 + </CardTitle>
86 + <CardDescription>
87 + Boucher, S.-P., Gagnon, M.-H., &amp; Power, G. J. (2025)
88 + </CardDescription>
89 + </CardHeader>
90 + </Card>
91 + </Reveal>
92 + </section>
93 +
94 + {/* Working papers */}
95 + <section className="mt-12">
96 + <Reveal>
97 + <h2 className="text-xl font-semibold tracking-tight">
98 + Working Papers
99 + </h2>
100 + </Reveal>
101 + <div className="mt-4 space-y-4">
102 + {workingPapers.map((paper, i) => (
103 + <Reveal key={paper.title} delay={i * 0.08}>
104 + <Card>
105 + <CardHeader>
106 + <Badge variant="secondary" className="w-fit">
107 + {paper.venue} · {paper.date}
108 + </Badge>
109 + <CardTitle className="text-lg leading-snug">
110 + {paper.title}
111 + </CardTitle>
112 + </CardHeader>
113 + {paper.link && (
114 + <CardContent>
115 + <a
116 + href={paper.link}
117 + target="_blank"
118 + rel="noopener noreferrer"
119 + className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline"
120 + >
121 + View on SSRN
122 + <ExternalLink className="h-4 w-4" aria-hidden="true" />
123 + </a>
124 + </CardContent>
125 + )}
126 + </Card>
127 + </Reveal>
128 + ))}
129 + </div>
130 + </section>
131 +
132 + {/* Presentations */}
133 + <section className="mt-12">
134 + <Reveal>
135 + <h2 className="text-xl font-semibold tracking-tight">
136 + Conference Presentations
137 + </h2>
138 + </Reveal>
139 + <div className="mt-4 grid gap-4 sm:grid-cols-3">
140 + {presentations.map((p, i) => (
141 + <Reveal key={p.event} delay={i * 0.08}>
142 + <Card className="h-full">
143 + <CardHeader>
144 + <CardTitle className="text-base leading-snug">
145 + {p.event}
146 + </CardTitle>
147 + <CardDescription>{p.detail}</CardDescription>
148 + </CardHeader>
149 + </Card>
150 + </Reveal>
151 + ))}
152 + </div>
153 + </section>
154 + </div>
155 + );
156 +}
added app/teaching/page.tsx +155 −0
@@ -0,0 +1,155 @@
1 +/*
2 + page.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import type { Metadata } from "next";
9 +
10 +import { Badge } from "@/components/ui/badge";
11 +import {
12 + Card,
13 + CardContent,
14 + CardHeader,
15 + CardTitle,
16 +} from "@/components/ui/card";
17 +import { Reveal } from "@/components/reveal";
18 +
19 +export const metadata: Metadata = {
20 + title: "Teaching",
21 + description:
22 + "Courses taught by Simon-Pierre Boucher at Université Laval — capital markets, financial econometrics, financial management, and financial theory.",
23 +};
24 +
25 +const courses = [
26 + {
27 + code: "GSF-3100",
28 + title: "Capital Markets",
29 + level: "Undergraduate",
30 + terms: "F2021, W2021, F2022, W2023",
31 + },
32 + {
33 + code: "GSF-6053",
34 + title: "Financial Econometrics I",
35 + level: "Graduate",
36 + terms: "W2022, W2025",
37 + },
38 + {
39 + code: "GSF-1500",
40 + title: "Financial Management",
41 + level: "Undergraduate",
42 + terms: "S2022",
43 + },
44 + {
45 + code: "GSF-6028",
46 + title: "Financial Theory",
47 + level: "Graduate",
48 + terms: "W2024",
49 + },
50 +];
51 +
52 +const taCourses = [
53 + { code: "GSF-2101", title: "Portfolio Management" },
54 + { code: "GSF-2102", title: "Corporate Finance" },
55 + { code: "GSF-6008", title: "Corporate Finance" },
56 + { code: "GSF-6025", title: "Financial Strategies & Policies I" },
57 + { code: "GSF-6028", title: "Financial Theory" },
58 +];
59 +
60 +export default function TeachingPage() {
61 + return (
62 + <div className="mx-auto max-w-5xl px-4 py-16 sm:px-6">
63 + <Reveal>
64 + <h1 className="text-3xl font-bold tracking-tight sm:text-4xl">
65 + Teaching
66 + </h1>
67 + <p className="mt-3 max-w-2xl text-muted-foreground">
68 + Lecturer at Université Laval (2021–Present) and Teaching Assistant
69 + (2018–2021).
70 + </p>
71 + </Reveal>
72 +
73 + {/* Lecturer */}
74 + <section className="mt-12">
75 + <Reveal>
76 + <h2 className="text-xl font-semibold tracking-tight">
77 + Lecturer — Université Laval (2021–Present)
78 + </h2>
79 + <Card className="mt-4 overflow-x-auto">
80 + <CardContent className="p-0">
81 + <table className="w-full text-sm">
82 + <thead>
83 + <tr className="border-b border-border text-left">
84 + <th scope="col" className="px-4 py-3 font-medium">
85 + Code
86 + </th>
87 + <th scope="col" className="px-4 py-3 font-medium">
88 + Course
89 + </th>
90 + <th scope="col" className="px-4 py-3 font-medium">
91 + Level
92 + </th>
93 + <th scope="col" className="px-4 py-3 font-medium">
94 + Terms
95 + </th>
96 + </tr>
97 + </thead>
98 + <tbody>
99 + {courses.map((course) => (
100 + <tr
101 + key={`${course.code}-${course.terms}`}
102 + className="border-b border-border last:border-0"
103 + >
104 + <td className="px-4 py-3 font-mono text-xs">
105 + {course.code}
106 + </td>
107 + <td className="px-4 py-3 font-medium">{course.title}</td>
108 + <td className="px-4 py-3">
109 + <Badge
110 + variant={
111 + course.level === "Graduate"
112 + ? "default"
113 + : "secondary"
114 + }
115 + >
116 + {course.level}
117 + </Badge>
118 + </td>
119 + <td className="px-4 py-3 text-muted-foreground">
120 + {course.terms}
121 + </td>
122 + </tr>
123 + ))}
124 + </tbody>
125 + </table>
126 + </CardContent>
127 + </Card>
128 + </Reveal>
129 + </section>
130 +
131 + {/* Teaching assistant */}
132 + <section className="mt-12">
133 + <Reveal>
134 + <h2 className="text-xl font-semibold tracking-tight">
135 + Teaching Assistant — Université Laval (2018–2021)
136 + </h2>
137 + </Reveal>
138 + <div className="mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
139 + {taCourses.map((course, i) => (
140 + <Reveal key={`${course.code}-${course.title}`} delay={i * 0.06}>
141 + <Card className="h-full">
142 + <CardHeader>
143 + <Badge variant="outline" className="w-fit font-mono">
144 + {course.code}
145 + </Badge>
146 + <CardTitle className="text-base">{course.title}</CardTitle>
147 + </CardHeader>
148 + </Card>
149 + </Reveal>
150 + ))}
151 + </div>
152 + </section>
153 + </div>
154 + );
155 +}
added components/app-card.tsx +76 −0
@@ -0,0 +1,76 @@
1 +/*
2 + app-card.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import Image from "next/image";
9 +import { Download, Github, Tag } from "lucide-react";
10 +
11 +import type { ZyquoApp } from "@/lib/apps";
12 +import { buttonVariants } from "@/components/ui/button";
13 +import {
14 + Card,
15 + CardContent,
16 + CardDescription,
17 + CardFooter,
18 + CardHeader,
19 + CardTitle,
20 +} from "@/components/ui/card";
21 +import { cn } from "@/lib/utils";
22 +
23 +export function AppCard({ app }: { app: ZyquoApp }) {
24 + return (
25 + <Card className="flex h-full flex-col transition-shadow hover:shadow-md">
26 + <CardHeader className="flex-row items-center gap-4 space-y-0">
27 + <Image
28 + src={app.icon}
29 + alt={`${app.name} app icon`}
30 + width={64}
31 + height={64}
32 + className="h-16 w-16 shrink-0 rounded-2xl shadow-sm"
33 + />
34 + <div className="min-w-0">
35 + <CardTitle className="text-lg">
36 + <span aria-hidden="true" className="mr-1.5">
37 + {app.emoji}
38 + </span>
39 + {app.name}
40 + </CardTitle>
41 + <CardDescription className="mt-1">{app.tagline}</CardDescription>
42 + </div>
43 + </CardHeader>
44 + <CardContent className="flex-1">
45 + <p className="text-sm text-muted-foreground">{app.description}</p>
46 + </CardContent>
47 + <CardFooter className="flex flex-wrap gap-2">
48 + <a
49 + href={app.repo}
50 + target="_blank"
51 + rel="noopener noreferrer"
52 + className={cn(buttonVariants({ variant: "outline", size: "sm" }))}
53 + >
54 + <Github aria-hidden="true" />
55 + GitHub Repo
56 + </a>
57 + <a
58 + href={app.release}
59 + target="_blank"
60 + rel="noopener noreferrer"
61 + className={cn(buttonVariants({ variant: "outline", size: "sm" }))}
62 + >
63 + <Tag aria-hidden="true" />
64 + Release
65 + </a>
66 + <a
67 + href={app.dmg}
68 + className={cn(buttonVariants({ variant: "default", size: "sm" }))}
69 + >
70 + <Download aria-hidden="true" />
71 + Download DMG
72 + </a>
73 + </CardFooter>
74 + </Card>
75 + );
76 +}
added components/reveal.tsx +32 −0
@@ -0,0 +1,32 @@
1 +/*
2 + reveal.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +"use client";
9 +
10 +import * as React from "react";
11 +import { motion } from "framer-motion";
12 +
13 +interface RevealProps {
14 + children: React.ReactNode;
15 + delay?: number;
16 + className?: string;
17 +}
18 +
19 +export function Reveal({ children, delay = 0, className }: RevealProps) {
20 + return (
21 + <motion.div
22 + data-reveal
23 + className={className}
24 + initial={{ opacity: 0, y: 16 }}
25 + whileInView={{ opacity: 1, y: 0 }}
26 + viewport={{ once: true, margin: "-40px" }}
27 + transition={{ duration: 0.5, delay, ease: "easeOut" }}
28 + >
29 + {children}
30 + </motion.div>
31 + );
32 +}
added components/site-footer.tsx +40 −0
@@ -0,0 +1,40 @@
1 +/*
2 + site-footer.tsx
3 + spboucher.ai Web
4 + Author: Simon-Pierre Boucher
5 + Mail: contact@spboucher.ai
6 +*/
7 +
8 +import Link from "next/link";
9 +
10 +export function SiteFooter() {
11 + return (
12 + <footer className="border-t border-border">
13 + <div className="mx-auto flex max-w-5xl flex-col items-center justify-between gap-4 px-4 py-8 text-sm text-muted-foreground sm:flex-row sm:px-6">
14 + <p>
15 + © {new Date().getFullYear()} Simon-Pierre Boucher — Université du
16 + Québec en Outaouais (UQO)
17 + </p>
18 + <div className="flex items-center gap-4">
19 + <a
20 + href="mailto:contact@spboucher.ai"
21 + className="transition-colors hover:text-foreground"
22 + >
23 + contact@spboucher.ai
24 + </a>
25 + <a
26 + href="https://github.com/spboucher-ai"
27 + target="_blank"
28 + rel="noopener noreferrer"
29 + className="transition-colors hover:text-foreground"
30 + >
31 + GitHub
32 + </a>
33 + <Link href="/cv" className="transition-colors hover:text-foreground">
34 + CV
35 + </Link>
36 + </div>
37 + </div>
38 + </footer>
39 + );
40 +}
added components/site-header.tsx +94 −0
Large diff (94 lines) is not loaded by default to keep the page fast.Load diff
added components/theme-toggle.tsx +45 −0
Large diff (45 lines) is not loaded by default to keep the page fast.Load diff
added components/ui/badge.tsx +39 −0
Large diff (39 lines) is not loaded by default to keep the page fast.Load diff
added components/ui/button.tsx +55 −0
Large diff (55 lines) is not loaded by default to keep the page fast.Load diff
added components/ui/card.tsx +77 −0
Large diff (77 lines) is not loaded by default to keep the page fast.Load diff
added lib/apps.ts +93 −0
Large diff (93 lines) is not loaded by default to keep the page fast.Load diff
added lib/utils.ts +13 −0
Large diff (13 lines) is not loaded by default to keep the page fast.Load diff
added next.config.ts +14 −0
Large diff (14 lines) is not loaded by default to keep the page fast.Load diff
added package-lock.json +1835 −0
Large diff (1,835 lines) is not loaded by default to keep the page fast.Load diff
added package.json +31 −0
Large diff (31 lines) is not loaded by default to keep the page fast.Load diff
added postcss.config.mjs +14 −0
Large diff (14 lines) is not loaded by default to keep the page fast.Load diff
added public/icons/zyquo-agent.png +0 −0

Binary file not shown.

added public/icons/zyquo-atlas.png +0 −0

Binary file not shown.

added public/icons/zyquo-cloud.png +0 −0

Binary file not shown.

added public/icons/zyquo-local.png +0 −0

Binary file not shown.

added public/icons/zyquo-mlx.png +0 −0

Binary file not shown.

added public/icons/zyquo-router.png +0 −0

Binary file not shown.

added scripts/start-production.sh +12 −0
Large diff (12 lines) is not loaded by default to keep the page fast.Load diff
added tsconfig.json +45 −0
Large diff (46 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme