Compare commits

..

54 Commits

Author SHA1 Message Date
8d1d47dc4f fix: handle script with swup
All checks were successful
test-build / guarddog (push) Successful in 33s
renovate / renovate (push) Successful in 49s
test-build / build (push) Successful in 1m41s
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
03f74a8181 feat: release 3.6.0
Some checks failed
renovate / renovate (push) Successful in 30s
test-build / guarddog (push) Successful in 1m3s
test-build / build (push) Failing after 1m10s
release-image-harbor / build (push) Failing after 1m30s
release-image-harbor / release (push) Has been skipped
release-image-gitea / build (push) Failing after 2m10s
release-image-gitea / release (push) Has been skipped
2026-03-12 12:42:45 -05:00
405fdf297c feat: replace timeago with dayjs
All checks were successful
renovate / renovate (push) Successful in 49s
test-build / guarddog (push) Successful in 1m8s
test-build / build (push) Successful in 3m24s
2026-03-12 12:35:23 -05:00
5b6b6e479f feat: responsive for small screen 2026-03-11 22:44:11 -05:00
04344808bd feat: enable security feature
All checks were successful
test-build / guarddog (push) Successful in 20s
renovate / renovate (push) Successful in 35s
test-build / build (push) Successful in 2m13s
release-image-gitea / build (push) Successful in 2m27s
release-image-gitea / release (push) Successful in 5m2s
release-image-harbor / build (push) Successful in 1m17s
release-image-harbor / release (push) Successful in 2m35s
2026-03-11 21:50:38 -05:00
6ec27345c3 feat: release 3.5.0 2026-03-11 19:44:49 -05:00
5e02443409 feat: remove security feature 2026-03-11 19:44:30 -05:00
8184d42942 feat: release 3.3.0
All checks were successful
test-build / guarddog (push) Successful in 45s
renovate / renovate (push) Successful in 51s
test-build / build (push) Successful in 5m44s
release-image-gitea / build (push) Successful in 1m7s
release-image-harbor / build (push) Successful in 1m45s
release-image-gitea / release (push) Successful in 3m14s
release-image-harbor / release (push) Successful in 3m11s
2026-03-11 19:31:54 -05:00
04dfecc099 feat: disable security feature 2026-03-11 19:31:35 -05:00
ec10d45fd0 feat: release 3.2.0
All checks were successful
test-build / guarddog (push) Successful in 43s
test-build / build (push) Successful in 1m28s
release-image-gitea / build (push) Successful in 1m29s
release-image-harbor / build (push) Successful in 1m44s
release-image-gitea / release (push) Successful in 4m38s
renovate / renovate (push) Successful in 1m3s
release-image-harbor / release (push) Successful in 6m0s
2026-03-11 18:54:42 -05:00
ceb70c7049 feat: add client:load
Some checks failed
renovate / renovate (push) Successful in 29s
test-build / build (push) Has been cancelled
test-build / guarddog (push) Has been cancelled
2026-03-11 18:54:07 -05:00
4dbc5d12a3 feat: remove static robots 2026-03-11 18:53:56 -05:00
b55c3a0e31 feat: remove partytown int
All checks were successful
test-build / guarddog (push) Successful in 17s
renovate / renovate (push) Successful in 23s
test-build / build (push) Successful in 1m25s
2026-03-11 02:04:32 -05:00
e63abf03ef feat: remove partytown int
Some checks failed
renovate / renovate (push) Successful in 25s
test-build / build (push) Failing after 29s
test-build / guarddog (push) Successful in 42s
2026-03-11 02:01:40 -05:00
a7e7e5b0e8 feat: add security feature
Some checks failed
test-build / guarddog (push) Successful in 23s
renovate / renovate (push) Successful in 43s
test-build / build (push) Failing after 1m50s
2026-03-11 01:49:07 -05:00
96724d0016 chore(deps): update deps 2026-03-11 01:47:26 -05:00
30b2e980c0 feat: change cache path
All checks were successful
test-build / guarddog (push) Successful in 20s
renovate / renovate (push) Successful in 34s
test-build / build (push) Successful in 1m9s
2026-03-11 01:25:58 -05:00
113f42ca21 feat: setup node
All checks were successful
test-build / guarddog (push) Successful in 25s
renovate / renovate (push) Successful in 59s
test-build / build (push) Successful in 1m31s
release-image-gitea / build (push) Successful in 1m33s
release-image-harbor / build (push) Successful in 1m48s
release-image-gitea / release (push) Successful in 3m53s
release-image-harbor / release (push) Successful in 4m40s
2026-03-11 01:09:20 -05:00
1f2820e4b4 feat: convert to bun
Some checks failed
test-build / guarddog (push) Successful in 19s
renovate / renovate (push) Successful in 53s
test-build / build (push) Failing after 1m20s
2026-03-11 01:03:51 -05:00
dc088306ce feat: update workflow to major version
All checks were successful
test-build / guarddog (push) Successful in 13s
renovate / renovate (push) Successful in 40s
test-build / build (push) Successful in 1m57s
2026-03-11 00:15:25 -05:00
f6c1cf1bf5 chore(deps): update deps
All checks were successful
renovate / renovate (push) Successful in 30s
test-build / guarddog (push) Successful in 25s
test-build / build (push) Successful in 2m10s
2026-03-11 00:00:51 -05:00
962f354208 feat: release 3.0.0, major astro update to 6.0
Some checks failed
renovate / renovate (push) Successful in 44s
test-build / guarddog (push) Successful in 1m6s
test-build / build (push) Has been cancelled
2026-03-10 23:58:53 -05:00
bf43212afc Merge pull request 'fix(deps): update astro monorepo (major)' (#376) from renovate/major-astro-monorepo into main
Some checks failed
test-build / guarddog (push) Successful in 21s
renovate / renovate (push) Successful in 36s
test-build / build (push) Failing after 3m19s
Reviewed-on: #376
2026-03-11 04:55:31 +00:00
38 changed files with 4546 additions and 13762 deletions

View File

@@ -1,156 +0,0 @@
name: release-image-gitea
on:
push:
tags:
- 2.*
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10.x
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 24.14.0
cache: pnpm
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Cache Astro Build Cache
uses: actions/cache@v5
with:
path: |
node_modules/.cache
.astro/cache
key: ${{ runner.os }}-astro-cache-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-astro-cache-${{ hashFiles('**/pnpm-lock.yaml') }}-
${{ runner.os }}-astro-cache-
- name: Lint Code
run: pnpm lint
- name: Build Project
run: pnpm 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,156 +0,0 @@
name: release-image-harbor
on:
push:
tags:
- 2.*
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10.x
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 24.14.0
cache: pnpm
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Cache Astro Build Cache
uses: actions/cache@v5
with:
path: |
node_modules/.cache
.astro/cache
key: ${{ runner.os }}-astro-cache-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-astro-cache-${{ hashFiles('**/pnpm-lock.yaml') }}-
${{ runner.os }}-astro-cache-
- name: Lint Code
run: pnpm lint
- name: Build Project
run: pnpm 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:
branches:
- main
paths-ignore:
- '.gitea/workflows/**'
- '**.md'
pull_request:
branches:
@@ -16,36 +19,34 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10.x
- name: Set up Node.js
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 24.14.0
cache: pnpm
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- name: Install Dependencies
run: pnpm install --frozen-lockfile
run: bun install --frozen-lockfile
- name: Cache Astro Build Cache
uses: actions/cache@v5
with:
path: |
node_modules/.cache
.astro/cache
key: ${{ runner.os }}-astro-cache-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.sha }}
.astro
node_modules/.vite
key: ${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}
restore-keys: |
${{ runner.os }}-astro-cache-${{ hashFiles('**/pnpm-lock.yaml') }}-
${{ runner.os }}-astro-cache-
${{ runner.os }}-astro-
- name: Lint Code
run: pnpm lint
run: bun run lint
- name: Build Project
run: pnpm build
run: bun run build
- name: ntfy Failed
uses: niniyas/ntfy-action@master

2
.gitignore vendored
View File

@@ -10,8 +10,6 @@ node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env

3
.npmrc
View File

@@ -1,3 +0,0 @@
registry=https://registry.npmjs.org/
engine-strict=true
save-exact=true

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,32 +1,35 @@
FROM docker.io/node:24.14.0-alpine AS builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
ARG REGISTRY=dhi.io
FROM ${REGISTRY}/bun:1.3.10-alpine3.22-dev AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
COPY package.json bun.lock ./
FROM builder AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
RUN --mount=type=cache,id=bun,target=/root/.bun/install/cache \
bun install --production --frozen-lockfile
FROM prod-deps AS build-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
FROM builder AS build-deps
RUN --mount=type=cache,id=bun,target=/root/.bun/install/cache \
bun install --frozen-lockfile
FROM build-deps AS build
COPY . .
RUN pnpm run build
RUN bun run build
FROM dhi.io/node:24.14.0 AS runtime
FROM ${REGISTRY}/bun:1.3.10-alpine3.22 AS runtime
WORKDIR /app
COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist
LABEL version="2.25.0"
LABEL description="Astro based personal website"
ARG APP_VERSION=latest
ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=4321
LABEL version=$APP_VERSION
LABEL description="Astro based personal website"
EXPOSE $PORT
CMD ["node", "./dist/server/entry.mjs"]
CMD ["bun", "run", "./dist/server/entry.mjs"]

View File

@@ -7,9 +7,8 @@ Personal site used for information about myself and blog.
With dependencies installed, you can utilize the following npm scripts to manage your project's development lifecycle:
- `pnpm build`: Bundles your site into static files for production.
- `pnpm dev`: Starts a local development server with hot reloading enabled.
- `pnpm preview`: Serves your build output locally for preview before deployment.
- `bun run build`: Bundles your site into static files for production.
- `bun run dev`: Starts a local development server with hot reloading enabled.
For detailed help with Astro CLI commands, visit [Astro's documentation](https://docs.astro.build/en/reference/cli-reference/).

View File

@@ -1,7 +1,6 @@
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
import partytown from '@astrojs/partytown';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
@@ -25,10 +24,7 @@ export default defineConfig({
}
},
prefetch: true,
integrations: [
partytown(),
react(),
sitemap(),
icon({

3958
bun.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "site-profile",
"type": "module",
"version": "2.25.0",
"version": "3.6.0",
"homepage": "https://www.alexlebens.dev",
"bugs": {
"url": "https://gitea.alexlebens.dev/alexlebens/site-profile/issues",
@@ -19,8 +19,10 @@
},
"scripts": {
"dev": "astro dev",
"full-dev": "rm -rf dist node_modules .astro && bun install && astro build && astro dev",
"build": "astro build",
"preview": "astro preview",
"full-preview": "rm -rf dist node_modules .astro && bun install && astro build && astro preview",
"astro": "astro",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx,astro}\"",
"lint": "eslint \"src/**/*.{js,ts,jsx,tsx,astro}\"",
@@ -28,8 +30,7 @@
},
"dependencies": {
"@astrojs/check": "^0.9.7",
"@astrojs/node": "^10.0.0",
"@astrojs/partytown": "^2.1.5",
"@astrojs/node": "^10.0.1",
"@astrojs/react": "^5.0.0",
"@astrojs/rss": "^4.0.17",
"@astrojs/sitemap": "^3.7.1",
@@ -44,9 +45,10 @@
"@tailwindcss/vite": "^4.2.1",
"@types/react": "^19.2.14",
"@types/unist": "^3.0.3",
"astro": "^6.0.0",
"astro": "^6.0.4",
"astro-icon": "^1.1.5",
"markdown-it": "14.1.1",
"dayjs": "^1.11.20",
"markdown-it": "^14.1.1",
"marked": "^17.0.4",
"marked-shiki": "^1.2.1",
"mdast-util-to-string": "^4.0.0",
@@ -63,9 +65,14 @@
},
"devDependencies": {
"@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/typography": "^0.5.19",
"@types/markdown-it": "14.1.2",
"@types/markdown-it": "^14.1.2",
"eslint": "^10.0.3",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-astro": "^1.6.0",
@@ -76,7 +83,8 @@
"prettier": "^3.8.1",
"prettier-plugin-astro": "^0.14.1",
"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-eslint": "^8.57.0"
}

12845
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
onlyBuiltDependencies:
- swup

View File

@@ -1,4 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://www.alexlebens.dev/sitemap-index.xml

View File

@@ -95,5 +95,3 @@ const currentPath = pathname.slice(1);
</div>
</nav>
</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);
document.documentElement.classList.toggle('dark', isDark);
};
applyTheme();
document.addEventListener('astro:after-swap', applyTheme);
</script>
<script>
function setupThemeToggle() {
const themeToggles = document.querySelectorAll('[data-theme-toggle]');
// Create theme switch overlay element
if (!document.querySelector('.theme-switch-overlay')) {
const overlay = document.createElement('div');
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 }
);
});
});
if (!document.querySelector('.theme-switch-overlay')) {
const overlay = document.createElement('div');
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);
}
['click', 'touchend'].forEach((eventType) => {
document.addEventListener(
eventType,
(e) => {
const target = e.target as HTMLElement;
const toggle = target.closest('[data-theme-toggle]');
if (!toggle) return;
// Run setup on load
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', () => {
if (document.visibilityState === 'visible') {
const currentTheme = localStorage.getItem('theme');
@@ -154,7 +142,6 @@
}
});
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ({ matches }) => {
if (!localStorage.getItem('theme')) {
if (matches) {

View File

@@ -22,8 +22,8 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
data-astro-prefetch
>
<div class="relative grow overflow-hidden">
<div class="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="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 shrink-0">
<div class="card-hover-icon-scale shrink-0 mr-3">
<Logo
srcLight={getDirectusImageURL(logoLight)}
@@ -35,12 +35,12 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
{title}
</h3>
</div>
<div>
<p class="card-text-description mb-4">
<div class="overflow-hidden">
<p class="card-text-description line-clamp-3">
{description}
</p>
</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">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -55,7 +55,7 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
</svg>
{count}
</span>
<span class="inline-flex items-center">
<div class="inline-flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
@@ -68,8 +68,10 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
{publishDate}
</span>
<span>
{publishDate}
</span>
</div>
</div>
</div>
</div>

View File

@@ -10,7 +10,7 @@ const { dayName, label, icon, temp } = Astro.props;
---
<div class="smooth-reveal-2 group flex flex-col">
<div class="card-base w-40">
<div class="card-base w-32 sm:w-40">
<div class="p-5 text-center">
<span class="card-text-description block font-bold text-xs uppercase tracking-widest">
{dayName}

View File

@@ -6,7 +6,7 @@ import type { Post } from '@lib/directusTypes';
import CategoryCard from '@components/cards/CategoryCard.astro';
import LargeCategoryCard from '@components/cards/LargeCategoryCard.astro';
import directus from '@lib/directus';
import { timeago } from '@support/time';
import { formatFromNow } from '@support/time';
const global = await directus.request(readSingleton('site_global'));
@@ -93,7 +93,7 @@ const categories = (await directus.request(readItems('categories')))
logoLight={category.logoLight}
logoDark={category.logoDark}
count={postMap.get(category.slug)?.length ?? 0}
publishDate={timeago(postMap.get(category.slug)?.[0]?.published_date)}
publishDate={formatFromNow(postMap.get(category.slug)?.[0]?.published_date)}
/>
</div>
);
@@ -106,7 +106,7 @@ const categories = (await directus.request(readItems('categories')))
logoLight={global.all_logoLight}
logoDark={global.all_logoDark}
count={posts.length}
publishDate={timeago(posts[0]?.published_date)}
publishDate={formatFromNow(posts[0]?.published_date)}
/>
</div>
</div>

View File

@@ -82,7 +82,7 @@ const images = await Promise.all(imagesData.map(async (img) => ({
</section>
<script>
document.addEventListener('astro:page-load', () => {
function initHeroImage() {
const container = document.getElementById('hero-image-container');
if (container) {
const images = container.querySelectorAll('.hero-image');
@@ -96,5 +96,13 @@ const images = await Promise.all(imagesData.map(async (img) => ({
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>

View File

@@ -18,7 +18,7 @@ const global = await directus.request(readSingleton('site_global'));
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">
<h1 class="smooth-reveal card-text-header block">
{title}

View File

@@ -11,7 +11,7 @@ interface 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 ? (
<LargeBlogLeftCard post={post}/>
) : (

View File

@@ -20,7 +20,7 @@ const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, ti
</div>
{error ? (
<div class="card-base p-10 text-accent text-center">
Sorry, {error.toLowerCase}
Sorry, {error.toLowerCase()}
</div>
) : (
<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 BaseHead from '@components/BaseHead.astro';
@@ -39,8 +38,7 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
structuredData={structuredData}
/>
<ClientRouter fallback="swap" />
<!-- Set Theme -->
<script is:inline>
const 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);
</script>
<!-- Preline -->
<script
src="/vendor/preline/collapse2.1.0.min.js"
is:inline
/>
<!-- Rybbit Tracking Snippet -->
<script
src="https://rybbit.alexlebens.dev/api/script.js"
@@ -104,7 +108,52 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
}
};
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>
<style>

View File

@@ -86,24 +86,6 @@ const global = await directus.request(readSingleton('site_global'));
const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)];
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>
<style>

View File

@@ -47,33 +47,3 @@ const global = await directus.request(readSingleton('site_global'));
</section>
</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>
</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" />
</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();
}
// 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>

View File

@@ -57,56 +57,3 @@ const recentPosts: Post[] = posts.filter(
/>
</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>
</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 />
</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 />
<RecentPostsSection
posts={recentPosts}
title="Latest Posts"
subTitle="Checkout my most recent thoughts here"
/>
<WeatherSection
server:defer
latitude={weather.latitude}
@@ -63,12 +69,6 @@ const recentPosts = posts
timezone={weather.timezone}
/>
<RecentPostsSection
posts={recentPosts}
title="Latest Posts"
subTitle="Checkout my most recent thoughts here"
/>
<GiteaSection
title="Follow me on Gitea"
subTitle="I love open source and have my code availabile on my Gitea server."
@@ -76,62 +76,3 @@ const recentPosts = posts
/>
</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
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();
});

View File

@@ -1,34 +1,13 @@
import { format, register } from 'timeago.js';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
const TimeAgoConfiguration: string[][] = [
['today', 'today'],
['%s seconds ago', 'in %s seconds'],
['1 minute ago', 'in 1 minute'],
['%s minutes ago', 'in %s minutes'],
['1 hour ago', 'in 1 hour'],
['%s hours ago', 'in %s hours'],
['1 day ago', 'in 1 day'],
['%s days ago', 'in %s days'],
['1 week ago', 'in 1 week'],
['%s weeks ago', 'in %s weeks'],
['1 month ago', 'in 1 month'],
['%s months ago', 'in %s months'],
['1 year ago', 'in 1 year'],
['%s years ago', 'in %s years'],
];
dayjs.extend(relativeTime);
function timeago(date?: Date): string {
function formatFromNow(date: Date | null): string {
if (!date) {
return 'today';
return 'none';
}
const localeFunc = (number: number, index: number, _?: number): [string, string] => {
return TimeAgoConfiguration[index] as [string, string];
};
register('timeago', localeFunc);
return format(date, 'timeago');
return dayjs(date).fromNow()
}
function formatDate(date: Date): string {
@@ -46,4 +25,4 @@ function formatShortDate(date: Date): string {
})
}
export { formatDate, formatShortDate, timeago };
export { formatFromNow, formatDate, formatShortDate, };