Compare commits

...

33 Commits

Author SHA1 Message Date
148fe8eeff feat: add and update pre-commit
All checks were successful
test-build / guarddog (push) Successful in 17s
test-build / build (push) Successful in 4m8s
renovate / renovate (push) Successful in 1m35s
2026-03-15 16:56:54 -05:00
8d1d47dc4f fix: handle script with swup
All checks were successful
test-build / guarddog (push) Successful in 33s
test-build / build (push) Successful in 1m41s
renovate / renovate (push) Successful in 1m26s
2026-03-14 13:17:33 -05:00
7b8fb380d4 feat: enable prerender pending resoltuion of server island fix
All checks were successful
test-build / guarddog (push) Successful in 30s
test-build / build (push) Successful in 1m55s
renovate / renovate (push) Successful in 1m15s
2026-03-13 11:39:59 -05:00
500d9e2ea0 feat: move script handling to use swup instead of astro transitions, move animations to baselayout
All checks were successful
renovate / renovate (push) Successful in 41s
test-build / build (push) Successful in 1m25s
test-build / guarddog (push) Successful in 1m43s
2026-03-13 10:59:25 -05:00
70a94990e2 feat: remove astro features that are replaced by swup 2026-03-13 10:35:15 -05:00
d0f10986cd feat: improve category cards spacing, overflow, and add fade effect to edges
All checks were successful
test-build / guarddog (push) Successful in 23s
renovate / renovate (push) Successful in 1m42s
test-build / build (push) Successful in 1m48s
2026-03-13 10:18:04 -05:00
629403fdde feat: adjustment to spacing 2026-03-13 10:17:39 -05:00
43f0f7b324 feat: test placing weather section below posts
All checks were successful
renovate / renovate (push) Successful in 2m8s
test-build / guarddog (push) Successful in 24s
test-build / build (push) Successful in 6m32s
2026-03-13 09:26:45 -05:00
eab6e0c31d feat: disable prerender pending resoltuion of server island fix 2026-03-13 09:25:52 -05:00
805cb28185 feat: add scripts to clear and run from scratch
All checks were successful
test-build / guarddog (push) Successful in 31s
renovate / renovate (push) Successful in 45s
test-build / build (push) Successful in 1m32s
2026-03-13 01:35:14 -05:00
54c82a7f79 fix: function needing paranthensis 2026-03-13 01:34:32 -05:00
9a62f867f1 ci: minor tweaks to args and env 2026-03-13 01:34:02 -05:00
0bef13c414 feat: copy package.json
All checks were successful
test-build / guarddog (push) Successful in 30s
renovate / renovate (push) Successful in 52s
test-build / build (push) Successful in 2m10s
2026-03-12 22:18:06 -05:00
0dfcc25984 feat: disable security feature
All checks were successful
test-build / guarddog (push) Successful in 28s
test-build / build (push) Successful in 1m34s
renovate / renovate (push) Successful in 31s
2026-03-12 18:05:46 -05:00
4c8665ebe2 feat: use alpine-dev for bun builder
All checks were successful
test-build / guarddog (push) Successful in 24s
renovate / renovate (push) Successful in 28s
test-build / build (push) Successful in 3m51s
2026-03-12 16:26:17 -05:00
d083660f1c ci: make registry an arg 2026-03-12 15:44:42 -05:00
e9a8b6de97 ci: reorder release workflow
All checks were successful
renovate / renovate (push) Successful in 36s
2026-03-12 15:14:34 -05:00
578e1661cd fix: use semantic release outputs
All checks were successful
renovate / renovate (push) Successful in 1m11s
2026-03-12 15:07:38 -05:00
7882c3ecc7 feat: remove old release workflows
All checks were successful
renovate / renovate (push) Successful in 1m8s
2026-03-12 14:41:45 -05:00
db79f912ee fix: incorrect name of step 2026-03-12 14:41:25 -05:00
36eaa0c132 feat: add outputs of semantic release
All checks were successful
test-build / guarddog (push) Successful in 20s
renovate / renovate (push) Successful in 1m25s
test-build / build (push) Successful in 3m17s
2026-03-12 14:30:23 -05:00
24c837cf84 feat: change release format 2026-03-12 14:25:28 -05:00
f32b75e31d feat: install deps
All checks were successful
renovate / renovate (push) Successful in 1m10s
2026-03-12 14:20:25 -05:00
ce75e7ca5e feat: add if to ignore harbor release result
All checks were successful
renovate / renovate (push) Successful in 58s
2026-03-12 14:13:19 -05:00
bf3a7ef261 feat: setup node for semantic release
All checks were successful
renovate / renovate (push) Successful in 1m41s
2026-03-12 14:08:57 -05:00
cae8cd3a39 chore(deps): update deps
All checks were successful
test-build / guarddog (push) Successful in 27s
renovate / renovate (push) Successful in 1m21s
test-build / build (push) Successful in 2m40s
2026-03-12 14:02:50 -05:00
620b496957 feat: add semantic release 2026-03-12 14:01:14 -05:00
4b58117454 feat: ignore on docs updates
All checks were successful
renovate / renovate (push) Successful in 28s
2026-03-12 13:55:39 -05:00
68f2080bda feat: add release branch, update and merge release workflow
All checks were successful
test-build / guarddog (push) Successful in 50s
renovate / renovate (push) Successful in 58s
test-build / build (push) Successful in 4m4s
2026-03-12 13:54:11 -05:00
07fa86b17c feat: remove release-please
All checks were successful
renovate / renovate (push) Successful in 58s
test-build / guarddog (push) Successful in 1m16s
test-build / build (push) Successful in 3m22s
2026-03-12 13:26:28 -05:00
1577ee4c27 feat: use different workflow for gitea
Some checks failed
test-build / guarddog (push) Successful in 21s
renovate / renovate (push) Successful in 39s
test-build / build (push) Successful in 3m2s
release-image-harbor / release-please (push) Failing after 2m39s
release-image-gitea / release-please (push) Failing after 2m41s
release-image-harbor / build (push) Has been skipped
release-image-harbor / guarddog (push) Has been skipped
release-image-gitea / build (push) Has been skipped
release-image-gitea / guarddog (push) Has been skipped
release-image-harbor / release (push) Has been skipped
release-image-gitea / release (push) Has been skipped
2026-03-12 13:05:12 -05:00
e0a3d391b3 feat: add token
Some checks failed
release-image-harbor / release-please (push) Failing after 6s
release-image-harbor / build (push) Has been skipped
release-image-harbor / guarddog (push) Has been skipped
release-image-harbor / release (push) Has been skipped
renovate / renovate (push) Successful in 30s
release-image-gitea / release-please (push) Failing after 38s
release-image-gitea / build (push) Has been skipped
release-image-gitea / guarddog (push) Has been skipped
release-image-gitea / release (push) Has been skipped
test-build / guarddog (push) Successful in 58s
test-build / build (push) Successful in 1m49s
2026-03-12 13:03:26 -05:00
99032f7a62 feat: add automation to release using release-please in workflows
Some checks failed
renovate / renovate (push) Successful in 38s
release-image-harbor / release-please (push) Failing after 42s
release-image-harbor / build (push) Has been skipped
release-image-harbor / guarddog (push) Has been skipped
release-image-harbor / release (push) Has been skipped
release-image-gitea / release-please (push) Failing after 1m44s
release-image-gitea / build (push) Has been skipped
release-image-gitea / guarddog (push) Has been skipped
release-image-gitea / release (push) Has been skipped
test-build / guarddog (push) Successful in 59s
test-build / build (push) Has been cancelled
2026-03-12 12:59:16 -05:00
30 changed files with 1643 additions and 896 deletions

View File

@@ -1,154 +0,0 @@
name: release-image-gitea
on:
push:
tags:
- 3.*
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 24.14.0
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Cache Astro Build Cache
uses: actions/cache@v5
with:
path: |
.astro
node_modules/.vite
key: ${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}
restore-keys: |
${{ runner.os }}-astro-
- name: Lint Code
run: bun run lint
- name: Build Project
run: bun run build
release:
runs-on: ubuntu-js
needs: build
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Login to Registry
uses: docker/login-action@v4
with:
registry: ${{ vars.REPOSITORY_HOST }}
username: ${{ gitea.actor }}
password: ${{ secrets.REPOSITORY_TOKEN }}
- name: Login to Docker
uses: docker/login-action@v4
with:
registry: ${{ vars.DH_REGISTRY }}
username: ${{ secrets.DH_USERNAME }}
password: ${{ secrets.DH_TOKEN }}
- name: Create Kubeconfig
run: |
mkdir $HOME/.kube
echo "${{ secrets.KUBECONFIG_BUILDX }}" > $HOME/.kube/config
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v4
with:
driver: kubernetes
driver-opts: |
namespace=gitea
qemu.install=true
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["harbor.alexlebens.net/proxy-hub.docker/"]
- name: Available Platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v6
with:
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
images: |
${{ vars.REPOSITORY_HOST }}/${{ gitea.repository }}
- name: Get Version Info
id: version
run: |
echo "version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
echo "commit=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
if git describe --tags --exact-match HEAD 2>/dev/null; then
echo "is_release=true" >> $GITHUB_OUTPUT
else
echo "is_release=false" >> $GITHUB_OUTPUT
fi
- name: Build and Push Image
uses: docker/build-push-action@v7
with:
context: .
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
VERSION=${{ steps.version.outputs.version }}
COMMIT_SHA=${{ steps.version.outputs.commit }}
IS_RELEASE=${{ steps.version.outputs.is_release }}
file: ./Dockerfile
cache-from: type=gha
cache-to: type=gha,mode=max
- name: ntfy Success
uses: niniyas/ntfy-action@master
if: success()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Success - Site Profile'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: 'Image for Site Profile has been released!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Image for Site Profile has failed to be released.'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yml", "clear": true}]'
image: true

View File

@@ -1,154 +0,0 @@
name: release-image-harbor
on:
push:
tags:
- 3.*
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 24.14.0
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Cache Astro Build Cache
uses: actions/cache@v5
with:
path: |
.astro
node_modules/.vite
key: ${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}
restore-keys: |
${{ runner.os }}-astro-
- name: Lint Code
run: bun run lint
- name: Build Project
run: bun run build
release:
runs-on: ubuntu-js
needs: build
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Login to Registry
uses: docker/login-action@v4
with:
registry: ${{ vars.REGISTRY_HOST }}
username: ${{ vars.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_SECRET }}
- name: Login to Docker
uses: docker/login-action@v4
with:
registry: ${{ vars.DH_REGISTRY }}
username: ${{ secrets.DH_USERNAME }}
password: ${{ secrets.DH_TOKEN }}
- name: Create Kubeconfig
run: |
mkdir $HOME/.kube
echo "${{ secrets.KUBECONFIG_BUILDX }}" > $HOME/.kube/config
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v4
with:
driver: kubernetes
driver-opts: |
namespace=gitea
qemu.install=true
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["harbor.alexlebens.net/proxy-hub.docker/"]
- name: Available Platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v6
with:
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
images: |
${{ vars.REGISTRY_HOST }}/images/site-profile
- name: Get Version Info
id: version
run: |
echo "version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
echo "commit=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
if git describe --tags --exact-match HEAD 2>/dev/null; then
echo "is_release=true" >> $GITHUB_OUTPUT
else
echo "is_release=false" >> $GITHUB_OUTPUT
fi
- name: Build and Push Image
uses: docker/build-push-action@v7
with:
context: .
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
VERSION=${{ steps.version.outputs.version }}
COMMIT_SHA=${{ steps.version.outputs.commit }}
IS_RELEASE=${{ steps.version.outputs.is_release }}
file: ./Dockerfile
cache-from: type=gha
cache-to: type=gha,mode=max
- name: ntfy Success
uses: niniyas/ntfy-action@master
if: success()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Success - Site Profile'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: 'Image for Site Profile has been released!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Image for Site Profile has failed to be released.'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yml", "clear": true}]'
image: true

View File

@@ -0,0 +1,340 @@
name: release-image
on:
push:
branches:
- release
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: release
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 24.14.0
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Cache Astro Build Cache
uses: actions/cache@v5
with:
path: |
.astro
node_modules/.vite
key: ${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}
restore-keys: |
${{ runner.os }}-astro-
- name: Lint Code
run: bun run lint
- name: Build Project
run: bun run build
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Test Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'During release tests failed for building Site Profile'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
image: true
guarddog:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: release
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install GuardDog
run: |
python3 -m pip install --upgrade pip
python3 -m pip install guarddog
- name: Run GuardDog
run: |
guarddog npm scan ./
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Security Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'During release guarddog scan failed for Site Profile'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
image: true
semantic-release:
needs: [ build, guarddog ]
runs-on: ubuntu-js
outputs:
new-release-published: ${{ steps.semantic.outputs.new-release-published }}
new-release-version: ${{ steps.semantic.outputs.new-release-version }}
new-release-git-tag: ${{ steps.semantic.outputs.new-release-git-tag }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.BOT_TOKEN }}
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 24.14.0
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Run Semantic Release
id: semantic
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}
NODE_PATH: ${{ github.workspace }}/node_modules
run: |
bun run semantic-release
release-harbor:
runs-on: ubuntu-js
needs: semantic-release
if: ${{ needs.semantic-release.outputs.new-release-published == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: release
- name: Login to Harbor Registry
uses: docker/login-action@v4
with:
registry: ${{ vars.REGISTRY_HOST }}
username: ${{ vars.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_SECRET }}
- name: Login to Docker
uses: docker/login-action@v4
with:
registry: ${{ vars.DH_REGISTRY }}
username: ${{ secrets.DH_USERNAME }}
password: ${{ secrets.DH_TOKEN }}
- name: Create Kubeconfig
run: |
mkdir $HOME/.kube
echo "${{ secrets.KUBECONFIG_BUILDX }}" > $HOME/.kube/config
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v4
with:
driver: kubernetes
driver-opts: |
namespace=gitea
qemu.install=true
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["harbor.alexlebens.net/proxy-hub.docker/"]
- name: Available Platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v6
with:
images: |
${{ vars.REGISTRY_HOST }}/images/site-profile
tags: |
type=ref,event=branch
type=sha,format=long
type=raw,value=latest,enable=${{ needs.semantic-release.outputs.new-release-published == 'true' }}
type=semver,pattern={{version}},value=${{ needs.semantic-release.outputs.new-release-version }}
type=semver,pattern={{major}}.{{minor}},value=${{ needs.semantic-release.outputs.new-release-version }}
type=semver,pattern={{major}},value=${{ needs.semantic-release.outputs.new-release-version }}
- name: Build and Push Image
uses: docker/build-push-action@v7
with:
context: .
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_VERSION=${{ needs.semantic-release.outputs.new-release-version }}
COMMIT_SHA=${{ github.sha }}
IS_RELEASE=true
file: ./Dockerfile
cache-from: type=gha
cache-to: type=gha,mode=max
- name: ntfy Success
uses: niniyas/ntfy-action@master
if: success()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Success - Site Profile'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: 'Harbor Image for Site Profile has been released!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Harbor Image for Site Profile has failed to be released.'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yml", "clear": true}]'
image: true
release-gitea:
runs-on: ubuntu-js
needs: [ semantic-release, release-harbor ]
if: |
always() &&
needs.semantic-release.outputs.new-release-published == 'true'
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: release
- name: Login to Gitea Registry
uses: docker/login-action@v4
with:
registry: ${{ vars.REPOSITORY_HOST }}
username: ${{ gitea.actor }}
password: ${{ secrets.REPOSITORY_TOKEN }}
- name: Login to Docker
uses: docker/login-action@v4
with:
registry: ${{ vars.DH_REGISTRY }}
username: ${{ secrets.DH_USERNAME }}
password: ${{ secrets.DH_TOKEN }}
- name: Create Kubeconfig
run: |
mkdir $HOME/.kube
echo "${{ secrets.KUBECONFIG_BUILDX }}" > $HOME/.kube/config
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v4
with:
driver: kubernetes
driver-opts: |
namespace=gitea
qemu.install=true
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["harbor.alexlebens.net/proxy-hub.docker/"]
- name: Available Platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v6
with:
images: |
${{ vars.REPOSITORY_HOST }}/${{ gitea.repository }}
tags: |
type=ref,event=branch
type=sha,format=long
type=raw,value=latest,enable=${{ needs.semantic-release.outputs.new-release-published == 'true' }}
type=semver,pattern={{version}},value=${{ needs.semantic-release.outputs.new-release-version }}
type=semver,pattern={{major}}.{{minor}},value=${{ needs.semantic-release.outputs.new-release-version }}
type=semver,pattern={{major}},value=${{ needs.semantic-release.outputs.new-release-version }}
- name: Build and Push Image
uses: docker/build-push-action@v7
with:
context: .
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_VERSION=${{ needs.semantic-release.outputs.new-release-version }}
COMMIT_SHA=${{ github.sha }}
IS_RELEASE=true
file: ./Dockerfile
cache-from: type=gha
cache-to: type=gha,mode=max
- name: ntfy Success
uses: niniyas/ntfy-action@master
if: success()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Success - Site Profile'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: 'Gitea Image for Site Profile has been released!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Release Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Gitea Image for Site Profile has failed to be released.'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
image: true

View File

@@ -4,6 +4,9 @@ on:
push: push:
branches: branches:
- main - main
paths-ignore:
- '.gitea/workflows/**'
- '**.md'
pull_request: pull_request:
branches: branches:
@@ -16,16 +19,16 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Set up Node - name: Set up Node
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: 24.14.0 node-version: 24.14.0
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Install Dependencies - name: Install Dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile

17
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,17 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-json
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.11
hooks:
- id: prettier
types_or: [javascript, typescript, css, scss, html, json, yaml, markdown]
additional_dependencies:
- prettier
- prettier-plugin-astro
- prettier-plugin-tailwindcss

18
.releaserc.json Normal file
View File

@@ -0,0 +1,18 @@
{
"branches": ["release"],
"tagFormat": "${version}",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"semantic-release-export-data",
["@semantic-release/npm", { "npmPublish": false }],
["@semantic-release/git", {
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}],
["@saithodev/semantic-release-gitea", {
"giteaUrl": "https://gitea.alexlebens.dev"
}]
]
}

0
CHANGELOG.md Normal file
View File

View File

@@ -1,4 +1,6 @@
FROM dhi.io/bun:1.3.10-debian13-dev AS builder ARG REGISTRY=dhi.io
FROM ${REGISTRY}/bun:1.3.10-alpine3.22-dev AS builder
WORKDIR /app WORKDIR /app
COPY package.json bun.lock ./ COPY package.json bun.lock ./
@@ -15,16 +17,19 @@ FROM build-deps AS build
COPY . . COPY . .
RUN bun run build RUN bun run build
FROM dhi.io/bun:1.3.10-alpine3.22 AS runtime FROM ${REGISTRY}/bun:1.3.10-alpine3.22 AS runtime
WORKDIR /app WORKDIR /app
COPY --from=prod-deps /app/node_modules /app/node_modules COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist COPY --from=build /app/dist /app/dist
LABEL version="3.6.0" ARG APP_VERSION=latest
LABEL description="Astro based personal website"
ENV NODE_ENV=production
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV PORT=4321 ENV PORT=4321
LABEL version=$APP_VERSION
LABEL description="Astro based personal website"
EXPOSE $PORT EXPOSE $PORT
CMD ["bun", "run", "./dist/server/entry.mjs"] CMD ["bun", "run", "./dist/server/entry.mjs"]

View File

@@ -13,8 +13,6 @@ import { getSiteURL } from './src/support/url';
export default defineConfig({ export default defineConfig({
site: getSiteURL(), site: getSiteURL(),
security: { csp: true },
image: { image: {
remotePatterns: [ remotePatterns: [
{ protocol: 'https', hostname: '*.alexlebens.net' }, { protocol: 'https', hostname: '*.alexlebens.net' },
@@ -26,8 +24,6 @@ export default defineConfig({
} }
}, },
prefetch: true,
integrations: [ integrations: [
react(), react(),
sitemap(), sitemap(),

1132
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -19,8 +19,10 @@
}, },
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"full-dev": "rm -rf dist node_modules .astro && bun install && astro build && astro dev",
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"full-preview": "rm -rf dist node_modules .astro && bun install && astro build && astro preview",
"astro": "astro", "astro": "astro",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx,astro}\"", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx,astro}\"",
"lint": "eslint \"src/**/*.{js,ts,jsx,tsx,astro}\"", "lint": "eslint \"src/**/*.{js,ts,jsx,tsx,astro}\"",
@@ -28,7 +30,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.7", "@astrojs/check": "^0.9.7",
"@astrojs/node": "^10.0.0", "@astrojs/node": "^10.0.1",
"@astrojs/react": "^5.0.0", "@astrojs/react": "^5.0.0",
"@astrojs/rss": "^4.0.17", "@astrojs/rss": "^4.0.17",
"@astrojs/sitemap": "^3.7.1", "@astrojs/sitemap": "^3.7.1",
@@ -43,7 +45,7 @@
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.1",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/unist": "^3.0.3", "@types/unist": "^3.0.3",
"astro": "^6.0.2", "astro": "^6.0.4",
"astro-icon": "^1.1.5", "astro-icon": "^1.1.5",
"dayjs": "^1.11.20", "dayjs": "^1.11.20",
"markdown-it": "^14.1.1", "markdown-it": "^14.1.1",
@@ -63,6 +65,11 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint-react/eslint-plugin": "^2.13.0", "@eslint-react/eslint-plugin": "^2.13.0",
"@saithodev/semantic-release-gitea": "^2.1.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^14.1.0",
"@tailwindcss/forms": "^0.5.11", "@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"@types/markdown-it": "^14.1.2", "@types/markdown-it": "^14.1.2",
@@ -76,7 +83,8 @@
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
"timeago.js": "^4.0.2", "semantic-release": "^25.0.3",
"semantic-release-export-data": "^1.2.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.57.0" "typescript-eslint": "^8.57.0"
} }

View File

@@ -95,5 +95,3 @@ const currentPath = pathname.slice(1);
</div> </div>
</nav> </nav>
</header> </header>
<script is:inline src="/vendor/preline/collapse2.1.0.min.js"></script>

View File

@@ -49,100 +49,88 @@
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches); (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
document.documentElement.classList.toggle('dark', isDark); document.documentElement.classList.toggle('dark', isDark);
}; };
applyTheme(); applyTheme();
document.addEventListener('astro:after-swap', applyTheme);
</script> </script>
<script> <script>
function setupThemeToggle() { if (!document.querySelector('.theme-switch-overlay')) {
const themeToggles = document.querySelectorAll('[data-theme-toggle]'); const overlay = document.createElement('div');
overlay.className = 'theme-switch-overlay fixed inset-0 pointer-events-none z-50';
// Create theme switch overlay element overlay.style.opacity = '0';
if (!document.querySelector('.theme-switch-overlay')) { overlay.style.transition = 'opacity 0.15s ease-out';
const overlay = document.createElement('div'); document.body.appendChild(overlay);
overlay.className = 'theme-switch-overlay fixed inset-0 pointer-events-none z-50';
overlay.style.opacity = '0';
overlay.style.transition = 'opacity 0.15s ease-out';
document.body.appendChild(overlay);
}
themeToggles.forEach((toggle) => {
['click', 'touchend'].forEach((eventType) => {
toggle.addEventListener(
eventType,
(e) => {
e.preventDefault();
e.stopPropagation();
// Get click/touch position for radial animation
let x, y;
if (e.type === 'touchend' && e.changedTouches && e.changedTouches[0]) {
const rect = toggle.getBoundingClientRect();
x = e.changedTouches[0].clientX - rect.left;
y = e.changedTouches[0].clientY - rect.top;
} else {
const rect = toggle.getBoundingClientRect();
x = e.clientX - rect.left;
y = e.clientY - rect.top;
}
document.documentElement.style.setProperty('--x', `${x}px`);
document.documentElement.style.setProperty('--y', `${y}px`);
const overlay = document.querySelector('.theme-switch-overlay');
const isDark = document.documentElement.classList.contains('dark');
const newTheme = isDark ? 'light' : 'dark';
// Show overlay during transition
if (overlay) {
overlay.style.backgroundColor =
newTheme === 'dark' ? 'rgba(24, 24, 27, 0.3)' : 'rgba(255, 255, 255, 0.3)';
overlay.style.opacity = '1';
}
document.documentElement.classList.add('theme-switching');
// Force a reflow to ensure all elements update
document.body.offsetHeight;
// Toggle dark mode with a slight delay to allow overlay to appear
setTimeout(() => {
if (isDark) {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
localStorage.setItem('theme', newTheme);
document.dispatchEvent(
new CustomEvent('themeChanged', {
detail: { isDark: newTheme === 'dark' },
})
);
// Force another reflow to ensure all elements update
document.body.offsetHeight;
setTimeout(() => {
if (overlay) {
overlay.style.opacity = '0';
}
document.documentElement.classList.remove('theme-switching');
}, 150);
}, 50);
},
{ passive: false }
);
});
});
} }
['click', 'touchend'].forEach((eventType) => {
document.addEventListener(
eventType,
(e) => {
const target = e.target as HTMLElement;
const toggle = target.closest('[data-theme-toggle]');
// Run setup on load if (!toggle) return;
document.addEventListener('astro:page-load', setupThemeToggle);
e.preventDefault();
let x, y;
const rect = toggle.getBoundingClientRect();
if (eventType === 'touchend') {
const touchEvent = e as TouchEvent;
if (touchEvent.changedTouches && touchEvent.changedTouches[0]) {
x = touchEvent.changedTouches[0].clientX - rect.left;
y = touchEvent.changedTouches[0].clientY - rect.top;
}
} else {
const mouseEvent = e as MouseEvent;
x = mouseEvent.clientX - rect.left;
y = mouseEvent.clientY - rect.top;
}
document.documentElement.style.setProperty('--x', `${x}px`);
document.documentElement.style.setProperty('--y', `${y}px`);
const overlay = document.querySelector('.theme-switch-overlay') as HTMLElement;
const isDark = document.documentElement.classList.contains('dark');
const newTheme = isDark ? 'light' : 'dark';
if (overlay) {
overlay.style.backgroundColor =
newTheme === 'dark' ? 'rgba(24, 24, 27, 0.3)' : 'rgba(255, 255, 255, 0.3)';
overlay.style.opacity = '1';
}
document.documentElement.classList.add('theme-switching');
document.body.offsetHeight;
setTimeout(() => {
if (isDark) {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
localStorage.setItem('theme', newTheme);
document.dispatchEvent(
new CustomEvent('themeChanged', {
detail: { isDark: newTheme === 'dark' },
})
);
document.body.offsetHeight;
setTimeout(() => {
if (overlay) {
overlay.style.opacity = '0';
}
document.documentElement.classList.remove('theme-switching');
}, 150);
}, 50);
},
{ passive: false }
);
});
// Also run on page visibility change to ensure theme is consistent
document.addEventListener('visibilitychange', () => { document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') { if (document.visibilityState === 'visible') {
const currentTheme = localStorage.getItem('theme'); const currentTheme = localStorage.getItem('theme');
@@ -154,7 +142,6 @@
} }
}); });
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ({ matches }) => { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ({ matches }) => {
if (!localStorage.getItem('theme')) { if (!localStorage.getItem('theme')) {
if (matches) { if (matches) {

View File

@@ -22,8 +22,8 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
data-astro-prefetch data-astro-prefetch
> >
<div class="relative grow overflow-hidden"> <div class="relative grow overflow-hidden">
<div class="absolute inset-1 flex flex-col p-3 md:p-4 lg:p-5"> <div class="mask-fade-edges absolute inset-1 flex flex-col p-3 md:p-4 lg:p-5">
<div class="flex flex-row items-center mb-4"> <div class="flex flex-row items-center mb-4 shrink-0">
<div class="card-hover-icon-scale shrink-0 mr-3"> <div class="card-hover-icon-scale shrink-0 mr-3">
<Logo <Logo
srcLight={getDirectusImageURL(logoLight)} srcLight={getDirectusImageURL(logoLight)}
@@ -35,12 +35,12 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
{title} {title}
</h3> </h3>
</div> </div>
<div> <div class="overflow-hidden">
<p class="card-text-description mb-4"> <p class="card-text-description line-clamp-3">
{description} {description}
</p> </p>
</div> </div>
<div class="card-text-description flex items-center justify-between text-xs mt-auto pt-1 md:pt-2"> <div class="card-text-description flex shrink-0 items-center justify-between text-xs mt-auto pt-1 md:pt-2">
<span class="inline-flex items-center"> <span class="inline-flex items-center">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -82,7 +82,7 @@ const images = await Promise.all(imagesData.map(async (img) => ({
</section> </section>
<script> <script>
document.addEventListener('astro:page-load', () => { function initHeroImage() {
const container = document.getElementById('hero-image-container'); const container = document.getElementById('hero-image-container');
if (container) { if (container) {
const images = container.querySelectorAll('.hero-image'); const images = container.querySelectorAll('.hero-image');
@@ -96,5 +96,13 @@ const images = await Promise.all(imagesData.map(async (img) => ({
images[randomIndex].classList.add('flex'); images[randomIndex].classList.add('flex');
} }
} }
}); }
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initHeroImage);
} else {
initHeroImage();
}
if ((window as any).swup) {
(window as any).swup.hooks.on('page:view', initHeroImage);
}
</script> </script>

View File

@@ -18,7 +18,7 @@ const global = await directus.request(readSingleton('site_global'));
const { posts, title, subTitle } = Astro.props; const { posts, title, subTitle } = Astro.props;
--- ---
<section class="max-w-340 2xl:max-w-full px-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-8"> <section class="max-w-340 2xl:max-w-full px-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-4">
<div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14"> <div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14">
<h1 class="smooth-reveal card-text-header block"> <h1 class="smooth-reveal card-text-header block">
{title} {title}

View File

@@ -11,7 +11,7 @@ interface Props {
const { posts } = Astro.props; const { posts } = Astro.props;
--- ---
<section class="smooth-reveal flex flex-col gap-4 md:mb-20"> <section class="smooth-reveal-cards flex flex-col gap-4 md:mb-20">
{posts.map((post, index) => index % 2 === 0 ? ( {posts.map((post, index) => index % 2 === 0 ? (
<LargeBlogLeftCard post={post}/> <LargeBlogLeftCard post={post}/>
) : ( ) : (

View File

@@ -20,7 +20,7 @@ const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, ti
</div> </div>
{error ? ( {error ? (
<div class="card-base p-10 text-accent text-center"> <div class="card-base p-10 text-accent text-center">
Sorry, {error.toLowerCase} Sorry, {error.toLowerCase()}
</div> </div>
) : ( ) : (
<div class="flex flex-wrap justify-center gap-4 lg:gap-6"> <div class="flex flex-wrap justify-center gap-4 lg:gap-6">

View File

@@ -1,5 +1,4 @@
--- ---
import { ClientRouter } from 'astro:transitions';
import { readSingleton } from '@directus/sdk'; import { readSingleton } from '@directus/sdk';
import BaseHead from '@components/BaseHead.astro'; import BaseHead from '@components/BaseHead.astro';
@@ -39,8 +38,7 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
structuredData={structuredData} structuredData={structuredData}
/> />
<ClientRouter fallback="swap" /> <!-- Set Theme -->
<script is:inline> <script is:inline>
const theme = (() => { const theme = (() => {
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) { if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
@@ -60,6 +58,12 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
window.localStorage.setItem('theme', theme); window.localStorage.setItem('theme', theme);
</script> </script>
<!-- Preline -->
<script
src="/vendor/preline/collapse2.1.0.min.js"
is:inline
/>
<!-- Rybbit Tracking Snippet --> <!-- Rybbit Tracking Snippet -->
<script <script
src="https://rybbit.alexlebens.dev/api/script.js" src="https://rybbit.alexlebens.dev/api/script.js"
@@ -104,7 +108,52 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
} }
}; };
resetScroll(); resetScroll();
document.addEventListener('astro:after-swap', resetScroll);
document.addEventListener('swup:page:view', () => {
resetScroll();
if (typeof (window as any).HSStaticMethods !== 'undefined') {
(window as any).HSStaticMethods.autoInit();
}
if (typeof (window as any).rybbit === 'function') {
(window as any).rybbit('trackPageview');
}
});
</script>
<script>
const animateContent = () => {
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(() => {
el.classList.add('animate-reveal');
}, 50 + index * 100);
});
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(() => {
el.classList.add('animate-reveal');
}, 200 + index * 250);
});
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(() => {
el.classList.add('animate-reveal');
}, 400 + index * 250);
});
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(() => {
el.classList.add('animate-reveal-fade');
}, 100 + index * 250);
});
};
animateContent();
document.addEventListener('swup:page:view', animateContent);
</script> </script>
<style> <style>

View File

@@ -86,24 +86,6 @@ const global = await directus.request(readSingleton('site_global'));
const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)]; const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)];
funFactElement.textContent = randomFact; funFactElement.textContent = randomFact;
} }
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
100 + index * 150
);
});
};
animateContent();
});
</script> </script>
<style> <style>

View File

@@ -47,33 +47,3 @@ const global = await directus.request(readSingleton('site_global'));
</section> </section>
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
});
</script>

View File

@@ -52,56 +52,3 @@ const posts = await directus.request(
</section> </section>
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
// Animate group 2
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
200 + index * 150
);
});
// Animate topic cards with staggered delay
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
500 + index * 100
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
});
</script>

View File

@@ -37,23 +37,3 @@ const global = await directus.request(readSingleton('site_global'));
<ApplicationSection className="smooth-reveal-2" /> <ApplicationSection className="smooth-reveal-2" />
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
};
animateContent();
});
</script>

View File

@@ -174,21 +174,4 @@ const content = marked.parse(post.content || '');
lightbox.init(); lightbox.init();
} }
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
100 + index * 100
);
});
};
animateContent();
});
</script> </script>

View File

@@ -57,56 +57,3 @@ const recentPosts: Post[] = posts.filter(
/> />
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
200 + index * 300
);
});
// Animate group 2
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
500 + index * 100
);
});
// Animate topic cards with staggered delay
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
1000 + index * 250
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
});
</script>

View File

@@ -75,56 +75,3 @@ const categoriesPosts = posts
</section> </section>
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
// Animate group 2
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
200 + index * 150
);
});
// Animate topic cards with staggered delay
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
500 + index * 100
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
});
</script>

View File

@@ -37,56 +37,3 @@ const global = await directus.request(readSingleton('site_global'));
<CategorySection /> <CategorySection />
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
// Animate group 2
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
200 + index * 150
);
});
// Animate topic cards with staggered delay
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
500 + index * 100
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
});
</script>

View File

@@ -55,6 +55,12 @@ const recentPosts = posts
<FeatureSection /> <FeatureSection />
<RecentPostsSection
posts={recentPosts}
title="Latest Posts"
subTitle="Checkout my most recent thoughts here"
/>
<WeatherSection <WeatherSection
server:defer server:defer
latitude={weather.latitude} latitude={weather.latitude}
@@ -63,12 +69,6 @@ const recentPosts = posts
timezone={weather.timezone} timezone={weather.timezone}
/> />
<RecentPostsSection
posts={recentPosts}
title="Latest Posts"
subTitle="Checkout my most recent thoughts here"
/>
<GiteaSection <GiteaSection
title="Follow me on Gitea" title="Follow me on Gitea"
subTitle="I love open source and have my code availabile on my Gitea server." subTitle="I love open source and have my code availabile on my Gitea server."
@@ -76,62 +76,3 @@ const recentPosts = posts
/> />
</BaseLayout> </BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
// Animate group 2
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
200 + index * 250
);
});
// Animate topic cards with staggered delay
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
400 + index * 250
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
const observer = new MutationObserver(() => {
animateContent();
});
observer.observe(document.body, { childList: true, subtree: true });
});
</script>

View File

@@ -138,3 +138,15 @@
bg-neutral-100 dark:bg-neutral-800 bg-neutral-100 dark:bg-neutral-800
shadow-xs dark:shadow-md shadow-xs dark:shadow-md
} }
@utility mask-fade-edges {
-webkit-mask-image:
linear-gradient(to right, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent),
linear-gradient(to bottom, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent);
mask-image:
linear-gradient(to right, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent),
linear-gradient(to bottom, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent);
-webkit-mask-composite: source-in;
mask-composite: intersect;
}

View File

@@ -1,50 +0,0 @@
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
// Animate group 1
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
50 + index * 100
);
});
// Animate group 2
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
smoothReveal2.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
200 + index * 250
);
});
// Animate topic cards with staggered delay
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
smoothRevealCards.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
400 + index * 250
);
});
// Animate with just fade in with staggered delay
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
smoothRevealFade.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal-fade');
},
100 + index * 250
);
});
};
animateContent();
});