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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Vercel Preview Deployment

# Variáveis de ambiente para os jobs da Vercel
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

on:
push:
branches:
- develop
pull_request:
branches: [main, develop]

jobs:
# -----------------------------------------------------------------
# JOB 1: Testa e Linta o código
# -----------------------------------------------------------------
test-and-lint:
name: Test & Lint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22.x]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run linter
run: pnpm run lint

- name: Run tests
run: pnpm run test:ci

# -----------------------------------------------------------------
# JOB 2: Deploy de Preview (Vercel)
# -----------------------------------------------------------------
deploy-preview:
name: Deploy Preview
needs: test-and-lint
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Vercel CLI
run: npm install --global vercel@latest

- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}

- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy Project Artifacts to Vercel
# A Vercel CLI detecta o contexto de PR e posta o comentário automaticamente
run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
90 changes: 19 additions & 71 deletions .github/workflows/ci.yml → .github/workflows/production.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
name: CI/CD Pipeline com Tagging
name: Vercel Production Deployment and Tagging

# Variáveis de ambiente para os jobs da Vercel
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
branches:
- main

jobs:
# -----------------------------------------------------------------
# JOB 1: Apenas Testa e Linta
# JOB 1: Testa e Linta o código
# -----------------------------------------------------------------
test-and-lint:
name: Test & Lint
Expand Down Expand Up @@ -48,11 +52,6 @@ jobs:
name: Deploy to Production
needs: test-and-lint
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
deployments: write
if: github.ref == 'refs/heads/main' && github.event_name == 'push'

steps:
- name: Checkout code
Expand All @@ -71,62 +70,7 @@ jobs:
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}

# -----------------------------------------------------------------
# JOB 3: Deploy de Preview
# -----------------------------------------------------------------
deploy-preview:
name: Deploy Preview
needs: test-and-lint
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
deployments: write
if: github.event_name == 'pull_request' || (github.ref == 'refs/heads/develop' && github.event_name == 'push')

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Vercel CLI
run: npm install --global vercel@latest

- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}

- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy Project Artifacts to Vercel
run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}

- name: Get Repo ID
if: github.event_name == 'pull_request'
run: |
REPO_ID=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/${{ github.repository }} | jq -r .id)
echo "REPO_ID=$REPO_ID" >> $GITHUB_ENV

- name: Notify Vercel for PR Comment (if PR)
if: github.event_name == 'pull_request'
run: |
curl -X POST "https://api.vercel.com/v13/deployments" \
-H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{
"name": "mrdeveloper",
"source": "cli",
"gitSource": {
"type": "github",
"repo": "${{ github.repository }}",
"ref": "${{ github.head_ref }}",
"sha": "${{ github.sha }}",
"repoId": ${{ env.REPO_ID }}
}
}'



# -----------------------------------------------------------------
# JOB 4: Criar Tag de Versão (Sem mudanças)
# JOB 3: Criar Tag de Versão
# -----------------------------------------------------------------
tag-release:
name: Tag Release
Expand All @@ -137,6 +81,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# Necessário para buscar tags existentes
fetch-depth: 0

- name: Get version from package.json
id: get_version
Expand All @@ -145,12 +92,13 @@ jobs:
echo "TAG_VERSION=v$VERSION" >> $GITHUB_ENV

- name: Check if tag already exists
id: check_tag
run: |
if git ls-remote --tags origin | grep -q "refs/tags/${TAG_VERSION}"; then
echo "Tag $TAG_VERSION already exists. Skipping..."
if git rev-parse "refs/tags/${{ env.TAG_VERSION }}" >/dev/null 2>&1; then
echo "Tag ${{ env.TAG_VERSION }} already exists. Skipping..."
echo "SKIP_TAG=true" >> $GITHUB_ENV
else
echo "Tag $TAG_VERSION does not exist. Proceeding..."
echo "Tag ${{ env.TAG_VERSION }} does not exist. Proceeding..."
echo "SKIP_TAG=false" >> $GITHUB_ENV
fi

Expand All @@ -159,5 +107,5 @@ jobs:
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git tag $TAG_VERSION -m "Release $TAG_VERSION"
git push origin $TAG_VERSION
git tag ${{ env.TAG_VERSION }} -m "Release ${{ env.TAG_VERSION }}"
git push origin ${{ env.TAG_VERSION }}
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./App.scss";
import "./styles/layout.scss";

import AnimatedBackground from "./components/AnimatedBackground";
import Header from "./components/Header";
import Footer from "./components/Footer";
import Hero from "./sections/Hero";
Expand All @@ -11,6 +12,7 @@ import Contact from "./sections/Contact";
const App = () => {
return (
<div className="app">
<AnimatedBackground />
<main className="main" role="main">
<Hero />
<Header />
Expand Down
70 changes: 61 additions & 9 deletions src/components/AnimatedBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ const AnimatedBackground: React.FC = () => {
const particleCount = 500;
const gridSize = 50; // Spatial partitioning grid size
const mouseRadius = 800;
const mouseRadiusSquared = mouseRadius * mouseRadius;
const maxLineLength = 95;
const maxLineLengthSquared = maxLineLength * maxLineLength;
const propagationRadius = 40; // Valor mais realista para propagação visível
Expand All @@ -242,20 +241,49 @@ const AnimatedBackground: React.FC = () => {
const opacityCache = new OpacityCache();

const mouse = { x: -1000, y: -1000 };
let globalOpacity = 0;
let targetOpacity = 0;
const opacitySpeed = 0.01; // Velocidade suave de fade
let effectiveRadius = 0;
let targetRadius = 0;
const radiusSpeed = 0.005; // Velocidade de shrink/implode mais lenta

const checkMouseInHero = (x: number, y: number) => {
if (x === -1000 || y === -1000) return false;
const elementUnderMouse = document.elementFromPoint(x, y);
return elementUnderMouse?.closest(".hero") !== null;
};

const handleMouseMove = (e: MouseEvent) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
opacityCache.updateMouse(mouse.x, mouse.y, mouseRadius);
const isInHero = checkMouseInHero(e.clientX, e.clientY);
if (isInHero) {
mouse.x = e.clientX;
mouse.y = e.clientY;
opacityCache.updateMouse(mouse.x, mouse.y, mouseRadius);
targetOpacity = 1;
targetRadius = mouseRadius;
} else {
// Não setar mouse.x = -1000 imediatamente, deixar o raio controlar
targetOpacity = 0;
targetRadius = 0;
}
};

const handleMouseLeave = () => {
mouse.x = -1000;
mouse.y = -1000;
targetOpacity = 0;
targetRadius = 0;
};

const handleScroll = () => {
if (!checkMouseInHero(mouse.x, mouse.y)) {
targetOpacity = 0;
targetRadius = 0;
}
};

window.addEventListener("mousemove", handleMouseMove, { passive: true });
window.addEventListener("mouseleave", handleMouseLeave, { passive: true });
window.addEventListener("scroll", handleScroll, { passive: true });

const particles: Particle[] = [];
for (let i = 0; i < particleCount; i++) {
Expand All @@ -268,6 +296,16 @@ const AnimatedBackground: React.FC = () => {
const animate = () => {
frameCount++;

// Update global opacity and effective radius for smooth transitions
globalOpacity += (targetOpacity - globalOpacity) * opacitySpeed;
effectiveRadius += (targetRadius - effectiveRadius) * radiusSpeed;

// Stop animation when radius reaches zero
if (effectiveRadius <= 1) {
mouse.x = -1000;
mouse.y = -1000;
}

// Clear canvas
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
Expand All @@ -293,11 +331,12 @@ const AnimatedBackground: React.FC = () => {
? spatialGrid.getNeighbors(mouseGridX, mouseGridY, mouseRadius)
: [];

// Filter particles within mouse radius
// Filter particles within effective radius
const effectiveRadiusSquared = effectiveRadius * effectiveRadius;
const connectedParticles: Particle[] = [];
for (const p of nearbyParticles) {
const distSquared = distanceCache.get(mouse.x, mouse.y, p.x, p.y);
if (distSquared < mouseRadiusSquared) {
if (distSquared < effectiveRadiusSquared) {
p.layer = 0; // Partículas conectadas diretamente ao mouse = camada 0
connectedParticles.push(p);
}
Expand All @@ -306,6 +345,7 @@ const AnimatedBackground: React.FC = () => {
if (connectedParticles.length > 0) {
ctx.save();
ctx.globalCompositeOperation = "lighter";
ctx.globalAlpha = globalOpacity;

const processedSet = new Set<Particle>(connectedParticles);

Expand Down Expand Up @@ -565,12 +605,24 @@ const AnimatedBackground: React.FC = () => {
window.removeEventListener("resize", resize);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseleave", handleMouseLeave);
window.removeEventListener("scroll", handleScroll);
cancelAnimationFrame(animationFrameId);
};
}, []);

return (
<div className="canvas">
<div
className="canvas"
style={{
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
zIndex: -1,
pointerEvents: "none",
}}
>
<canvas ref={canvasRef} className="connecting-dots" />
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions src/sections/About.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
display: flex;
flex-direction: column;
align-items: center;
background: rgba(0, 0, 0, 0.85); // Contraste com o background animado
padding: 2rem;
border-radius: 10px;

@media (max-width: 1500px) {
width: 1100px;
Expand Down
3 changes: 3 additions & 0 deletions src/sections/Contact.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
justify-content: center;
z-index: 10;
margin-bottom: 10rem;
background: rgba(0, 0, 0, 0.85); // Contraste com o background animado
padding: 2rem;
border-radius: 10px;

@media (max-width: 700px) {
width: 400px;
Expand Down
3 changes: 3 additions & 0 deletions src/sections/Experience.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
width: 1300px;
margin-left: auto;
margin-right: auto;
background: rgba(0, 0, 0, 0.85); // Contraste com o background animado
padding: 2rem;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
Expand Down
2 changes: 0 additions & 2 deletions src/sections/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ArrowRight, Download } from "lucide-react";
import { Helmet } from "react-helmet-async";
import AnimatedBackground from "../components/AnimatedBackground";
import "./Hero.scss";

const Hero: React.FC = () => {
Expand All @@ -13,7 +12,6 @@ const Hero: React.FC = () => {
content="Portfólio de Matheus Caiser, desenvolvedor web Full Stack especialista em criar soluções modernas e performáticas com React, Node.js, TypeScript e outras tecnologias de ponta."
/>
</Helmet>
<AnimatedBackground />
<div className="hero__content">
<div className="hero__inner">
<h1 className="hero__title">
Expand Down
Loading