diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..af07f22 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI - Build and Test + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '23' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Type check + run: yarn type-check + + - name: Build application + run: yarn build + + # Uncomment when tests are available + # - name: Run tests + # run: yarn test diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..336fb9d --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,49 @@ +name: Deploy to Production + +on: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + name: Deploy to Production + runs-on: ubuntu-latest + environment: + name: production + url: https://djoufson.com + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + run: | + docker build -t djoufson/website:latest -t djoufson/website:${{ github.sha }} . + docker push djoufson/website:latest + docker push djoufson/website:${{ github.sha }} + + - name: Deploy to VPS + uses: appleboy/ssh-action@v0.1.7 + with: + host: ${{ secrets.VPS_HOST }} + username: ${{ secrets.VPS_USERNAME }} + key: ${{ secrets.VPS_SSH_KEY }} + port: ${{ secrets.VPS_PORT }} + script: | + cd ${{ secrets.PROJECT_PATH }} + docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} + docker compose -f docker-compose.prod.yml pull + docker compose -f docker-compose.prod.yml up -d --force-recreate + docker image prune -f + docker logout \ No newline at end of file diff --git a/.github/workflows/main_deploy.yaml b/.github/workflows/main_deploy.yaml deleted file mode 100644 index bcee90e..0000000 --- a/.github/workflows/main_deploy.yaml +++ /dev/null @@ -1,58 +0,0 @@ -name: Deploy to Production - -on: - push: - branches: - - main - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build-and-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: '23' - - name: Install dependencies - run: npm install - - name: Build - run: npm run build - # There are no tests in this code base yet - # - name: Run tests - # run: npm test - - deploy: - needs: build-and-test - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push Docker images - run: | - docker build -t djoufson/website . - docker push djoufson/website:latest - - - name: Deploy to VPS - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.VPS_HOST }} - username: ${{ secrets.VPS_USERNAME }} - key: ${{ secrets.VPS_SSH_KEY }} - port: ${{ secrets.VPS_PORT }} - script: | - cd ${{ secrets.PROJECT_PATH }} - docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} - docker compose -f docker-compose.prod.yml pull - docker compose -f docker-compose.prod.yml up -d --force-recreate diff --git a/app/globals.css b/app/globals.css index f317634..7c684c5 100644 --- a/app/globals.css +++ b/app/globals.css @@ -4,8 +4,74 @@ /* @custom-variant dark (&:is(.dark *)); */ /* Add to your existing globals.css */ -/* Prism.js syntax highlighting theme */ -@import 'prism-themes/themes/prism-material-dark.css'; +/* Highlight.js syntax highlighting theme - responsive to dark mode */ +@import 'highlight.js/styles/github.css'; + +/* Light mode overrides for highlight.js */ +.hljs { + background: transparent !important; + color: #1f2937 !important; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #991b1b !important; +} + +.hljs-string, +.hljs-doctag { + color: #059669 !important; +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: #7c3aed !important; +} + +.hljs-comment, +.hljs-quote { + color: #6b7280 !important; +} + +.hljs-number, +.hljs-literal { + color: #1d4ed8 !important; +} + +/* Dark mode overrides for highlight.js */ +.dark .hljs { + background: transparent !important; + color: #e6edf3 !important; +} + +.dark .hljs-keyword, +.dark .hljs-selector-tag, +.dark .hljs-subst { + color: #ff7b72 !important; +} + +.dark .hljs-string, +.dark .hljs-doctag { + color: #a5c9ea !important; +} + +.dark .hljs-title, +.dark .hljs-section, +.dark .hljs-selector-id { + color: #79c0ff !important; +} + +.dark .hljs-comment, +.dark .hljs-quote { + color: #8b949e !important; +} + +.dark .hljs-number, +.dark .hljs-literal { + color: #79c0ff !important; +} /* Custom prose styles */ /* blog-content.css */ @@ -31,15 +97,15 @@ } .prose h6 { - @apply text-sm font-medium mt-4 mb-2 text-gray-600; + @apply text-sm font-medium mt-4 mb-2 text-gray-600 dark:text-gray-400; } .prose p { - @apply text-base leading-relaxed mb-5 text-gray-800; + @apply text-base leading-relaxed mb-5 text-foreground; } .prose a { - @apply text-blue-600 underline hover:text-blue-800 transition-colors; + @apply text-blue-600 dark:text-blue-400 underline hover:text-blue-800 dark:hover:text-blue-300 transition-colors; } .prose ul { @@ -55,15 +121,22 @@ } .prose blockquote { - @apply border-l-4 border-gray-300 pl-4 italic text-gray-600 my-6; + @apply border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic text-gray-600 dark:text-gray-400 my-6; } +/* Code blocks with syntax highlighting */ .prose pre { - @apply bg-gray-900 text-white text-sm p-4 rounded-md overflow-x-auto my-6; + @apply text-sm p-4 rounded-lg overflow-x-auto my-6 bg-transparent border border-gray-200 dark:border-gray-700; +} + +/* Inline code (not inside pre blocks) */ +.prose :not(pre) > code { + @apply text-pink-600 dark:text-pink-400 bg-transparent px-2 py-1 rounded-md font-mono text-sm border border-gray-200 dark:border-gray-700; } -.prose code { - @apply text-pink-600 bg-transparent px-1 py-0.5 rounded font-mono text-sm; +/* Ensure highlighted code blocks don't get inline code styling */ +.prose pre code { + @apply bg-transparent text-inherit px-0 py-0; } .prose img { @@ -75,15 +148,15 @@ } .prose th { - @apply border-b-2 border-gray-300 px-4 py-2 font-semibold; + @apply border-b-2 border-gray-300 dark:border-gray-600 px-4 py-2 font-semibold; } .prose td { - @apply border-b border-gray-200 px-4 py-2; + @apply border-b border-gray-200 dark:border-gray-700 px-4 py-2; } .prose hr { - @apply my-8 border-gray-200; + @apply my-8 border-gray-200 dark:border-gray-700; } .container { diff --git a/app/layout.tsx b/app/layout.tsx index 631c6f9..1ea45bc 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,6 +4,8 @@ import Footer from "@/components/footer"; import Header from "@/components/header"; import GoogleAnalytics from "@/components/google/GoogleAnalytics"; import GoogleAdSense from "@/components/google/GoogleAdSense"; +import BackToTop from "@/components/BackToTop"; +import { ThemeProvider } from "@/components/theme-provider"; export const metadata: Metadata = { title: "Djoufson's Amazing World π", @@ -16,15 +18,23 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - +