Compare commits
245 commits
0.1.14ulh2
...
main
Author | SHA1 | Date | |
---|---|---|---|
ac85131e69 | |||
2c42f18e81 | |||
d56f70c1ba |
|||
36e0a056fa |
|||
31c3f76a9b |
|||
693f0f2eae |
|||
8d6ededac6 |
|||
67e565493c |
|||
cc6b3c22b8 |
|||
13ccaa1977 |
|||
52dd87b52d |
|||
94687c6866 |
|||
ce4e7777e0 |
|||
14cc9e0b7e |
|||
e402b7e321 |
|||
f11a53cd11 |
|||
f7c7408fdd |
|||
3f0d0ab3c4 |
|||
1cbec0fe78 |
|||
3814b179ce |
|||
34df58168b |
|||
d53bae9537 |
|||
1cae67023f |
|||
edb548144a |
|||
a4e306b45a |
|||
2e513cb72f |
|||
120af666cb |
|||
805ce53362 |
|||
08b2227b22 |
|||
66c9220468 |
|||
753a39e13c |
|||
215b803749 |
|||
278befbb50 |
|||
02d5af5a2d |
|||
ae98709260 |
|||
cea02754ce |
|||
63024eb324 |
|||
3da3625d16 |
|||
2ec229f40b |
|||
ba2710f4b3 |
|||
83cda5dbb5 |
|||
b75fb2499e |
|||
d0d43d8caf |
|||
199758f822 |
|||
c50c8eb9ce |
|||
e6ee5799a5 |
|||
3b72c24361 |
|||
88e4b92b8d | |||
c013c7a298 | |||
4c99e009f4 | |||
b71a83de60 |
|||
7e19e5f76b |
|||
62e429329b |
|||
b2e4ac27b6 |
|||
d19ded377f |
|||
7d6f6eb4d5 |
|||
1782b69664 |
|||
aa59137748 |
|||
aed70ccdd9 |
|||
ed8549d6aa |
|||
19492fc41b |
|||
81c1def4c4 |
|||
d5e4818b2b |
|||
e81dbd6048 |
|||
621b17f5b4 |
|||
03e341710f |
|||
eec488ddff |
|||
298b91262e |
|||
51b8817dc7 |
|||
dd78cc13ef |
|||
9db0b71ee4 |
|||
c7ae9fa64b |
|||
c754871a9b |
|||
7f4b1e7259 |
|||
9171fda784 |
|||
2321c5e5ab |
|||
6ff1e5ec1c |
|||
7a3c4f14ae |
|||
f9fb025622 |
|||
cc885e7740 |
|||
7be69a0ce5 |
|||
99fe35aa23 |
|||
d2dddbeb2a |
|||
7222d1c58b |
|||
d9e4f5b5f9 |
|||
19e7d02520 |
|||
fa88fede29 |
|||
a77312c17c |
|||
b0fc3a05c5 |
|||
e717b4b8dd |
|||
86b6263ec1 |
|||
49f270974f |
|||
c3dc83ff1d |
|||
1945b41b4b |
|||
b32fa9e4ad |
|||
afe18e37b6 |
|||
4eb390d0b1 |
|||
7a0863d9a2 |
|||
20c6753ee1 |
|||
cdda005d47 |
|||
34e24346d1 |
|||
aab26e63b2 |
|||
5dfbd2cccd |
|||
8c425d8690 |
|||
4a4d5434f5 |
|||
d6fb3bccbe |
|||
0e637384b6 |
|||
f4e8a39c08 |
|||
cf84c65beb |
|||
90ac6d527c |
|||
da582980b9 |
|||
c0588ee760 |
|||
e11130ce1a |
|||
500080be9f |
|||
9922507975 |
|||
e4252aa6dc |
|||
4cb0f2db10 |
|||
031d057811 |
|||
aaa9611b1c |
|||
8e6be5ae82 |
|||
d93d0416e5 |
|||
564e902b69 |
|||
ed91be36d7 |
|||
3d729731a5 |
|||
2bd68c106c |
|||
34421fdb76 |
|||
d906df3fec |
|||
8a84de8807 |
|||
014ea40dce |
|||
0473c836d7 |
|||
3587ee38a7 |
|||
c0d6d8a516 |
|||
4387207c14 |
|||
f26fdbe579 |
|||
2696d0e146 |
|||
0b7b7fa071 |
|||
2d07dbbbdb |
|||
6e3d23e8f7 |
|||
86ee37e441 |
|||
9f225b0708 |
|||
4f16151149 |
|||
9718bc07b6 |
|||
f82341135c |
|||
acbb7489e5 |
|||
ab2b69aff3 |
|||
ee6c695d33 |
|||
974f259804 |
|||
f3ff40b6f1 |
|||
58ae955f66 |
|||
7948fac47a |
|||
7ad7efe0e2 |
|||
37a7e99e78 |
|||
d91e9c4af5 |
|||
45f8b2bfd8 |
|||
52923ad819 |
|||
e020bb1c18 |
|||
aad69a3e77 |
|||
93ea52b3a6 |
|||
4d4271a5da |
|||
1575997c4d |
|||
3fbb0d0196 |
|||
e859576215 |
|||
8ab6361b6d |
|||
950eb1d9f3 |
|||
61ce9f8575 |
|||
3b307e48c7 |
|||
96a9c2593c |
|||
1f7709031c |
|||
9d8493273d |
|||
676e5e2b00 |
|||
ab5630602d |
|||
6b1b3ab6f3 |
|||
d75fd91634 |
|||
a7bb38b89a |
|||
c72ffef3fc |
|||
5007af517b |
|||
a9568375c1 |
|||
de68104603 |
|||
245cac7d87 |
|||
60330800f9 |
|||
7a425c4fda |
|||
186b797bb6 |
|||
bc5f43802f |
|||
be1416a74a |
|||
1cee0efcad |
|||
45fd654ef1 |
|||
e4b0b87e9f |
|||
cc55ec3a87 |
|||
08ae371dbf |
|||
a73afacf6c |
|||
0dca43eb19 |
|||
9cb6ff6ed7 |
|||
700aa1d223 |
|||
d06655296b |
|||
beae91e5fd |
|||
394e575ca6 |
|||
1aa9753d8b |
|||
c4d73c2f98 |
|||
2decf6604b |
|||
f5bb4bf78e | |||
5181739230 |
|||
449006320c |
|||
483b2cc84b |
|||
9daf38e537 |
|||
da8564f01a |
|||
dd9d0a662f |
|||
2a258ac60b | |||
0494694cc9 | |||
764c5750a6 |
|||
b7db7dd512 | |||
fcd05dcb09 |
|||
7ed78bceff |
|||
e406bd8764 | |||
064eb975b9 |
|||
d7eb7e9c49 | |||
3347d2eaf2 |
|||
3dbdca627e |
|||
f2ff4e47cf | |||
340f39710e |
|||
4b3a8898f6 |
|||
b03eba2bd5 |
|||
336c0c0db5 |
|||
11b12e55b8 |
|||
bb0fe0bece |
|||
b4702278d2 |
|||
9a84cf7fc2 | |||
eeabc4e66f |
|||
be896ca00c | |||
ed9af00592 |
|||
141b1cffc6 |
|||
e8e6733c9f | |||
0cac39ba78 |
|||
76c723d516 | |||
a24a7af3d8 |
|||
91f8c1834a |
|||
719a9ff3de | |||
8e4b169404 |
|||
af83dfa7d8 |
|||
5d1da2d3d8 |
|||
cb8b53ea63 |
|||
71a6ea0051 | |||
2b2ca46214 |
|||
df9337c684 |
|||
7e614b89a8 |
|||
76e0674098 |
|
@ -1,69 +1,101 @@
|
||||||
|
name: Build and deploy preview site
|
||||||
|
run-name: Building and deploying preview for version ${{ github.ref_name }}
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+pre[0-9]+'
|
- '[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
checking:
|
check-tag:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container: node:lts
|
steps:
|
||||||
|
- name: Checking Out Repository Code
|
||||||
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- name: Check if Version in package.json matches Tag
|
||||||
|
run: |
|
||||||
|
VERSION=$(npm pkg get version --workspaces=false | tr -d \")
|
||||||
|
if test $VERSION != "${{ github.ref_name }}"; then
|
||||||
|
echo "Expected Version is: '${{ github.ref_name }}' actual Version is: '$VERSION'";
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Version is: '$VERSION'";
|
||||||
|
fi
|
||||||
|
|
||||||
|
checking:
|
||||||
|
needs: [ check-tag ]
|
||||||
|
runs-on: docker
|
||||||
|
container: forgejo.neshweb.net/ci-docker-images/node-alpine-git:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: npm install
|
run: npm i
|
||||||
- name: Run astro check (linting + static analysis)
|
- name: Run astro check (linting + static analysis)
|
||||||
run: npm run astro check
|
run: npm run astro check
|
||||||
|
|
||||||
build-site:
|
build-site:
|
||||||
needs: [checking]
|
needs: [ checking ]
|
||||||
if: success()
|
if: success()
|
||||||
runs-on: dind
|
runs-on: dind
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Log into Docker Package Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: forgejo.neshweb.net
|
registry: forgejo.neshweb.net
|
||||||
username: ${{ secrets.FORGEJO_USERNAME }}
|
username: ${{ secrets.FORGEJO_USERNAME }}
|
||||||
password: ${{ secrets.FORGEJO_TOKEN }}
|
password: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
- name: Push to Package Registry
|
- name: Build and push to Docker Package Registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: forgejo.neshweb.net/firq/firq-dev-website:${{ github.ref_name }}, forgejo.neshweb.net/firq/firq-dev-website:preview
|
tags: forgejo.neshweb.net/firq/firq-dev-website:${{ github.ref_name }}, forgejo.neshweb.net/firq/firq-dev-website:preview
|
||||||
|
build-args: |
|
||||||
|
version=${{ github.ref_name }}
|
||||||
|
|
||||||
publish:
|
create-release:
|
||||||
needs: [build-site]
|
needs: [ build-site ]
|
||||||
if: success()
|
if: success()
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- name: Release New Version
|
- name: Release New Version
|
||||||
uses: actions/forgejo-release@v1
|
uses: https://code.forgejo.org/actions/forgejo-release@v1
|
||||||
with:
|
with:
|
||||||
direction: upload
|
direction: upload
|
||||||
url: https://forgejo.neshweb.net
|
url: https://forgejo.neshweb.net
|
||||||
release-dir: release
|
release-dir: release
|
||||||
token: ${{ secrets.FORGEJO_TOKEN }}
|
token: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
tag: ${{ github.ref_name }}
|
tag: ${{ github.ref_name }}
|
||||||
|
prerelease: true
|
||||||
|
|
||||||
# doesn't work atm
|
auto-deploy-dockge:
|
||||||
# unlighthouse:
|
needs: [ build-site ]
|
||||||
# needs: [publish]
|
if: success()
|
||||||
# if: success()
|
runs-on: docker
|
||||||
# runs-on: docker
|
container: forgejo.neshweb.net/firq/dockge-cli:0.1.2
|
||||||
# container: registry.gitlab.com/gitlab-ci-utils/lighthouse:latest
|
steps:
|
||||||
# services:
|
- name: Configure Dockge CLI
|
||||||
# website:
|
run: |
|
||||||
# image: forgejo.neshweb.net/firq/firq-dev-website:preview
|
dockge host '${{ vars.DOCKGE_HOST }}'
|
||||||
# steps:
|
dockge login --user '${{ secrets.DOCKGE_USER }}' --password '${{ secrets.DOCKGE_CREDENTIAL }}'
|
||||||
# - name: Install Dependencies
|
- name: Check status and redeploy
|
||||||
# run: npm install @unlighthouse/cli puppeteer
|
run: |
|
||||||
# - name: Run unlighthouse
|
dockge status firq-dev-preview
|
||||||
# run: node_modules/.bin/unlighthouse-ci
|
dockge update firq-dev-preview
|
||||||
# - name: Upload reports
|
dockge status firq-dev-preview
|
||||||
# uses: actions/upload-artifact@v3
|
|
||||||
# with:
|
run-unlighthouse:
|
||||||
# name: unlighthouse-reports
|
needs: [ build-site ]
|
||||||
# path: unlighthouse-reports/
|
if: success()
|
||||||
|
runs-on: docker
|
||||||
|
steps:
|
||||||
|
- name: Launch workflow
|
||||||
|
run: |
|
||||||
|
payload="{\"ref\": \"${GITHUB_REF_NAME}\", \"inputs\": { \"containertag\": \"${GITHUB_REF_NAME}\" }}"
|
||||||
|
curl -X "POST" \
|
||||||
|
-H "accept: application/json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-d "${payload}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/workflows/unlighthouse.yml/dispatches" -v
|
||||||
|
|
|
@ -1,46 +1,97 @@
|
||||||
|
name: Build and deploy production site
|
||||||
|
run-name: Building and deploying release for version ${{ github.ref_name }}
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+'
|
- '[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
checking:
|
check-tag:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container: node:lts
|
steps:
|
||||||
|
- name: Checking Out Repository Code
|
||||||
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- name: Check if Version in package.json matches Tag
|
||||||
|
run: |
|
||||||
|
VERSION=$(npm pkg get version --workspaces=false | tr -d \")
|
||||||
|
if test $VERSION != "${{ github.ref_name }}"; then
|
||||||
|
echo "Expected Version is: '${{ github.ref_name }}' actual Version is: '$VERSION'";
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Version is: '$VERSION'";
|
||||||
|
fi
|
||||||
|
|
||||||
|
checking:
|
||||||
|
needs: [ check-tag ]
|
||||||
|
runs-on: docker
|
||||||
|
container: forgejo.neshweb.net/ci-docker-images/node-alpine-git:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: npm install
|
run: npm i
|
||||||
- name: Run astro check (linting + static analysis)
|
- name: Run astro check (linting + static analysis)
|
||||||
run: npm run astro check
|
run: npm run astro check
|
||||||
|
|
||||||
build-site:
|
build-site:
|
||||||
needs: [checking]
|
needs: [ checking ]
|
||||||
if: success()
|
if: success()
|
||||||
runs-on: dind
|
runs-on: dind
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Log into Docker Package Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: forgejo.neshweb.net
|
registry: forgejo.neshweb.net
|
||||||
username: ${{ secrets.FORGEJO_USERNAME }}
|
username: ${{ secrets.FORGEJO_USERNAME }}
|
||||||
password: ${{ secrets.FORGEJO_TOKEN }}
|
password: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
- name: Push to Package Registry
|
- name: Build and push to Docker Package Registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: forgejo.neshweb.net/firq/firq-dev-website:${{ github.ref_name }}, forgejo.neshweb.net/firq/firq-dev-website:latest
|
tags: forgejo.neshweb.net/firq/firq-dev-website:${{ github.ref_name }}, forgejo.neshweb.net/firq/firq-dev-website:latest
|
||||||
|
build-args: |
|
||||||
|
version=${{ github.ref_name }}
|
||||||
|
|
||||||
release:
|
auto-deploy-dockge:
|
||||||
needs: [build-site]
|
needs: [ build-site ]
|
||||||
|
if: success()
|
||||||
|
runs-on: docker
|
||||||
|
container: forgejo.neshweb.net/firq/dockge-cli:0.1.2
|
||||||
|
steps:
|
||||||
|
- name: Configure Dockge CLI
|
||||||
|
run: |
|
||||||
|
dockge host '${{ vars.DOCKGE_HOST }}'
|
||||||
|
dockge login --user '${{ secrets.DOCKGE_USER }}' --password '${{ secrets.DOCKGE_CREDENTIAL }}'
|
||||||
|
- name: Check status and redeploy
|
||||||
|
run: |
|
||||||
|
dockge status firq-dev
|
||||||
|
dockge update firq-dev
|
||||||
|
dockge status firq-dev
|
||||||
|
|
||||||
|
run-unlighthouse:
|
||||||
|
needs: [ build-site ]
|
||||||
|
if: success()
|
||||||
|
runs-on: docker
|
||||||
|
steps:
|
||||||
|
- name: Launch workflow
|
||||||
|
run: |
|
||||||
|
payload="{\"ref\": \"${GITHUB_REF_NAME}\", \"inputs\": { \"containertag\": \"${GITHUB_REF_NAME}\" }}"
|
||||||
|
curl -X "POST" \
|
||||||
|
-H "accept: application/json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-d "${payload}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/workflows/unlighthouse.yml/dispatches" -v
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
needs: [ build-site ]
|
||||||
if: success()
|
if: success()
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- name: Release New Version
|
- name: Release New Version
|
||||||
uses: actions/forgejo-release@v1
|
uses: https://code.forgejo.org/actions/forgejo-release@v1
|
||||||
with:
|
with:
|
||||||
direction: upload
|
direction: upload
|
||||||
url: https://forgejo.neshweb.net
|
url: https://forgejo.neshweb.net
|
||||||
|
|
|
@ -1,16 +1,27 @@
|
||||||
|
name: Linting and checking code
|
||||||
|
run-name: Linting and checking code
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "**"
|
- '**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
checking:
|
get-version:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container: node:lts
|
steps:
|
||||||
|
- name: Checking Out Repository Code
|
||||||
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- name: Get version from package.json
|
||||||
|
run: |
|
||||||
|
VERSION=$(npm pkg get version --workspaces=false | tr -d \")
|
||||||
|
echo "Version is: '$VERSION'";
|
||||||
|
astro-check:
|
||||||
|
runs-on: docker
|
||||||
|
container: forgejo.neshweb.net/ci-docker-images/node-alpine-git:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: npm install
|
run: npm i
|
||||||
- name: Run astro check (linting + static analysis)
|
- name: Run astro check (linting + static analysis)
|
||||||
run: npm run astro check
|
run: npm run astro check
|
||||||
|
|
|
@ -1,18 +1,36 @@
|
||||||
|
name: Run unlighthouse-tests [Downstream pipeline]
|
||||||
|
run-name: Run unlighthouse-tests [Downstream pipeline] for version ${{ inputs.containertag }}
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
tags:
|
inputs:
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+unlighthouse[0-9]+'
|
containertag:
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+ulh[0-9]+'
|
description: "Tag of the container to test"
|
||||||
|
default: "preview"
|
||||||
|
type: "string"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
unlighthouse:
|
unlighthouse:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container: forgejo.neshweb.net/ci-docker-images/unlighthouse:latest
|
container:
|
||||||
|
image: forgejo.neshweb.net/ci-docker-images/unlighthouse:0.16.3
|
||||||
|
services:
|
||||||
|
website:
|
||||||
|
image: forgejo.neshweb.net/firq/firq-dev-website:${{ inputs.containertag }}
|
||||||
|
options: >-
|
||||||
|
--hostname website
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- name: Check availability
|
||||||
|
run: |
|
||||||
|
while [ "$(curl -o /dev/null -s -w '%{http_code}' http://website:8081)" -ne 200 ];
|
||||||
|
do echo "Waiting...";
|
||||||
|
sleep 5;
|
||||||
|
done;
|
||||||
- name: Run unlighthouse
|
- name: Run unlighthouse
|
||||||
run: unlighthouse-ci --site "https://preview.firq.dev/"
|
run: unlighthouse-ci --site "http://website:8081"
|
||||||
|
- name: Replace URLs
|
||||||
|
run: find ./unlighthouse-reports -type f | xargs sed -i "s|http://website:8081|https://preview.firq.dev|g";
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
run: cp serve.json unlighthouse-reports
|
run: cp serve.json unlighthouse-reports
|
||||||
- name: Upload reports
|
- name: Upload reports
|
||||||
|
@ -20,76 +38,58 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: unlighthouse-reports
|
name: unlighthouse-reports
|
||||||
path: unlighthouse-reports/
|
path: unlighthouse-reports/
|
||||||
|
- name: Upload Dockerfile
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dockerfile
|
||||||
|
path: Dockerfile.reports
|
||||||
|
|
||||||
deploy-unlighthouse-files:
|
build-site:
|
||||||
needs: [ unlighthouse ]
|
needs: [ unlighthouse ]
|
||||||
runs-on: docker
|
if: success()
|
||||||
env:
|
runs-on: dind
|
||||||
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
|
||||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install and update ssh + rsync
|
- name: Downloading static site artifacts
|
||||||
run: |
|
uses: actions/download-artifact@v3
|
||||||
which rsync || ( apt update -y && apt install rsync -y)
|
with:
|
||||||
which ssh-agent || ( apt update -y && apt install openssh-client -y)
|
name: dockerfile
|
||||||
- name: Downloading static site artifacts
|
- name: Downloading static site artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: unlighthouse-reports
|
name: unlighthouse-reports
|
||||||
path: public
|
path: reports
|
||||||
- name: Install SSH Key
|
- name: Set up Docker Buildx
|
||||||
uses: https://github.com/shimataro/ssh-key-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Log into Docker Package Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
registry: forgejo.neshweb.net
|
||||||
known_hosts: unnecessary
|
username: ${{ secrets.FORGEJO_USERNAME }}
|
||||||
- name: Adding Known Hosts
|
password: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
run: ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
|
- name: Build and push to Docker Package Registry
|
||||||
- name: Stop screen session, delete old files
|
uses: docker/build-push-action@v5
|
||||||
uses: https://github.com/appleboy/ssh-action@master
|
|
||||||
with:
|
with:
|
||||||
host: ${{ env.DEPLOY_HOST }}
|
context: .
|
||||||
username: ${{ env.DEPLOY_USER }}
|
file: Dockerfile.reports
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
push: true
|
||||||
script: |
|
tags: forgejo.neshweb.net/firq/firq-dev-website-unlighthouse:latest
|
||||||
screen -X -S firq_dev-unlighthouse kill
|
build-args: |
|
||||||
rm -r -f firq_dev/unlighthouse/*
|
version=${{ inputs.containertag }}
|
||||||
- name: Copy files using rsync
|
|
||||||
run: rsync -az --stats public/* ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:~/firq_dev/unlighthouse
|
|
||||||
- name: Check files on deploy target
|
|
||||||
uses: https://github.com/appleboy/ssh-action@master
|
|
||||||
with:
|
|
||||||
host: ${{ env.DEPLOY_HOST }}
|
|
||||||
username: ${{ env.DEPLOY_USER }}
|
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
script: |
|
|
||||||
cd firq_dev
|
|
||||||
find unlighthouse -maxdepth 1 -printf "%p\n"
|
|
||||||
|
|
||||||
deploy-unlighthouse-site:
|
auto-deploy-dockge:
|
||||||
needs: [ deploy-unlighthouse-files ]
|
needs: [ build-site ]
|
||||||
|
if: success()
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
env:
|
container: forgejo.neshweb.net/firq/dockge-cli:0.1.2
|
||||||
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
|
||||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install and update ssh + rsync
|
- name: Configure Dockge CLI
|
||||||
run: |
|
run: |
|
||||||
which rsync || ( apt update -y && apt install rsync -y)
|
dockge host '${{ vars.DOCKGE_HOST }}'
|
||||||
which ssh-agent || ( apt update -y && apt install openssh-client -y)
|
dockge login --user '${{ secrets.DOCKGE_USER }}' --password '${{ secrets.DOCKGE_CREDENTIAL }}'
|
||||||
- name: Install SSH Key
|
- name: Check status and redeploy
|
||||||
uses: https://github.com/shimataro/ssh-key-action@v2
|
run: |
|
||||||
with:
|
dockge status firq-dev-unlighthouse
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
dockge down firq-dev-unlighthouse
|
||||||
known_hosts: unnecessary
|
dockge update firq-dev-unlighthouse
|
||||||
- name: Adding Known Hosts
|
dockge start firq-dev-unlighthouse
|
||||||
run: ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
|
dockge status firq-dev-unlighthouse
|
||||||
- name: Start new screen session
|
|
||||||
uses: https://github.com/appleboy/ssh-action@master
|
|
||||||
with:
|
|
||||||
host: ${{ env.DEPLOY_HOST }}
|
|
||||||
username: ${{ env.DEPLOY_USER }}
|
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
script: |
|
|
||||||
PATH="$HOME/.local/bin:$PATH"
|
|
||||||
cd firq_dev
|
|
||||||
screen -S firq_dev-unlighthouse -dm serve unlighthouse/ -p ${{ secrets.UNLIGHTHOUSE_DEPLOY_PORT }}
|
|
||||||
|
|
14
.gitignore
vendored
|
@ -1,9 +1,14 @@
|
||||||
# build output
|
# build output
|
||||||
dist/
|
dist/
|
||||||
.output/
|
.output/
|
||||||
public/
|
|
||||||
maintenance/
|
maintenance/
|
||||||
|
|
||||||
|
# astro stuff
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# config stuff
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# Temp Scripts
|
# Temp Scripts
|
||||||
scripts/
|
scripts/
|
||||||
|
|
||||||
|
@ -23,10 +28,3 @@ pnpm-debug.log*
|
||||||
|
|
||||||
# macOS-specific files
|
# macOS-specific files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# exclude default png images
|
|
||||||
static/**/*.png
|
|
||||||
!static/assets/embed.png
|
|
||||||
!static/assets/lurker.png
|
|
||||||
!static/assets/padoru.png
|
|
||||||
!static/assets/fedora.png
|
|
5
.markdownlint.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"MD013":false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD036": false
|
||||||
|
}
|
|
@ -3,4 +3,5 @@
|
||||||
**/.vscode
|
**/.vscode
|
||||||
**/public
|
**/public
|
||||||
**/static
|
**/static
|
||||||
**/scripts
|
**/scripts
|
||||||
|
**/dist
|
4
.vscode/extensions.json
vendored
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"recommendations": ["astro-build.astro-vscode"],
|
|
||||||
"unwantedRecommendations": []
|
|
||||||
}
|
|
11
.vscode/launch.json
vendored
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"command": "./node_modules/.bin/astro dev",
|
|
||||||
"name": "Development server",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
23
Dockerfile
|
@ -1,16 +1,21 @@
|
||||||
FROM node:lts AS build
|
FROM forgejo.neshweb.net/ci-docker-images/node-chromium:22 AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
ENV CI=true
|
||||||
RUN npm i
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM forgejo.neshweb.net/ci-docker-images/website-serve:latest AS runtime
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm i
|
||||||
|
RUN npm run astro telemetry disable
|
||||||
|
RUN npm run build
|
||||||
|
RUN rm ./dist/.original.favicon.ico
|
||||||
|
|
||||||
|
FROM forgejo.neshweb.net/ci-docker-images/website-serve:2 AS runtime
|
||||||
|
ARG version=0.0.1
|
||||||
|
ENV version=${version}
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
COPY --from=build /app/dist /public
|
COPY --from=build /app/dist /public
|
||||||
COPY --from=build /app/serve.json /public/serve.json
|
COPY --from=build /app/serve.json /public/serve.json
|
||||||
RUN rm -r /public/data/
|
|
||||||
|
|
||||||
ENV PORT 8081
|
|
||||||
EXPOSE 8081
|
EXPOSE 8081
|
||||||
|
CMD echo "Website version ${version} - powered by @Firq"; serve --listen 8081 --no-clipboard /public
|
||||||
CMD [ "serve", "public/", "-p", "8081" ]
|
|
||||||
|
|
10
Dockerfile.reports
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
FROM forgejo.neshweb.net/ci-docker-images/website-serve:2 AS runtime
|
||||||
|
|
||||||
|
ARG version=0.0.1
|
||||||
|
ENV version=${version}
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
ADD reports /public
|
||||||
|
|
||||||
|
EXPOSE 8081
|
||||||
|
CMD echo "Website version ${version} - powered by @Firq"; serve --listen 8081 --no-clipboard /public
|
38
README.md
|
@ -2,43 +2,6 @@
|
||||||
|
|
||||||
This is build using [Astro](https://astro.build)
|
This is build using [Astro](https://astro.build)
|
||||||
|
|
||||||
|
|
||||||
## 🚀 Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
/
|
|
||||||
├── static/
|
|
||||||
│ ├── favicon.svg
|
|
||||||
│ └── other files ...
|
|
||||||
│
|
|
||||||
├── src/
|
|
||||||
│ ├── components/
|
|
||||||
│ │ ├── servantCard.astro
|
|
||||||
│ │ ├── ceCard.astro
|
|
||||||
│ │ ├── contactCard.astro
|
|
||||||
│ │ └── taCard.astro
|
|
||||||
│ │
|
|
||||||
│ ├── layouts/
|
|
||||||
│ │ ├── customFooter.astro
|
|
||||||
│ │ ├── baseSection.astro
|
|
||||||
│ │ ├── taSection.astro
|
|
||||||
│ │ ├── contactSection.astro
|
|
||||||
│ │ └── Layout.astro
|
|
||||||
│ │
|
|
||||||
│ └── pages/
|
|
||||||
│ └── index.astro
|
|
||||||
│
|
|
||||||
├── .gitlab-ci.yml
|
|
||||||
├── astro.config.mjs
|
|
||||||
└── package.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
|
||||||
|
|
||||||
More routes will follow soon
|
|
||||||
|
|
||||||
Any static assets, like images, can be placed in the `static/` directory.
|
|
||||||
|
|
||||||
## 🧞 Astro-Specific Commands
|
## 🧞 Astro-Specific Commands
|
||||||
|
|
||||||
All commands are run from the root of the project, from a terminal:
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
@ -51,4 +14,3 @@ All commands are run from the root of the project, from a terminal:
|
||||||
| `npm run preview` | Preview your build locally, before deploying |
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
|
||||||
| `npm run astro --help` | Get help using the Astro CLI |
|
| `npm run astro --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { defineConfig } from 'astro/config';
|
import mdx from "@astrojs/mdx";
|
||||||
|
|
||||||
// https://astro.build/config
|
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import criticalCss from 'astro-critical-css';
|
||||||
|
import astroMetaTags from "astro-meta-tags";
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
sitemap: true,
|
sitemap: true,
|
||||||
base: '/',
|
base: '/',
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
publicDir: 'static',
|
publicDir: 'public',
|
||||||
site: 'https://firq.dev/',
|
site: 'https://firq.dev/',
|
||||||
integrations: [sitemap()]
|
integrations: [sitemap(), mdx(), astroMetaTags(), criticalCss()],
|
||||||
});
|
});
|
22739
package-lock.json
generated
28
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@firq/fgosite",
|
"name": "@firq/fgosite",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.1.14",
|
"version": "0.2.1-pre.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
|
@ -11,10 +11,24 @@
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.3.3",
|
"@astro-community/astro-embed-youtube": "^0.5.6",
|
||||||
"@astrojs/sitemap": "^3.0.3",
|
"@astrojs/check": "^0.9.4",
|
||||||
"astro": "^4.0.7",
|
"@astrojs/mdx": "^4.3.2",
|
||||||
"iconoir": "^6.1.0",
|
"@astrojs/sitemap": "^3.4.2",
|
||||||
"typescript": "^5.3.3"
|
"@fontsource-variable/work-sans": "^5.2.5",
|
||||||
}
|
"astro": "^5.12.7",
|
||||||
|
"astro-critical-css": "^0.0.7",
|
||||||
|
"astro-meta-tags": "^0.3.1",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
|
"iconoir": "^7.10.1",
|
||||||
|
"postcss-preset-env": "^10.1.5",
|
||||||
|
"typescript": "^5.8.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"unlighthouse": "^0.17.2"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"last 2 versions",
|
||||||
|
">0.5% and not dead"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
13
postcss.config.cjs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
"postcss-preset-env": {
|
||||||
|
autoprefixer: { flexbox: "no-2009" },
|
||||||
|
stage: 2,
|
||||||
|
features: {
|
||||||
|
"custom-properties": false,
|
||||||
|
"custom-media-queries": true,
|
||||||
|
"nesting-rules": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Before Width: 256px | Height: 256px | Size: 123 KiB After (image error) Size: 123 KiB |
6
public/ai.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Spawning AI
|
||||||
|
# Prevent datasets from using the following file types
|
||||||
|
|
||||||
|
User-Agent: *
|
||||||
|
Disallow: /
|
||||||
|
Disallow: *
|
61
public/robots.txt
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /assets/data/
|
||||||
|
|
||||||
|
User-agent: AI2Bot
|
||||||
|
User-agent: Ai2Bot-Dolma
|
||||||
|
User-agent: aiHitBot
|
||||||
|
User-agent: Amazonbot
|
||||||
|
User-agent: anthropic-ai
|
||||||
|
User-agent: Applebot
|
||||||
|
User-agent: Applebot-Extended
|
||||||
|
User-agent: Brightbot 1.0
|
||||||
|
User-agent: Bytespider
|
||||||
|
User-agent: CCBot
|
||||||
|
User-agent: ChatGPT-User
|
||||||
|
User-agent: Claude-Web
|
||||||
|
User-agent: ClaudeBot
|
||||||
|
User-agent: cohere-ai
|
||||||
|
User-agent: cohere-training-data-crawler
|
||||||
|
User-agent: Cotoyogi
|
||||||
|
User-agent: Crawlspace
|
||||||
|
User-agent: Diffbot
|
||||||
|
User-agent: DuckAssistBot
|
||||||
|
User-agent: FacebookBot
|
||||||
|
User-agent: Factset_spyderbot
|
||||||
|
User-agent: FirecrawlAgent
|
||||||
|
User-agent: FriendlyCrawler
|
||||||
|
User-agent: Google-Extended
|
||||||
|
User-agent: GoogleOther
|
||||||
|
User-agent: GoogleOther-Image
|
||||||
|
User-agent: GoogleOther-Video
|
||||||
|
User-agent: GPTBot
|
||||||
|
User-agent: iaskspider/2.0
|
||||||
|
User-agent: ICC-Crawler
|
||||||
|
User-agent: ImagesiftBot
|
||||||
|
User-agent: img2dataset
|
||||||
|
User-agent: imgproxy
|
||||||
|
User-agent: ISSCyberRiskCrawler
|
||||||
|
User-agent: Kangaroo Bot
|
||||||
|
User-agent: Meta-ExternalAgent
|
||||||
|
User-agent: Meta-ExternalFetcher
|
||||||
|
User-agent: NovaAct
|
||||||
|
User-agent: OAI-SearchBot
|
||||||
|
User-agent: omgili
|
||||||
|
User-agent: omgilibot
|
||||||
|
User-agent: Operator
|
||||||
|
User-agent: PanguBot
|
||||||
|
User-agent: Perplexity-User
|
||||||
|
User-agent: PerplexityBot
|
||||||
|
User-agent: PetalBot
|
||||||
|
User-agent: Scrapy
|
||||||
|
User-agent: SemrushBot-OCOB
|
||||||
|
User-agent: SemrushBot-SWA
|
||||||
|
User-agent: Sidetrade indexer bot
|
||||||
|
User-agent: TikTokSpider
|
||||||
|
User-agent: Timpibot
|
||||||
|
User-agent: VelenPublicWebCrawler
|
||||||
|
User-agent: Webzio-Extended
|
||||||
|
User-agent: YouBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
Sitemap: https://firq.dev/sitemap-index.xml
|
11
serve.json
|
@ -14,9 +14,18 @@
|
||||||
"source" : "**/*.css",
|
"source" : "**/*.css",
|
||||||
"headers" : [{
|
"headers" : [{
|
||||||
"key" : "Cache-Control",
|
"key" : "Cache-Control",
|
||||||
"value" : "max-age=86400"
|
"value" : "max-age=31536000"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source": "**/*.woff2",
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"key": "Cache-Control",
|
||||||
|
"value" : "max-age=31536000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source" : "**/*.@(jpg|jpeg|gif|png|webp|svg)",
|
"source" : "**/*.@(jpg|jpeg|gif|png|webp|svg)",
|
||||||
"headers" : [{
|
"headers" : [{
|
||||||
|
|
BIN
src/assets/ce/bond-ce/scathach.png
Normal file
After ![]() (image error) Size: 18 KiB |
BIN
src/assets/ce/bond-ce/sherlock.png
Normal file
After ![]() (image error) Size: 21 KiB |
BIN
src/assets/ce/bond-ce/summerskadi.png
Normal file
After ![]() (image error) Size: 18 KiB |
BIN
src/assets/ce/bond-ce/taigong.png
Normal file
After ![]() (image error) Size: 20 KiB |
4
src/assets/custom-svg/bluesky.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="600" height="530" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m135.72 44.03c66.496 49.921 138.02 151.14 164.28 205.46 26.262-54.316 97.782-155.54 164.28-205.46 47.98-36.021 125.72-63.892 125.72 24.795 0 17.712-10.155 148.79-16.111 170.07-20.703 73.984-96.144 92.854-163.25 81.433 117.3 19.964 147.14 86.092 82.697 152.22-122.39 125.59-175.91-31.511-189.63-71.766-2.514-7.3797-3.6904-10.832-3.7077-7.8964-0.0174-2.9357-1.1937 0.51669-3.7077 7.8964-13.714 40.255-67.233 197.36-189.63 71.766-64.444-66.128-34.605-132.26 82.697-152.22-67.108 11.421-142.55-7.4491-163.25-81.433-5.9562-21.282-16.111-152.36-16.111-170.07 0-88.687 77.742-60.816 125.72-24.795z" fill="#000"/>
|
||||||
|
</svg>
|
After (image error) Size: 742 B |
1
src/assets/custom-svg/discord.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><path fill="#fff" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></svg>
|
After (image error) Size: 761 B |
37
src/assets/custom-svg/forgejo.svg
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<svg viewBox="0 0 212 212" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
>
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="https://codeberg.org/forgejo/governance/src/branch/main/branding#logo">
|
||||||
|
<dc:title>Forgejo logo</dc:title>
|
||||||
|
<cc:creator rdf:resource="https://caesarschinas.com/"><cc:attributionName>Caesar Schinas</cc:attributionName></cc:creator>
|
||||||
|
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<style type="text/css">
|
||||||
|
:root, svg {
|
||||||
|
color-scheme: dark light;
|
||||||
|
}
|
||||||
|
circle {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 15;
|
||||||
|
}
|
||||||
|
path {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 25;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<g transform="translate(6,6)">
|
||||||
|
<path d="M58 168 v-98 a50 50 0 0 1 50-50 h20" />
|
||||||
|
<path d="M58 168 v-30 a50 50 0 0 1 50-50 h20" />
|
||||||
|
<circle cx="142" cy="20" r="18" />
|
||||||
|
<circle cx="142" cy="88" r="18" />
|
||||||
|
<circle cx="58" cy="180" r="18" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After (image error) Size: 1.2 KiB |
6
src/assets/custom-svg/rayshift.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg width="124.48" height="203.92" version="1.1" viewBox="0 0 124.48 203.92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<rect x=".0051316" y="-.0039062" width="124.49" height="204" fill="none"/>
|
||||||
|
<path transform="translate(-58.01 -18.484)" d="m119.56 18.484-61.354 74.137 23.795 27.727-23.992 27.334 62.535 74.725 61.943-75.119-24.582-27.332 17.699-19.666-20.451-24.58-1.377-1.7695-0.01-2.3594 1.1894-4.3262 3.3438-4.3262-2.9512-2.5566-23.006 28.709-12.389-15.141-28.908 35.199-15.141-16.715 43.457-52.111 15.732 19.271 2.7539-3.5391-5.1133-5.5078-3.5391-5.7012-2.5566-5.3105 0.19532-4.5215 3.1465-4.918zm22.811 67.646 2.3613 0.19727 13.223 13.273-8.1113 9.7344-14.748-17.699zm-22.416 9.2422 21.236 24.975-20.844 24.385-21.041-24.777zm-29.105 35.789 29.105 35.594 29.889-35.396 14.158 16.322-43.852 52.896-43.852-52.109z" stroke="#000" stroke-width=".40441"/>
|
||||||
|
</svg>
|
After (image error) Size: 1,002 B |
61
src/assets/custom-svg/reddit.svg
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
id="Layer_1"
|
||||||
|
data-name="Layer 1"
|
||||||
|
viewBox="0 0 204.89999 204.89999"
|
||||||
|
version="1.1"
|
||||||
|
sodipodi:docname="Reddit_Icon_2Color.svg"
|
||||||
|
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||||
|
width="204.89999"
|
||||||
|
height="204.89999"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview11"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.3837233"
|
||||||
|
inkscape:cx="42.999958"
|
||||||
|
inkscape:cy="93.131617"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Layer_1" />
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<style
|
||||||
|
id="style2">
|
||||||
|
.cls-1 {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-1, .cls-2 {
|
||||||
|
stroke-width: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #ff4500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||||
|
transform="translate(-25.54,-34.12)" />
|
||||||
|
<path
|
||||||
|
class="cls-1"
|
||||||
|
d="m 128.5,39.079997 c 2.22,9.41 10.67,16.42 20.76,16.42 11.78,0 21.33,-9.55 21.33,-21.33 0,-11.78 -9.55,-21.33 -21.33,-21.33 -10.3,0 -18.89,7.3 -20.89,17.01 -17.25,1.85 -30.720003,16.48 -30.720003,34.21 0,0.04 0,0.07 0,0.11 -18.76,0.79 -35.89,6.13 -49.49,14.56 -5.05,-3.91 -11.39,-6.24 -18.27,-6.24 -16.51,0 -29.8900000517578,13.38 -29.8900000517578,29.890003 0,11.98 7.0399999517578,22.3 17.2100000517578,27.07 0.99,34.7 38.8,62.61 85.310003,62.61 46.51,0 84.37,-27.94 85.31,-62.67 10.09,-4.8 17.07,-15.09 17.07,-27 0,-16.510003 -13.38,-29.890003 -29.89,-29.890003 -6.85,0 -13.16,2.31 -18.2,6.19 -13.72,-8.49 -31.04,-13.83 -49.99,-14.54 0,-0.03 0,-0.05 0,-0.08 0,-12.7 9.44,-23.24 21.68,-24.97 z M 46.959997,121.35 c 0.5,-10.84 7.7,-19.16 16.07,-19.16 8.37,0 14.77,8.79 14.27,19.63 -0.5,10.84 -6.75,14.78 -15.13,14.78 -8.38,0 -15.71,-4.41 -15.21,-15.25 z M 142.02,102.19 c 8.38,0 15.58,8.32 16.07,19.16 0.5,10.84 -6.84,15.25 -15.21,15.25 -8.37,0 -14.63,-3.93 -15.13,-14.78 -0.5,-10.84 5.89,-19.63 14.27,-19.63 z m -9.96,44.24 c 1.57,0.16 2.57,1.79 1.96,3.25 -5.15,12.31 -17.31,20.96 -31.5,20.96 -14.190003,0 -26.340003,-8.65 -31.500003,-20.96 -0.61,-1.46 0.39,-3.09 1.96,-3.25 9.2,-0.93 19.15,-1.44 29.540003,-1.44 10.39,0 20.33,0.51 29.54,1.44 z"
|
||||||
|
id="path8" />
|
||||||
|
</svg>
|
After (image error) Size: 2.7 KiB |
1
src/assets/custom-svg/twitch.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M2.149 0l-1.612 4.119v16.836h5.731v3.045h3.224l3.045-3.045h4.657l6.269-6.269v-14.686h-21.314zm19.164 13.612l-3.582 3.582h-5.731l-3.045 3.045v-3.045h-4.836v-15.045h17.194v11.463zm-3.582-7.343v6.262h-2.149v-6.262h2.149zm-5.731 0v6.262h-2.149v-6.262h2.149z" fill-rule="evenodd" clip-rule="evenodd"/></svg>
|
After (image error) Size: 394 B |
BIN
src/assets/favicon.ico
Normal file
After Width: 32px | Height: 32px | Size: 4.2 KiB |
Before ![]() (image error) Size: 554 KiB After ![]() (image error) Size: 248 KiB ![]() ![]() |
Before ![]() (image error) Size: 565 KiB After ![]() (image error) Size: 268 KiB ![]() ![]() |
BIN
src/assets/favourites/summerscathach.png
Normal file
After ![]() (image error) Size: 238 KiB |
BIN
src/assets/favourites/summerskadi.png
Normal file
After ![]() (image error) Size: 283 KiB |
Before ![]() (image error) Size: 24 KiB After ![]() (image error) Size: 22 KiB ![]() ![]() |
BIN
src/assets/servant/koyanlight_02.png
Normal file
After ![]() (image error) Size: 122 KiB |
BIN
src/assets/servant/liz_02.png
Normal file
After ![]() (image error) Size: 120 KiB |
BIN
src/assets/servant/scathach_03.png
Normal file
After ![]() (image error) Size: 69 KiB |
BIN
src/assets/servant/sherlock_03.png
Normal file
After ![]() (image error) Size: 44 KiB |
BIN
src/assets/servant/summerskadi_02.png
Normal file
After ![]() (image error) Size: 86 KiB |
BIN
src/assets/servant/summerskadi_03.png
Normal file
After ![]() (image error) Size: 107 KiB |
BIN
src/assets/servant/taigong_03.png
Normal file
After ![]() (image error) Size: 68 KiB |
BIN
src/assets/ta_icons/beast_4_l.png
Normal file
After ![]() (image error) Size: 28 KiB |
BIN
src/assets/ta_icons/koyanskayaofdark.png
Normal file
After ![]() (image error) Size: 36 KiB |
BIN
src/assets/thumbnails/O1f-go7uJQM.jpg
Normal file
After ![]() (image error) Size: 91 KiB |
BIN
src/assets/thumbnails/UwbNp_dB_VU.jpg
Normal file
After ![]() (image error) Size: 145 KiB |
BIN
src/assets/thumbnails/WrHudtdfivA.jpg
Normal file
After ![]() (image error) Size: 144 KiB |
53
src/components/aboutText.astro
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
I'm a 24 year old software developer from Germany. Even if my roots are with Electrical Engineering,
|
||||||
|
I am currently pursuing a Masters Degree in Computer Science after starting to code more during my studies.
|
||||||
|
I am mostly programming in Python, but I'm also confident in writing Javascript.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I enjoy biking and hiking, as well as visiting different anime conventions.
|
||||||
|
I am a huge fan of the Fate franchise, specifically Fate/Grand Order, which I am playing since the end of 2019.
|
||||||
|
I also enjoy other games, mainly Honkai: Star Rail and theHunter: Call of the Wild.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, 'Segoe UI', sans-serif;
|
||||||
|
max-width: min(87.5%, 360px);
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 1rem 1rem;
|
||||||
|
margin-top: -0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div > p:first-of-type {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div > p:last-of-type {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 512px) {
|
||||||
|
div {
|
||||||
|
max-width: 75%;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
div {
|
||||||
|
max-width: 65%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,8 +1,9 @@
|
||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
url: string | undefined
|
url: string
|
||||||
|
slug: string
|
||||||
title: string
|
title: string
|
||||||
pubdate: string
|
pubdate: Date
|
||||||
description: string
|
description: string
|
||||||
author: string
|
author: string
|
||||||
}
|
}
|
||||||
|
@ -12,11 +13,11 @@ const options_date: Intl.DateTimeFormatOptions = {
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
}
|
}
|
||||||
const { author, description, pubdate, url, title } = Astro.props
|
const { author, description, pubdate, url, title, slug } = Astro.props
|
||||||
const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
|
const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
|
||||||
---
|
---
|
||||||
|
|
||||||
<a href={url} rel="noopener noreferrer">
|
<a href={`${url}/${slug}`} rel="noopener noreferrer">
|
||||||
<div class="circle"></div>
|
<div class="circle"></div>
|
||||||
<article>
|
<article>
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
|
@ -29,56 +30,32 @@ const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
|
||||||
.circle {
|
.circle {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media (min-width: 900px) {
|
|
||||||
.circle {
|
|
||||||
margin: 1rem 1rem 1rem 0rem;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
visibility: visible;
|
|
||||||
height: 1.5rem;
|
|
||||||
width: 1.5rem;
|
|
||||||
border-radius: 40%;
|
|
||||||
background-color: var(--c-darkpurple);
|
|
||||||
transition: transform var(--speed) var(--ease);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover > .circle {
|
|
||||||
height: 1.75rem;
|
|
||||||
width: 1.75rem;
|
|
||||||
translate: -0.125rem;
|
|
||||||
margin-right: 0.825rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
height: auto;
|
height: fit-content;
|
||||||
margin: 0.5rem;
|
margin: 0px 0.5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
}
|
}
|
||||||
article > h2 {
|
article > h2 {
|
||||||
margin: 0.3rem 0.5rem;
|
margin: 0.3rem 0.5rem;
|
||||||
color: var(--c-darkpurple);
|
color: var(--c-accent-1);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
article > h3 {
|
article > h3 {
|
||||||
margin: 0.2em 0.5rem;
|
margin: 0.2em 0.5rem;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -88,17 +65,54 @@ const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
align-content: flex-start;
|
align-content: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--c-darkergray);
|
background-color: var(--c-primary-background);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: transform var(--speed) var(--ease);
|
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
border: 2px solid var(--c-primary-background);
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
}
|
}
|
||||||
a:hover > article {
|
|
||||||
transform: scaleY(102.5%) scaleX(101%);
|
a:hover article {
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 900px) {
|
||||||
|
.circle {
|
||||||
|
margin: 1rem 0.5rem 1rem 0.5rem;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
visibility: visible;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
background-color: var(--c-accent-1);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0.25rem;
|
||||||
|
border-color: var(--c-secondary-background);
|
||||||
|
border-radius: 40%;
|
||||||
|
transition:
|
||||||
|
height var(--a-time-short) var(--a-animation-1),
|
||||||
|
width var(--a-time-short) var(--a-animation-1),
|
||||||
|
margin-right var(--a-time-short) var(--a-animation-1),
|
||||||
|
transform var(--a-time-short) var(--a-animation-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover > .circle {
|
||||||
|
height: 1.25rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
transform: translateX(-0.125rem);
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
border: 2px solid var(--c-primary-background);
|
||||||
|
align-items: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,29 +1,26 @@
|
||||||
---
|
---
|
||||||
import type { ImageMetadata } from 'astro'
|
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
import mlb_ce from '../assets/ce/mlb.webp'
|
import mlb_ce from '@assets/ce/mlb.webp'
|
||||||
|
import type { GlobImage } from '../../types/generic'
|
||||||
|
import { plsLoadImage } from '../../utils/tools'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
name: string
|
name: string
|
||||||
imageFile: string
|
imageFile: string
|
||||||
mlb: string
|
mlb: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const { mlb, imageFile, name } = Astro.props
|
const { mlb, imageFile, name } = Astro.props
|
||||||
const imagePath = `/src/assets/ce/${imageFile}`
|
const imagePath = `/src/assets/ce/${imageFile}`
|
||||||
const images_ces = import.meta.glob<{ default: ImageMetadata }>(
|
const images_ces = import.meta.glob<GlobImage>('/src/assets/ce/*.png')
|
||||||
'/src/assets/ce/*.{png,webp}'
|
const loadedCEImage = plsLoadImage(images_ces, imagePath)
|
||||||
)
|
|
||||||
|
|
||||||
let mlb_image: string = 'mlbalign'
|
const mlb_image = mlb ? 'mlbalign' : 'hidemlb'
|
||||||
if (mlb === 'false') {
|
|
||||||
mlb_image = 'hidemlb'
|
|
||||||
}
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<div>
|
<div>
|
||||||
<Image src={images_ces[imagePath]()} alt={name} class="ce-crop" />
|
<Image src={loadedCEImage} alt={name} class="ce-crop" />
|
||||||
</div>
|
</div>
|
||||||
<div class={mlb_image}>
|
<div class={mlb_image}>
|
||||||
<Image src={mlb_ce} alt="Max-limit broken" class="mlb" />
|
<Image src={mlb_ce} alt="Max-limit broken" class="mlb" />
|
||||||
|
@ -37,15 +34,14 @@ if (mlb === 'false') {
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
background-color: var(--c-darkergray);
|
border: 2px var(--c-primary-background) solid;
|
||||||
padding: 20px;
|
background-color: var(--c-primary-background);
|
||||||
padding-top: 5px;
|
|
||||||
width: 35%;
|
width: 35%;
|
||||||
height: auto;
|
height: auto;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: transform var(--speed) var(--ease);
|
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
|
@ -58,11 +54,11 @@ if (mlb === 'false') {
|
||||||
}
|
}
|
||||||
|
|
||||||
article:hover {
|
article:hover {
|
||||||
transform: scale(var(--hover-scale));
|
border: 2px var(--c-accent-1) solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
article div {
|
article div {
|
||||||
margin: 0.5em auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
@ -73,27 +69,17 @@ if (mlb === 'false') {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading-center {
|
|
||||||
display: flex;
|
|
||||||
height: 4rem;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 1.25em;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-crop {
|
.ce-crop {
|
||||||
width: 6em;
|
width: min(85%, 85px);
|
||||||
height: 6em;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mlb {
|
.mlb {
|
||||||
width: 5.5rem;
|
width: min(85%, 85px);
|
||||||
height: auto;
|
height: auto;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
margin-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mlbalign {
|
.mlbalign {
|
||||||
|
@ -104,18 +90,18 @@ if (mlb === 'false') {
|
||||||
|
|
||||||
@media (min-width: 512px) {
|
@media (min-width: 512px) {
|
||||||
.ce-crop {
|
.ce-crop {
|
||||||
width: 7.5em;
|
width: 100px;
|
||||||
height: 7.5em;
|
height: 100px;
|
||||||
}
|
}
|
||||||
article {
|
article {
|
||||||
width: 10em;
|
width: 128px;
|
||||||
}
|
}
|
||||||
.mlbalign {
|
.mlbalign {
|
||||||
width: 7.5em;
|
width: 85px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mlb {
|
.mlb {
|
||||||
width: 7rem;
|
width: 85px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,6 +1,7 @@
|
||||||
---
|
---
|
||||||
import type { ImageMetadata } from 'astro'
|
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
|
import type { GlobImage } from '../../types/generic'
|
||||||
|
import { plsLoadImage } from '../../utils/tools'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
site: string
|
site: string
|
||||||
|
@ -10,80 +11,80 @@ export interface Props {
|
||||||
|
|
||||||
const { imageFile, link, site } = Astro.props
|
const { imageFile, link, site } = Astro.props
|
||||||
const imagePath = `/src/assets/social/${imageFile}`
|
const imagePath = `/src/assets/social/${imageFile}`
|
||||||
const images_logos = import.meta.glob<{ default: ImageMetadata }>(
|
const images_logos = import.meta.glob<GlobImage>(
|
||||||
'/src/assets/social/*.{png,webp}'
|
'/src/assets/social/*.{png,webp}'
|
||||||
)
|
)
|
||||||
|
const loadedLogoImage = plsLoadImage(images_logos, imagePath)
|
||||||
---
|
---
|
||||||
|
|
||||||
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={site}>
|
<a
|
||||||
|
href={link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
aria-label=`${site} - new window`
|
||||||
|
>
|
||||||
<article class="contact do-hover">
|
<article class="contact do-hover">
|
||||||
<Image src={images_logos[imagePath]()} alt="" />
|
<Image src={loadedLogoImage} alt="" loading={'eager'} width={128} height={128} quality={100}/>
|
||||||
<div>
|
<div>
|
||||||
<h2>{site}</h2>
|
<h2>{site}</h2>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
<span class="visually-hidden">{site}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
--s-image: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
--size-value: 6.25rem;
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--c-darkergray);
|
justify-content: center;
|
||||||
border-color: var(--c-darkgray);
|
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: transform var(--speed) var(--ease);
|
background-color: var(--c-primary-background);
|
||||||
|
border: 2px solid var(--c-primary-background);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
padding: 6px;
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article img {
|
article img {
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
width: var(--size-value);
|
width: var(--s-image);
|
||||||
height: var(--size-value);
|
height: auto;
|
||||||
}
|
|
||||||
|
|
||||||
article:hover div {
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
padding: 0 0.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
height: calc(var(--size-value));
|
|
||||||
width: calc(var(--size-value) - 1rem);
|
|
||||||
opacity: 90%;
|
|
||||||
z-index: 100;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover h2 {
|
|
||||||
margin: 0;
|
|
||||||
display: inline-flex;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
article h2 {
|
|
||||||
position: absolute;
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article div {
|
article div {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
article:hover {
|
||||||
transform: scale(var(--hover-scale));
|
border-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
article:hover div {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
margin: unset;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
width: var(--s-image);
|
||||||
|
height: var(--s-image);
|
||||||
|
opacity: 90%;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
article:hover h2 {
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--c-primary-text);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
96
src/components/cards/favouriteCard.astro
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
---
|
||||||
|
import { Image } from 'astro:assets'
|
||||||
|
import type { GlobImage } from '../../types/generic'
|
||||||
|
import { plsLoadImage } from '../../utils/tools'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
name: string
|
||||||
|
origin: string
|
||||||
|
imageFile: string
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { link, imageFile, origin, name } = Astro.props
|
||||||
|
const imagePath = `/src/assets/favourites/${imageFile}`
|
||||||
|
const images = import.meta.glob<GlobImage>(
|
||||||
|
'/src/assets/favourites/*.{png,webp}'
|
||||||
|
)
|
||||||
|
const loadedImage = plsLoadImage(images, imagePath)
|
||||||
|
---
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
aria-label=`${name} - new window`
|
||||||
|
>
|
||||||
|
<div class="heading">{name}</div>
|
||||||
|
<Image src={loadedImage} alt={name} loading={'eager'} width={128} height={128} quality={100}/>
|
||||||
|
<h2 class="subtext">
|
||||||
|
{origin}
|
||||||
|
</h2>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.heading {
|
||||||
|
display: flex;
|
||||||
|
height: 3.5rem;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
max-width: 150px;
|
||||||
|
padding-bottom: 0.3rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
border-radius: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
padding: 0em 0.25em;
|
||||||
|
width: max(40%, 128px);
|
||||||
|
height: auto;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 2px var(--c-primary-background) solid;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 2px var(--c-accent-1) solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: min(90%, 100px);
|
||||||
|
margin: 0px 0.5rem;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 512px) {
|
||||||
|
a {
|
||||||
|
padding: 10px 10px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
img {
|
||||||
|
width: 128px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtext {
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0.5rem;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
75
src/components/cards/projectCard.astro
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
url: string
|
||||||
|
description: string,
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { url, description, name } = Astro.props
|
||||||
|
const target = url.startsWith("/") ? "" : "_blank"
|
||||||
|
---
|
||||||
|
|
||||||
|
<a href={url} rel="noopener noreferrer" target={target}>
|
||||||
|
<h2>{name}</h2>
|
||||||
|
<p>{description}</p>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
a {
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: fit-content;
|
||||||
|
min-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 12em;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0px 0.5rem;
|
||||||
|
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border: 2px solid var(--c-primary-background);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
text-align: justify;
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
a > h2 {
|
||||||
|
margin: 0.3rem 0.5rem;
|
||||||
|
color: var(--c-accent-1);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a > h3 {
|
||||||
|
margin: 0.2em 0.5rem;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 420px) {
|
||||||
|
a {
|
||||||
|
min-width: 24em;
|
||||||
|
max-width: 24em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,6 +1,7 @@
|
||||||
---
|
---
|
||||||
import type { ImageMetadata } from 'astro';
|
|
||||||
import { Image } from 'astro:assets';
|
import { Image } from 'astro:assets';
|
||||||
|
import type { GlobImage } from '../../types/generic'
|
||||||
|
import { plsLoadImage } from '../../utils/tools'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
name: string
|
name: string
|
||||||
|
@ -11,83 +12,69 @@ export interface Props {
|
||||||
bondceImageFile: string
|
bondceImageFile: string
|
||||||
ml: string
|
ml: string
|
||||||
bond10: boolean
|
bond10: boolean
|
||||||
|
index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bond10, ml, bondceImageFile, servantImageFile, np, skills, level, name } = Astro.props
|
const { bond10, ml, bondceImageFile, servantImageFile, np, skills, level, name, index } = Astro.props
|
||||||
const servantImagePath = `/src/assets/servant/${servantImageFile}`
|
const servantImagePath = `/src/assets/servant/${servantImageFile}`
|
||||||
const bondceImagePath = `/src/assets/ce/bond-ce/${bondceImageFile}`
|
const bondceImagePath = `/src/assets/ce/bond-ce/${bondceImageFile}`
|
||||||
const images_servants = import.meta.glob<{ default: ImageMetadata }>('/src/assets/servant/*.{png,webp}')
|
const images_servants = import.meta.glob<GlobImage>('/src/assets/servant/*.{png,webp}')
|
||||||
const images_bond_ces = import.meta.glob<{ default: ImageMetadata }>('/src/assets/ce/bond-ce/*.{png,webp}')
|
const images_bond_ces = import.meta.glob<GlobImage>('/src/assets/ce/bond-ce/*.{png,webp}')
|
||||||
|
const loadedServantImage = plsLoadImage(images_servants, servantImagePath)
|
||||||
|
const loadedBondCEImage = plsLoadImage(images_bond_ces, bondceImagePath)
|
||||||
|
|
||||||
let bondce_css: string = 'bond-ce'
|
let bondce_css: string = 'bond-ce'
|
||||||
|
|
||||||
if (bond10 === false) {
|
if (bond10 === false) {
|
||||||
bondce_css += ' unobtained'
|
bondce_css += ' unobtained'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loading: "eager" | "lazy" = index <= 3 ? "eager" : "lazy";
|
||||||
---
|
---
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<Image src={images_servants[servantImagePath]()} alt={name} />
|
<Image src={loadedServantImage} width={128} height={128} alt={name} quality={100} loading={loading}/>
|
||||||
<h2 class="subtext">
|
<h2 class="subtext">
|
||||||
Level {level}<br />
|
Level {level}<br />
|
||||||
{skills}<br />
|
{skills}<br />
|
||||||
NP {np}
|
NP {np}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="expand-on-hover">
|
<div class="expand-on-hover">
|
||||||
<Image src={images_bond_ces[bondceImagePath]()} alt="" class={bondce_css}/>
|
<Image src={loadedBondCEImage} alt="" class={bondce_css}/>
|
||||||
<h2 class="subtext">Mana Loading: {ml === 'Not Unlocked' && <br />}{ml}</h2>
|
<h2 class="subtext">Mana Loading: {ml === 'Not Unlocked' && <br />}{ml}</h2>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.heading {
|
|
||||||
display: flex;
|
|
||||||
height: 4rem;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 24px;
|
|
||||||
color: white;
|
|
||||||
max-width: 200px;
|
|
||||||
padding-bottom: 0.3rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.servants-container {
|
|
||||||
row-gap: 1em;
|
|
||||||
column-gap: 1em;
|
|
||||||
justify-content: center;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover {
|
article:hover {
|
||||||
transform: scale(var(--hover-scale));
|
transition: border-color var(--a-time-default) var(--a-animation-1), transform var(--a-time-default) var(--a-animation-1);
|
||||||
|
transform: scale(1);
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: var(--c-darkergray);
|
background-color: var(--c-primary-background);
|
||||||
border-color: var(--c-darkgray);
|
border: 2px var(--c-primary-background) solid;
|
||||||
padding: 0em 0.75em 0.5rem 0.75em;
|
padding: 10px;
|
||||||
width: 40%;
|
width: max(30%, 100px);
|
||||||
height: auto;
|
height: auto;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: transform var(--speed) var(--ease);
|
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
article > img {
|
article > img {
|
||||||
width: 7.5rem;
|
width: 90%;
|
||||||
height: 7.5rem;
|
height: auto;
|
||||||
border-radius: 2.5rem;
|
border-radius: 2.5rem;
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
.subtext {
|
.subtext {
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 5;
|
margin: 5;
|
||||||
|
@ -95,25 +82,26 @@ if (bond10 === false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-on-hover {
|
.expand-on-hover {
|
||||||
background-color: var(--c-duskgray);
|
background-color: var(--c-primary-background);
|
||||||
|
border: 2px var(--c-primary-background) solid;
|
||||||
|
border-top: 0px;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
transform: scaleY(0);
|
transform: scaleY(0);
|
||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 92.5%;
|
top: 92.5%;
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
border-radius: 0px 0px 1.25rem 1.25rem;
|
border-radius: 0px 0px 1.25rem 1.25rem;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
article:hover .expand-on-hover {
|
article:hover .expand-on-hover {
|
||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
transition: transform 200ms ease-in-out;
|
border-color: var(--c-accent-1);
|
||||||
background-color: var(--c-duskgray);
|
transition: border-color var(--a-time-default) var(--a-animation-1), transform var(--a-time-default) var(--a-animation-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bond-ce {
|
.bond-ce {
|
||||||
padding-top: 0.75rem;
|
padding-top: 0.25rem;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
}
|
}
|
||||||
|
@ -130,16 +118,12 @@ if (bond10 === false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
article > img {
|
article > img {
|
||||||
width: 200px;
|
width: 128px;
|
||||||
height: 200px;
|
height: 128px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bond-ce {
|
.bond-ce {
|
||||||
height: 3.5rem;
|
height: 3rem;
|
||||||
}
|
|
||||||
|
|
||||||
.expand-on-hover {
|
|
||||||
top: 95%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
189
src/components/cards/taCard.astro
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
---
|
||||||
|
import { Image } from 'astro:assets'
|
||||||
|
import type { GlobImage } from '../../types/generic'
|
||||||
|
import { plsLoadImage } from '../../utils/tools'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
date: string
|
||||||
|
title: string
|
||||||
|
link: string
|
||||||
|
targetImageFile: string
|
||||||
|
user?: string
|
||||||
|
servantImageFile?: string
|
||||||
|
turns?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { turns, targetImageFile, user, date, servantImageFile, link, title } =
|
||||||
|
Astro.props
|
||||||
|
|
||||||
|
const options_date: Intl.DateTimeFormatOptions = {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetImagePath = `/src/assets/ta_icons/${targetImageFile}`
|
||||||
|
const servantImagePath = `/src/assets/ta_icons/ta_servants/${servantImageFile}`
|
||||||
|
|
||||||
|
const formatted_date = new Date(date).toLocaleDateString('de-DE', options_date)
|
||||||
|
const target_images = import.meta.glob<GlobImage>(
|
||||||
|
'/src/assets/ta_icons/*.{png,webp}'
|
||||||
|
)
|
||||||
|
const servant_images = import.meta.glob<GlobImage>(
|
||||||
|
'/src/assets/ta_icons/ta_servants/*.{png,webp}'
|
||||||
|
)
|
||||||
|
|
||||||
|
const loadedServantImage = plsLoadImage(servant_images, servantImagePath)
|
||||||
|
const loadedTargetImage = plsLoadImage(target_images, targetImagePath)
|
||||||
|
|
||||||
|
const hasuser = user !== undefined ? 'display: flex' : 'display: none'
|
||||||
|
---
|
||||||
|
|
||||||
|
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
|
||||||
|
<div class="imagecontainer">
|
||||||
|
<Image src={loadedTargetImage} alt={title} />
|
||||||
|
<div class="title">
|
||||||
|
<h2>{title}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<span style={hasuser}>By {user}<br />•</span>
|
||||||
|
{formatted_date}
|
||||||
|
</p>
|
||||||
|
<div class="expand-on-hover">
|
||||||
|
<Image src={loadedServantImage} alt="" />
|
||||||
|
<h2>{turns}</h2>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border: 2px var(--c-primary-background) solid;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
width: max(30%, 100px);
|
||||||
|
height: auto;
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1), transform var(--a-time-default) var(--a-animation-1);
|
||||||
|
transform: scale(1);
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imagecontainer {
|
||||||
|
display: flex;
|
||||||
|
width: 90%;
|
||||||
|
height: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.imagecontainer > img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover .title {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 90%;
|
||||||
|
box-shadow: 0px 0px 0px 1px var(--c-primary-background);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover .title h2 {
|
||||||
|
margin: 4px;
|
||||||
|
display: inline-flex;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a .title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0.5rem 0px 0.625rem 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-on-hover {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: top;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border: 2px var(--c-primary-background) solid;
|
||||||
|
border-top: 0px;
|
||||||
|
z-index: 99;
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: top;
|
||||||
|
position: absolute;
|
||||||
|
top: 87.5%;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
border-radius: 0px 0px 1.25rem 1.25rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-on-hover img {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-on-hover h2 {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover .expand-on-hover {
|
||||||
|
transform: scaleY(1);
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1), transform var(--a-time-default) var(--a-animation-1), background-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 512px) {
|
||||||
|
a {
|
||||||
|
padding: 10px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a > .imagecontainer {
|
||||||
|
width: 112px;
|
||||||
|
height: 112px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,7 @@
|
||||||
---
|
---
|
||||||
import type { ImageMetadata } from 'astro'
|
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
|
import type { GlobImage } from '../../types/generic'
|
||||||
|
import { plsLoadImage } from '../../utils/tools'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
@ -10,79 +11,75 @@ export interface Props {
|
||||||
|
|
||||||
const { imageFile, link, title } = Astro.props
|
const { imageFile, link, title } = Astro.props
|
||||||
const imagePath = `/src/assets/technologies/${imageFile}`
|
const imagePath = `/src/assets/technologies/${imageFile}`
|
||||||
const images_logos = import.meta.glob<{ default: ImageMetadata }>(
|
const images_logos = import.meta.glob<GlobImage>(
|
||||||
'/src/assets/technologies/*.{png,webp}'
|
'/src/assets/technologies/*.{png,webp}'
|
||||||
)
|
)
|
||||||
|
const loadedImage = plsLoadImage(images_logos, imagePath)
|
||||||
---
|
---
|
||||||
|
|
||||||
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
|
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
|
||||||
<article>
|
<article>
|
||||||
<Image src={images_logos[imagePath]()} alt="" />
|
<Image src={loadedImage} alt="" loading={'eager'} width={150} height={150} quality={100}/>
|
||||||
<div>
|
<div>
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
<span class="visually-hidden">{title}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
--size-value: 6.25rem;
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
background-color: var(--c-darkergray);
|
|
||||||
border-color: var(--c-darkgray);
|
|
||||||
padding: 10px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: transform var(--speed) var(--ease);
|
background-color: var(--c-primary-background);
|
||||||
|
border: 2px solid var(--c-primary-background);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
padding: 6px;
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
line-height: 100px;
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
article:hover {
|
|
||||||
transform: scale(var(--hover-scale));
|
|
||||||
}
|
|
||||||
article > img {
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
width: var(--size-value);
|
|
||||||
height: var(--size-value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article:hover div {
|
article img {
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
padding: 0 0.5rem;
|
width: 80px;
|
||||||
display: flex;
|
height: auto;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
height: calc(var(--size-value));
|
|
||||||
width: calc(var(--size-value) - 1rem);
|
|
||||||
opacity: 90%;
|
|
||||||
z-index: 100;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover h2 {
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
display: inline-flex;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 150%;
|
|
||||||
}
|
|
||||||
|
|
||||||
article h2 {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article div {
|
article div {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article:hover {
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
article:hover div {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
margin: unset;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
opacity: 90%;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
article:hover h2 {
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,87 +0,0 @@
|
||||||
---
|
|
||||||
import type { ImageMetadata } from 'astro';
|
|
||||||
import { Image } from 'astro:assets';
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
name: string
|
|
||||||
origin: string
|
|
||||||
imageFile: string
|
|
||||||
link: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const { link, imageFile, origin, name } = Astro.props
|
|
||||||
const imagePath = `/src/assets/favourites/${imageFile}`
|
|
||||||
const images = import.meta.glob<{ default: ImageMetadata }>('/src/assets/favourites/*.{png,webp}')
|
|
||||||
---
|
|
||||||
|
|
||||||
<a href={link} target="_blank" rel="noopener noreferrer">
|
|
||||||
<div class="heading">{name}</div>
|
|
||||||
<Image src={images[imagePath]()} alt={name} />
|
|
||||||
<h2 class="subtext">
|
|
||||||
{origin}
|
|
||||||
</h2>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.heading {
|
|
||||||
display: flex;
|
|
||||||
height: 4rem;
|
|
||||||
font-size: 24px;
|
|
||||||
color: white;
|
|
||||||
max-width: 200px;
|
|
||||||
padding-bottom: 0.3rem;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
transform: scale(var(--hover-scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
border-radius: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: var(--c-darkergray);
|
|
||||||
border-color: var(--c-darkgray);
|
|
||||||
padding: 0em 0.75em;
|
|
||||||
width: 40%;
|
|
||||||
height: auto;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
transition: transform var(--speed) var(--ease);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a > img {
|
|
||||||
padding: 0px 0.25em;
|
|
||||||
width: 90%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 512px) {
|
|
||||||
a {
|
|
||||||
padding: 10px 10px;
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
a > img {
|
|
||||||
padding: 0px 1.5em;
|
|
||||||
width: 200px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtext {
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 5;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
74
src/components/fgotaHero.astro
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
fadeout?: boolean
|
||||||
|
}
|
||||||
|
const { fadeout } = Astro.props
|
||||||
|
const display = fadeout ? "": "display: none"
|
||||||
|
---
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
I am currently creating a site to catalogue past and future TAs for the whole community.
|
||||||
|
</span>
|
||||||
|
<a href=`https://fgo-ta.com/` target="_blank" rel="noopener noreferrer"><span class="fancy">Visit fgo-ta.com >></span></a>
|
||||||
|
<div class="fade" style={display}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
flex-flow: column;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
font-size: 1.5em;
|
||||||
|
padding: 2rem 0rem 0rem 0rem;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, 'Segoe UI', sans-serif;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
margin: 1rem 0px 0px;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
text-decoration: none;
|
||||||
|
background: var(--c-primary-background);
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px var(--c-accent-1) solid;
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
border-color: var(--c-accent-1-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade {
|
||||||
|
margin-top: 3rem;
|
||||||
|
background: linear-gradient(to bottom, transparent, var(--c-secondary-background));
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: 0 .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fancy {
|
||||||
|
color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (background-clip: text) {
|
||||||
|
a:hover .fancy {
|
||||||
|
transition: color var(--a-time-default) var(--a-animation-1);
|
||||||
|
background: linear-gradient(125deg, var(--c-accent-1), var(--c-accent-1-alt), var(--c-accent-2) );
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -12,11 +12,11 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 5em;
|
height: 5em;
|
||||||
background-color: var(--c-gray);
|
background-color: var(--c-secondary-background);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
49
src/components/links/linkCard.astro
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
import type { ImageMetadata } from 'astro'
|
||||||
|
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
text: string
|
||||||
|
icon: ImageMetadata
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { text, icon, link } = Astro.props
|
||||||
|
const icon_src_url = `url("${icon.src}")`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||||
|
<div class="icon"></div>
|
||||||
|
<span class="visually-hidden">{text}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style define:vars={{ icon_src_url }}>
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2.25em;
|
||||||
|
height: 2.25em;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border-radius: .35rem;
|
||||||
|
border: 2px solid var(--c-primary-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
& .icon {
|
||||||
|
transition: background-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
background-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
mask: var(--icon_src_url) no-repeat center;
|
||||||
|
mask-size: contain;
|
||||||
|
background-color: var(--c-primary-text);
|
||||||
|
height: 1.5em;
|
||||||
|
width: 1.5em;
|
||||||
|
}
|
||||||
|
</style>
|
29
src/components/links/linkContainer.astro
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
import LinkCard from './linkCard.astro'
|
||||||
|
import socialData from '@datafiles/socials.json'
|
||||||
|
import { socials } from './socials'
|
||||||
|
---
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
socialData.map((item) => (
|
||||||
|
<LinkCard
|
||||||
|
{...{ icon: socials[item.icon], text: item.icon, link: item.link }}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 95%;
|
||||||
|
height: auto;
|
||||||
|
column-gap: 0.5rem;
|
||||||
|
row-gap: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
22
src/components/links/socials.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import discord from '@assets/custom-svg/discord.svg'
|
||||||
|
import forgejo from '@assets/custom-svg/forgejo.svg'
|
||||||
|
import rayshift from '@assets/custom-svg/rayshift.svg'
|
||||||
|
import reddit from '@assets/custom-svg/reddit.svg'
|
||||||
|
import twitch from '@assets/custom-svg/twitch.svg'
|
||||||
|
import bluesky from '@assets/custom-svg/bluesky.svg'
|
||||||
|
|
||||||
|
import type { ImageMetadata } from 'astro'
|
||||||
|
|
||||||
|
import mastodon from 'iconoir/icons/mastodon.svg'
|
||||||
|
import youtube from 'iconoir/icons/youtube.svg'
|
||||||
|
|
||||||
|
export const socials: Record<string, ImageMetadata> = {
|
||||||
|
forgejo,
|
||||||
|
rayshift,
|
||||||
|
reddit,
|
||||||
|
twitch,
|
||||||
|
discord,
|
||||||
|
mastodon,
|
||||||
|
bluesky,
|
||||||
|
youtube,
|
||||||
|
}
|
|
@ -1,133 +0,0 @@
|
||||||
---
|
|
||||||
import { Image } from 'astro:assets';
|
|
||||||
import logo from '../assets/logo.svg'
|
|
||||||
---
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<a href="/" rel="noopener noreferrer" aria-label="Home">
|
|
||||||
<Image src={logo} alt="Website Logo"/>
|
|
||||||
</a>
|
|
||||||
<ul class="desktop">
|
|
||||||
<slot />
|
|
||||||
</ul>
|
|
||||||
<button class="mobile" aria-label="Navigation Button" tabindex="0" onclick="this.focus()">
|
|
||||||
<ul>
|
|
||||||
<slot />
|
|
||||||
</ul>
|
|
||||||
<div class="placeholder"></div>
|
|
||||||
<i class="iconoir-menu"></i>
|
|
||||||
</button>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
header {
|
|
||||||
z-index: 1000;
|
|
||||||
position: sticky;
|
|
||||||
top: 0px;
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
display: flex;
|
|
||||||
height: auto;
|
|
||||||
width: 100%;
|
|
||||||
align-items: flex-start;
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
header > a {
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-top: 8px;
|
|
||||||
display: block;
|
|
||||||
height: 48px;
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
a > img {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.desktop {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
display: none;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
flex-direction: row;
|
|
||||||
height: 64px;
|
|
||||||
width: 100%;
|
|
||||||
list-style-type: none;
|
|
||||||
row-gap: 0.5em;
|
|
||||||
column-gap: 0.5ch;
|
|
||||||
margin: 0px;
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
.mobile > ul {
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
flex-direction: column;
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
list-style-type: none;
|
|
||||||
line-height: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile {
|
|
||||||
display: flex;
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
border: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile > i {
|
|
||||||
position: static;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 2em;
|
|
||||||
align-self: flex-start;
|
|
||||||
padding-right: 1em;
|
|
||||||
padding-top: 1.15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile > ul {
|
|
||||||
display: none;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile:focus-within > ul {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile:focus-within > .placeholder {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile:focus-within {
|
|
||||||
justify-content: top;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
header:focus-within > a {
|
|
||||||
display: flex;
|
|
||||||
justify-self: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1140px) {
|
|
||||||
.mobile {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.desktop {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
height: 64px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header > a {
|
|
||||||
padding-top: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
37
src/components/navbar/navbar.astro
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
import NavbarDesktop from './navbarDesktop.astro'
|
||||||
|
import NavbarLogo from './navbarLogo.astro'
|
||||||
|
import NavbarMobile from './navbarMobile.astro'
|
||||||
|
---
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<NavbarLogo/>
|
||||||
|
<NavbarDesktop>
|
||||||
|
<slot name="desktop" />
|
||||||
|
</NavbarDesktop>
|
||||||
|
<NavbarMobile>
|
||||||
|
<slot name="mobile" />
|
||||||
|
</NavbarMobile>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
header {
|
||||||
|
z-index: 1000;
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
display: flex;
|
||||||
|
height: auto;
|
||||||
|
width: 100%;
|
||||||
|
align-items: flex-start;
|
||||||
|
line-height: 1.5em;
|
||||||
|
border-bottom: 2px solid var(--c-accent-1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1140px) {
|
||||||
|
header {
|
||||||
|
height: 64px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
29
src/components/navbar/navbarDesktop.astro
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<slot />
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
ul {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
display: none;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 64px;
|
||||||
|
width: 100%;
|
||||||
|
list-style-type: none;
|
||||||
|
row-gap: 0.5em;
|
||||||
|
column-gap: 0.5ch;
|
||||||
|
margin: 0px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
@media (min-width: 1140px) {
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
92
src/components/navbar/navbarEntry.astro
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
currentPage?: string
|
||||||
|
navtype: 'mobile' | 'desktop'
|
||||||
|
link: string
|
||||||
|
text: string
|
||||||
|
icon: ImageMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
const { icon, text, link, navtype, currentPage } = Astro.props
|
||||||
|
|
||||||
|
let currPage = ''
|
||||||
|
const slug = link.replace(new RegExp('/', 'g'), '')
|
||||||
|
|
||||||
|
if (currentPage === slug) {
|
||||||
|
currPage = 'current'
|
||||||
|
} else if (currentPage === 'home' && link === '/') {
|
||||||
|
currPage = 'current'
|
||||||
|
}
|
||||||
|
|
||||||
|
const icon_src_url = `url("${icon.src}")`
|
||||||
|
const fulllink = `/${slug}`
|
||||||
|
|
||||||
|
let extraattributes = navtype === 'mobile' ? { tabindex: '0' } : {}
|
||||||
|
---
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={fulllink}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
aria-label={text}
|
||||||
|
class={currPage}
|
||||||
|
role="button"
|
||||||
|
{...extraattributes}
|
||||||
|
>
|
||||||
|
<div class="icon"></div>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<style define:vars={{ icon_src_url }}>
|
||||||
|
* {
|
||||||
|
transition: color var(--a-time-default) var(--a-animation-1), background-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > a {
|
||||||
|
display: inline-flex;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
text-decoration: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.4em;
|
||||||
|
height: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
gap: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > a:hover {
|
||||||
|
color: var(--c-accent-1-alt) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > a:hover > .icon {
|
||||||
|
background-color: var(--c-accent-1-alt) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
color: var(--c-accent-1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current > .icon {
|
||||||
|
background-color: var(--c-accent-1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
mask: var(--icon_src_url) no-repeat center;
|
||||||
|
background-color: var(--c-primary-text);
|
||||||
|
width: 1.4em;
|
||||||
|
height: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 304px) {
|
||||||
|
li {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
32
src/components/navbar/navbarLogo.astro
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
import { Image } from 'astro:assets'
|
||||||
|
import logo from '@assets/logo.svg'
|
||||||
|
---
|
||||||
|
|
||||||
|
<a href="/" rel="noopener noreferrer" aria-label="Home" role="button">
|
||||||
|
<Image src={logo} alt="" loading="eager" />
|
||||||
|
<span class="visually-hidden">Logo</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
a {
|
||||||
|
margin-left: 16px;
|
||||||
|
padding-top: 8px;
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
display: flex;
|
||||||
|
justify-self: top;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a > img {
|
||||||
|
height: 42px;
|
||||||
|
width: 42px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
@media (min-width: 1140px) {
|
||||||
|
a {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
85
src/components/navbar/navbarMobile.astro
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
---
|
||||||
|
import hamburger from 'iconoir/icons/menu.svg'
|
||||||
|
const hamburger_src_url = `url("${hamburger.src}")`
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<input type="checkbox" id="dropdown" />
|
||||||
|
<ul class="dropdown-content">
|
||||||
|
<slot />
|
||||||
|
</ul>
|
||||||
|
<label for="dropdown" class="dropdown-btn" aria-label="Menu Toggle">
|
||||||
|
<div class="hamburger-menu" role="navigation"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const dropdownBtn = document.querySelector(".dropdown-btn")!;
|
||||||
|
const dropdownContent = document.querySelector(".dropdown-content")!;
|
||||||
|
|
||||||
|
dropdownBtn.addEventListener("click", () => {
|
||||||
|
dropdownContent.classList.toggle("menu-open");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style define:vars={{ hamburger_src_url }}>
|
||||||
|
input[type="checkbox"] {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content.menu-open {
|
||||||
|
display: flex;
|
||||||
|
height: auto;
|
||||||
|
transform: translateY(0);
|
||||||
|
transition: transform var(--a-time-default) var(--a-animation-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content {
|
||||||
|
display: none;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
width: 100%;
|
||||||
|
list-style-type: none;
|
||||||
|
line-height: 3em;
|
||||||
|
margin: 0.5rem 0px 0px;
|
||||||
|
padding: 0;
|
||||||
|
transform: translateY(-1em);
|
||||||
|
transition: transform var(--a-time-default) var(--a-animation-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-btn {
|
||||||
|
height: 64px;
|
||||||
|
display:flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
border: 0px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger-menu {
|
||||||
|
mask: var(--hamburger_src_url) no-repeat center;
|
||||||
|
mask-size: cover;
|
||||||
|
background-color: var(--c-primary-text);
|
||||||
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1140px) {
|
||||||
|
.dropdown {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,61 +0,0 @@
|
||||||
---
|
|
||||||
export interface Props {
|
|
||||||
currentPage?: string
|
|
||||||
link: string
|
|
||||||
text: string
|
|
||||||
icon: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const { icon, text, link, currentPage } = Astro.props
|
|
||||||
|
|
||||||
let currPage = ''
|
|
||||||
const slug = link.replace(new RegExp('/', 'g'), '')
|
|
||||||
|
|
||||||
if (currentPage === slug) {
|
|
||||||
currPage = 'current'
|
|
||||||
} else if (currentPage === 'home' && link === '/') {
|
|
||||||
currPage = 'current'
|
|
||||||
}
|
|
||||||
|
|
||||||
const fulllink = `/${slug}`
|
|
||||||
---
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
href={fulllink}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label={text}
|
|
||||||
class={currPage}
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<i class={icon}></i>
|
|
||||||
{text}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
li {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: left;
|
|
||||||
display: flex;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
li > a {
|
|
||||||
display: inline-flex;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 1.4em;
|
|
||||||
height: 100%;
|
|
||||||
font-weight: bold;
|
|
||||||
gap: 0.2em;
|
|
||||||
}
|
|
||||||
li > a:hover {
|
|
||||||
color: var(--c-purplepink);
|
|
||||||
}
|
|
||||||
.current {
|
|
||||||
color: var(--c-darkpurple) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
16
src/components/placeholder.astro
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
height?: string
|
||||||
|
}
|
||||||
|
const { height } = Astro.props
|
||||||
|
const ph_height = height || "5rem"
|
||||||
|
---
|
||||||
|
<div class="placeholder"></div>
|
||||||
|
|
||||||
|
<style define:vars={{ph_height}}>
|
||||||
|
.placeholder {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: var(--ph_height);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,194 +0,0 @@
|
||||||
---
|
|
||||||
import type { ImageMetadata } from 'astro'
|
|
||||||
import { Image } from 'astro:assets'
|
|
||||||
export interface Props {
|
|
||||||
date: string
|
|
||||||
title: string
|
|
||||||
link: string
|
|
||||||
targetImageFile: string
|
|
||||||
user?: string
|
|
||||||
servantImageFile?: string
|
|
||||||
turns?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const { turns, targetImageFile, user, date, servantImageFile, link, title } =
|
|
||||||
Astro.props
|
|
||||||
|
|
||||||
const options_date: Intl.DateTimeFormatOptions = {
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetImagePath = `/src/assets/ta_icons/${targetImageFile}`
|
|
||||||
const servantImagePath = `/src/assets/ta_icons/ta_servants/${servantImageFile}`
|
|
||||||
|
|
||||||
const formatted_date = new Date(date).toLocaleDateString('de-DE', options_date)
|
|
||||||
const target_images = import.meta.glob<{ default: ImageMetadata }>(
|
|
||||||
'/src/assets/ta_icons/*.{png,webp}'
|
|
||||||
)
|
|
||||||
const servant_images = import.meta.glob<{ default: ImageMetadata }>(
|
|
||||||
'/src/assets/ta_icons/ta_servants/*.{png,webp}'
|
|
||||||
)
|
|
||||||
|
|
||||||
let hasuser = ''
|
|
||||||
if (user !== undefined) {
|
|
||||||
hasuser = 'hasuser'
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
|
|
||||||
<article>
|
|
||||||
<Image src={target_images[targetImagePath]()} alt={title} class="icon" />
|
|
||||||
<div class="title">
|
|
||||||
<h2>{title}</h2>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
<span class={hasuser}>
|
|
||||||
By {user}<br /> •
|
|
||||||
</span>
|
|
||||||
{formatted_date}
|
|
||||||
</p>
|
|
||||||
<div class="expand-on-hover">
|
|
||||||
<Image src={servant_images[servantImagePath ?? 'undefined']()} alt="" />
|
|
||||||
<h2>{turns}</h2>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hasuser {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
|
||||||
background-color: var(--c-darkergray);
|
|
||||||
border-color: var(--c-darkgray);
|
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
|
||||||
transition: transform var(--speed) var(--ease);
|
|
||||||
height: auto;
|
|
||||||
width: auto;
|
|
||||||
max-width: 8rem;
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
padding-bottom: 1.5rem;
|
|
||||||
--size-value: 7rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover {
|
|
||||||
transform: scale(var(--hover-scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
article > .icon {
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
width: var(--size-value);
|
|
||||||
height: var(--size-value);
|
|
||||||
margin: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover .title {
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
height: calc(var(--size-value) + 0.1rem);
|
|
||||||
width: calc(var(--size-value) + 0.1rem);
|
|
||||||
opacity: 90%;
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
top: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover .title h2 {
|
|
||||||
margin: 0;
|
|
||||||
display: inline-flex;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 150%;
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .title h2 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
article .title {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
display: flex;
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 100%;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: bold;
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
margin: 0.5rem 0px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-on-hover {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
background-color: var(--c-duskgray);
|
|
||||||
z-index: 99;
|
|
||||||
transform: scaleY(0);
|
|
||||||
transform-origin: top;
|
|
||||||
position: absolute;
|
|
||||||
top: 90%;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
color: white;
|
|
||||||
border-radius: 0px 0px 1.25rem 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-on-hover img {
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-on-hover h2 {
|
|
||||||
margin: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article:hover .expand-on-hover {
|
|
||||||
transform: scaleY(1);
|
|
||||||
transition: transform 200ms ease-in-out;
|
|
||||||
background-color: var(--c-duskgray);
|
|
||||||
}
|
|
||||||
</style>
|
|
113
src/components/titles/smallTitle.astro
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
maintext: string
|
||||||
|
subtext: string
|
||||||
|
fadeout?: boolean
|
||||||
|
baseurl?: string
|
||||||
|
returnbutton?: boolean
|
||||||
|
buttontext?: string
|
||||||
|
}
|
||||||
|
const { maintext, subtext, fadeout, baseurl, returnbutton, buttontext } = Astro.props
|
||||||
|
const displayFadeout = fadeout ? "": "display: none"
|
||||||
|
const displayBackButton = returnbutton ? "": "display: none"
|
||||||
|
const text = buttontext || baseurl
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="head">{maintext}</div>
|
||||||
|
<div class="sub">{subtext}</div>
|
||||||
|
<a href=`/${baseurl}` style={displayBackButton}><< Back to {text}</a>
|
||||||
|
<div class="fade" style={displayFadeout}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, 'Segoe UI', sans-serif;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
margin: 1rem 0px 0px;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: var(--c-secondary-background);
|
||||||
|
border-radius: 10px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 2px;
|
||||||
|
border-color: var(--c-secondary-background);
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
transition: border-color var(--a-time-default) var(--a-animation-1);
|
||||||
|
border-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade {
|
||||||
|
margin-top: 2rem;
|
||||||
|
background: linear-gradient(to bottom, transparent, var(--c-secondary-background));
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head {
|
||||||
|
margin: 0 .5rem;
|
||||||
|
hyphens: auto;
|
||||||
|
padding-top: 2rem;
|
||||||
|
font-size: 3.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size-adjust: 0.52;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub {
|
||||||
|
margin: 0 .5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size-adjust: 0.52;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head {
|
||||||
|
color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (background-clip: text) {
|
||||||
|
.head {
|
||||||
|
background: linear-gradient(125deg, var(--c-accent-1), var(--c-accent-1-alt), var(--c-accent-2) );
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 620px) {
|
||||||
|
.head {
|
||||||
|
hyphens: none;
|
||||||
|
font-size: 3.75rem;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
.head {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
82
src/components/titles/title.astro
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
---
|
||||||
|
import LinkContainer from "../links/linkContainer.astro";
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="head">Hi, I'm <span class="fancy">Firq</span></div>
|
||||||
|
<div class="sub"></ Software dev / Scathach enthusiast / Germany /></div>
|
||||||
|
<LinkContainer />
|
||||||
|
<div class="fade"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
color: var(--c-primary-text);
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
background: linear-gradient(to bottom, transparent, var(--c-secondary-background));
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head {
|
||||||
|
padding-top: 2rem;
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size-adjust: 0.52;
|
||||||
|
line-height: 1.4;
|
||||||
|
min-height: 4.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: 'Work Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size-adjust: 0.52;
|
||||||
|
line-height: 1.4;
|
||||||
|
width: 97.5%;
|
||||||
|
min-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fancy {
|
||||||
|
color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (background-clip: text) {
|
||||||
|
.fancy {
|
||||||
|
background: linear-gradient(125deg, var(--c-accent-1), var(--c-accent-1-alt), var(--c-accent-2) );
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 620px) {
|
||||||
|
.head {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
.head {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
61
src/components/youtubeEmbed.astro
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
import { YouTube } from '@astro-community/astro-embed-youtube'
|
||||||
|
export interface Props {
|
||||||
|
id: string
|
||||||
|
thumbnail: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, thumbnail } = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="youtube-center">
|
||||||
|
<noscript style="display: flex; justify-content: center;">
|
||||||
|
<style>
|
||||||
|
lite-youtube {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="warningtext">
|
||||||
|
<span>
|
||||||
|
To view the video, enable JavaScript or<br /><a
|
||||||
|
href=`https://www.youtube.com/watch?v=${id}`
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer">watch it on YouTube</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
<YouTube id={id} poster={thumbnail} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.youtube-center {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
lite-youtube {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 360px;
|
||||||
|
max-width: 640px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warningtext {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--c-primary-background);
|
||||||
|
width: 640px;
|
||||||
|
height: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span > a {
|
||||||
|
font-style: unset;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
17
src/content.config.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { z, defineCollection } from 'astro:content'
|
||||||
|
import { glob } from 'astro/loaders';
|
||||||
|
|
||||||
|
const blogCollection = defineCollection({
|
||||||
|
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/data/blog" }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
tags: z.array(z.string()),
|
||||||
|
pubDate: z.date(),
|
||||||
|
description: z.string(),
|
||||||
|
author: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const collections = {
|
||||||
|
blog: blogCollection,
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'How Astro powers this site'
|
title: 'How Astro powers this site'
|
||||||
pubDate: 2023-03-09
|
pubDate: 2023-03-09
|
||||||
description: 'Blog post talking about how Astro provides the basis for this website'
|
description: 'Blog post talking about how Astro provides the basis for this website'
|
||||||
|
@ -75,7 +74,7 @@ allPosts.sort(
|
||||||
The `frontmatter` interface is a kind of header for the markdown files which provides astro with metadata like title, author and such.
|
The `frontmatter` interface is a kind of header for the markdown files which provides astro with metadata like title, author and such.
|
||||||
It is structured like this:
|
It is structured like this:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
layout: ../../layouts/blogPost.astro
|
||||||
title: 'How Astro powers this site'
|
title: 'How Astro powers this site'
|
|
@ -1,11 +1,13 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'How to: TA Cernunnos'
|
title: 'How to: TA Cernunnos'
|
||||||
pubDate: 2023-07-14
|
pubDate: 2023-07-14
|
||||||
description: 'A handful of observations from the cernunnos fight'
|
description: 'A handful of observations from the cernunnos fight'
|
||||||
author: 'Requiem & Firq'
|
author: 'Requiem & Firq'
|
||||||
tags: ['fgo', 'lostbelt 6', 'cernunnos']
|
tags: ['fgo', 'lostbelt 6', 'cernunnos']
|
||||||
---
|
---
|
||||||
|
import thumbnail_firq from "@assets/thumbnails/WrHudtdfivA.jpg"
|
||||||
|
import thumbnail_requiem from "@assets/thumbnails/O1f-go7uJQM.jpg"
|
||||||
|
import YoutubeEmbed from "@components/youtubeEmbed.astro"
|
||||||
|
|
||||||
## Foreword
|
## Foreword
|
||||||
|
|
||||||
|
@ -45,3 +47,19 @@ On the first break Cernunnos skill seals your servants. This means on turn 2 you
|
||||||
As an example:
|
As an example:
|
||||||
Let's say I used Alteras third skill turn 1. She has to wait 5 turns to reuse it and on turn 3 Vitch gives her 2 times cooldown reduction.
|
Let's say I used Alteras third skill turn 1. She has to wait 5 turns to reuse it and on turn 3 Vitch gives her 2 times cooldown reduction.
|
||||||
Ideally you would be able to reuse her third skill on turn 4 but because she gets skill sealed she can't do it.
|
Ideally you would be able to reuse her third skill on turn 4 but because she gets skill sealed she can't do it.
|
||||||
|
|
||||||
|
## Resulting runs
|
||||||
|
|
||||||
|
Both Requiem and I managed to get unique TAs done with Scathach (Requiem even with more servants). Those runs are embedded here:
|
||||||
|
|
||||||
|
### Firq
|
||||||
|
|
||||||
|
This run doesn't use the support Castoria provided by the game and instead makes use of Sherlock instead.
|
||||||
|
|
||||||
|
<YoutubeEmbed id="WrHudtdfivA" thumbnail={thumbnail_firq.src}/>
|
||||||
|
|
||||||
|
### Requiem
|
||||||
|
|
||||||
|
A frontline only run with double Castoria
|
||||||
|
|
||||||
|
<YoutubeEmbed id="O1f-go7uJQM" thumbnail={thumbnail_requiem.src}/>
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'Migrating to Forgejo'
|
title: 'Migrating to Forgejo'
|
||||||
pubDate: 2023-12-23
|
pubDate: 2023-12-23
|
||||||
description: 'My short recollection of migrating this site + CI to Forgejo'
|
description: 'My short recollection of migrating this site + CI to Forgejo'
|
||||||
|
@ -64,7 +63,6 @@ COPY --from=build /app/dist /public
|
||||||
COPY --from=build /app/serve.json /public/serve.json
|
COPY --from=build /app/serve.json /public/serve.json
|
||||||
RUN rm -r /public/assets/data/
|
RUN rm -r /public/assets/data/
|
||||||
|
|
||||||
ENV PORT 8081
|
|
||||||
EXPOSE 8081
|
EXPOSE 8081
|
||||||
|
|
||||||
CMD [ "serve", "public/", "-p", "8081" ]
|
CMD [ "serve", "public/", "-p", "8081" ]
|
||||||
|
@ -74,7 +72,7 @@ As you can see, I am using a custom container for the runtime stage, which will
|
||||||
|
|
||||||
### Custom serve docker - My new goto for static site serving
|
### Custom serve docker - My new goto for static site serving
|
||||||
|
|
||||||
When starting out with the `Dockerfile`, I first used the standard `node:lts` image for the runtime. This meant I also had to install the `serve` package by `@warren-bank` each time I built the container. Since this takes extra time and resources each run, I decided to create a pre-configured docker container that can be used for this instead.
|
When starting out with the `Dockerfile`, I first used the standard `node:lts` image for the runtime. This meant I also had to install the `serve` package by `@warren-bank` each time I built the container. Since this takes extra time and resources each run, I decided to create a pre-configured docker container that can be used for this instead.
|
||||||
|
|
||||||
The `Dockerfile` for that one is laughable simple:
|
The `Dockerfile` for that one is laughable simple:
|
||||||
|
|
||||||
|
@ -89,7 +87,7 @@ The container is also published to the docker registry that the Forgejo instance
|
||||||
|
|
||||||
Since my website is now using a docker container instead of the previous `rsync` + `screen` approach, a new deployment solution was needed.
|
Since my website is now using a docker container instead of the previous `rsync` + `screen` approach, a new deployment solution was needed.
|
||||||
|
|
||||||
In the end, Neshura proposed to use <a href="https://github.com/louislam/dockge" target="_blank" rel="noopener noreferrer" style="font-style: unset">Dockge</a>, a new, simple container management tool build by the developer of the beloved uptime-kuma. With that set up, getting the website only was really really easy:
|
In the end, Neshura proposed to use <a href="https://github.com/louislam/dockge" target="_blank" rel="noopener noreferrer" >Dockge</a>, a new, simple container management tool build by the developer of the beloved uptime-kuma. With that set up, getting the website only was really really easy:
|
||||||
|
|
||||||
1. Create a new stack
|
1. Create a new stack
|
||||||
2. Add a container entry
|
2. Add a container entry
|
||||||
|
@ -117,7 +115,8 @@ One last hurdle was, ironically, Gitlab. Specifically, it was their `lighthouse`
|
||||||
|
|
||||||
After some testing, I decided to just make my own version of the `lighthouse` container, without the user but with `unlighthouse` preinstalled (this also helps with processing times, as puppeteer takes a good amount of time to install each run).
|
After some testing, I decided to just make my own version of the `lighthouse` container, without the user but with `unlighthouse` preinstalled (this also helps with processing times, as puppeteer takes a good amount of time to install each run).
|
||||||
|
|
||||||
The `Dockerfile` can be found below (really simple again):
|
<details>
|
||||||
|
<summary>`Dockerfile` (really simple again)</summary>
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
FROM node:20.10.0-bookworm
|
FROM node:20.10.0-bookworm
|
||||||
|
@ -132,6 +131,8 @@ RUN apt-get update && apt-get -y install --no-install-recommends chromium=${CHRO
|
||||||
RUN npm install @unlighthouse/cli puppeteer
|
RUN npm install @unlighthouse/cli puppeteer
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
With this container, the CI step to actually run the tests became a lot easier:
|
With this container, the CI step to actually run the tests became a lot easier:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -161,4 +162,4 @@ In the end, I must say migrating was a lot more painless than expected. Sure, Fo
|
||||||
|
|
||||||
I also updated my about page to now reflect the migration, as the old technologies weren't up-to-date anymore.
|
I also updated my about page to now reflect the migration, as the old technologies weren't up-to-date anymore.
|
||||||
|
|
||||||
If you want to check out the repository by yourself, feel free to do so. <a href="https://forgejo.neshweb.net/Firq/firq-dev-website" target="_blank" rel="noopener noreferrer" style="font-style: unset">It is available on Neshuras Forgejo instance</a>
|
If you want to check out the repository by yourself, feel free to do so. <a href="https://forgejo.neshweb.net/Firq/firq-dev-website" target="_blank" rel="noopener noreferrer" >It is available on Neshuras Forgejo instance</a>
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'How to: Deploy this site'
|
title: 'How to: Deploy this site'
|
||||||
pubDate: 2023-03-17
|
pubDate: 2023-03-17
|
||||||
description: 'A summary of how this site is being deployed by diving into the gitlab-ci.yml'
|
description: 'A summary of how this site is being deployed by diving into the gitlab-ci.yml'
|
||||||
|
@ -61,7 +60,9 @@ deploy-site:
|
||||||
```
|
```
|
||||||
|
|
||||||
First installing ssh and readying everything from the CI variables, this was a big change from the Pages configuration. But there was one thing that really bothered me: The ssh call. If you look at it, you can see that I left out the commands that are passed to `ssh`. There is a good reason: These are waaaay too long.
|
First installing ssh and readying everything from the CI variables, this was a big change from the Pages configuration. But there was one thing that really bothered me: The ssh call. If you look at it, you can see that I left out the commands that are passed to `ssh`. There is a good reason: These are waaaay too long.
|
||||||
If displayed as separated shell calls, they would look something like this:
|
|
||||||
|
<details>
|
||||||
|
<summary>commands formatted as a shell script</summary>
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
screen -X -S website-firq-npx kill;
|
screen -X -S website-firq-npx kill;
|
||||||
|
@ -79,9 +80,11 @@ cd ~;
|
||||||
screen -S website-firq-npx -dm npx serve public/ -p 9000 -c serve.json"
|
screen -S website-firq-npx -dm npx serve public/ -p 9000 -c serve.json"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
With the following directory structure on the remote host, this can easily be explained:
|
With the following directory structure on the remote host, this can easily be explained:
|
||||||
|
|
||||||
```
|
```plain
|
||||||
/
|
/
|
||||||
├─ public/
|
├─ public/
|
||||||
│ ├─ site content
|
│ ├─ site content
|
||||||
|
@ -173,7 +176,8 @@ After completing, the new stage begins.
|
||||||
Since the files are now built on the GitLab server and not in the Proxmox instance, the files need to be moved over during the pipeline. This can be achieved by a great utility known as `rsync`. It can be used for sending data via ssh to any client that supports it, which makes it the ideal tool for my use-case.
|
Since the files are now built on the GitLab server and not in the Proxmox instance, the files need to be moved over during the pipeline. This can be achieved by a great utility known as `rsync`. It can be used for sending data via ssh to any client that supports it, which makes it the ideal tool for my use-case.
|
||||||
I also recommend looking <a href="https://blog.mitsunee.com/post/copy-steam-games-rsync" target="_blank" rel="noopener noreferrer">at this blog post from Mitsunee</a> detailing how to use `ssh` and `rsync` to sync game libraries to the Steam Deck with ease.
|
I also recommend looking <a href="https://blog.mitsunee.com/post/copy-steam-games-rsync" target="_blank" rel="noopener noreferrer">at this blog post from Mitsunee</a> detailing how to use `ssh` and `rsync` to sync game libraries to the Steam Deck with ease.
|
||||||
|
|
||||||
In my case, the resulting `.gitlab-ci.yml` looked like this:
|
<details>
|
||||||
|
<summary>.gitlab-ci.yml</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
deploy-site:
|
deploy-site:
|
||||||
|
@ -202,6 +206,8 @@ deploy-site:
|
||||||
- ssh $DEPLOY_USER@$DEPLOY_HOST "screen -S website-firq-npx -dm npx serve public/ -p 9000 -c serve.json"
|
- ssh $DEPLOY_USER@$DEPLOY_HOST "screen -S website-firq-npx -dm npx serve public/ -p 9000 -c serve.json"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
First, you have the rsync and ssh setup, which now also creates a new ssh-config for this. This really reduces the amount of arguments that need to be passed to ssh and rsync, making the whole flow less error-prone. My main issue with setting this up was to find out how to structure this, as I needed to setup ssh from the pipeline
|
First, you have the rsync and ssh setup, which now also creates a new ssh-config for this. This really reduces the amount of arguments that need to be passed to ssh and rsync, making the whole flow less error-prone. My main issue with setting this up was to find out how to structure this, as I needed to setup ssh from the pipeline
|
||||||
without causing any weird issues. But by using a custom ssh-key and config, it became really easy to get this to run.
|
without causing any weird issues. But by using a custom ssh-key and config, it became really easy to get this to run.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'FGO TA - How, what, why'
|
title: 'FGO TA - How, what, why'
|
||||||
pubDate: 2023-03-16
|
pubDate: 2023-03-16
|
||||||
description: 'What FGO TA is, what brought me to it and how you can do it yourself!'
|
description: 'What FGO TA is, what brought me to it and how you can do it yourself!'
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'Hello World!'
|
title: 'Hello World!'
|
||||||
pubDate: 2023-03-08
|
pubDate: 2023-03-08
|
||||||
description: 'First blog post, talking a bit about the site, the technologies behind it and the future ideas'
|
description: 'First blog post, talking a bit about the site, the technologies behind it and the future ideas'
|
|
@ -1,11 +1,12 @@
|
||||||
---
|
---
|
||||||
layout: ../../layouts/blogPost.astro
|
|
||||||
title: 'FGO Mechanics: Instant Death'
|
title: 'FGO Mechanics: Instant Death'
|
||||||
pubDate: 2023-08-19
|
pubDate: 2023-08-19
|
||||||
description: 'Blog post talking about instant death in FGO, how you can take advantage of it and what its limits are.'
|
description: 'Blog post talking about instant death in FGO and how you can take advantage.'
|
||||||
author: 'Firq'
|
author: 'Firq'
|
||||||
tags: ['fgo', 'games']
|
tags: ['fgo', 'games']
|
||||||
---
|
---
|
||||||
|
import thumbnail from "@assets/thumbnails/UwbNp_dB_VU.jpg"
|
||||||
|
import YoutubeEmbed from "@components/youtubeEmbed.astro"
|
||||||
|
|
||||||
> **Disclaimer**<br/>
|
> **Disclaimer**<br/>
|
||||||
> While writing this, Requiem and I faced a bit of a challenge concerning death rate calculations. Case in point is the passive "Item Construction"
|
> While writing this, Requiem and I faced a bit of a challenge concerning death rate calculations. Case in point is the passive "Item Construction"
|
||||||
|
@ -16,10 +17,10 @@ it doesn't interact with the hitrate, but if you have proof that this is differe
|
||||||
|
|
||||||
Instant Kill & Instant Death: One of the most rejected systems to exist in FGO. Most players know of it, but discard it because of its unreliability and uselessness against stronger enemies. In this article, I want to dive a bit into the workings of Instant Kill and help you understand how you can, in specific scenarios, use it to your advantage.
|
Instant Kill & Instant Death: One of the most rejected systems to exist in FGO. Most players know of it, but discard it because of its unreliability and uselessness against stronger enemies. In this article, I want to dive a bit into the workings of Instant Kill and help you understand how you can, in specific scenarios, use it to your advantage.
|
||||||
|
|
||||||
There are a multitude of servants that have access to instant-kill, but I will only highlight a few here (You can view the full list on <a href="https://gamepress.gg/grandorder/instant-death" target="_blank" rel="noopener noreferrer" style="font-style: unset">Gamepress</a>)
|
There are a multitude of servants that have access to instant-kill, but I will only highlight a few here (You can view the full list on <a href="https://gamepress.gg/grandorder/instant-death" target="_blank" rel="noopener noreferrer" >Gamepress</a>)
|
||||||
|
|
||||||
- **Nitocris (Caster)**: The most prominent figure, as she has easy access to her instant-killing NP and a skill that increases her death hit rate
|
- **Nitocris (Caster)**: The most prominent figure, as she has easy access to her instant-killing NP and a skill that increases her death hit rate
|
||||||
- **Void Shiki (Saber)**: Another Example of an AOE Instant-Kill servant who also has access to Death Resist debuffs. One quest in her release event even specifically used the Instant Kill mechanic (<a href="https://apps.atlasacademy.io/db/NA/quest/94021308/1" target="_blank" rel="noopener noreferrer" style="font-style: unset">Quest Link here</a>)
|
- **Void Shiki (Saber)**: Another Example of an AOE Instant-Kill servant who also has access to Death Resist debuffs. One quest in her release event even specifically used the Instant Kill mechanic (<a href="https://apps.atlasacademy.io/db/NA/quest/94021308/1" target="_blank" rel="noopener noreferrer" >Quest Link here</a>)
|
||||||
- **King Hassan (Assassin)**: The only servant that can afflict enemies with instant death on normal attacks (Note: David can do this as well, but only if he has his Bond CE equipped)
|
- **King Hassan (Assassin)**: The only servant that can afflict enemies with instant death on normal attacks (Note: David can do this as well, but only if he has his Bond CE equipped)
|
||||||
|
|
||||||
But since Instant Kill is based on probability, most players are relucatant to use it in their farming compositions. This thems from the different death rates enemies have:
|
But since Instant Kill is based on probability, most players are relucatant to use it in their farming compositions. This thems from the different death rates enemies have:
|
||||||
|
@ -51,10 +52,10 @@ The parameters here are the following:
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
The main reason this came up was because of a farming comp used in <a href="https://apps.atlasacademy.io/db/NA/quest/94061820/1" target="_blank" rel="noopener noreferrer" style="font-style: unset">Hunting Quest XI - Day 5 - Pride+</a>. The node is structured
|
The main reason this came up was because of a farming comp used in <a href="https://apps.atlasacademy.io/db/NA/quest/94061820/1" target="_blank" rel="noopener noreferrer" >Hunting Quest XI - Day 5 - Pride+</a>. The node is structured
|
||||||
into a 2 - 1 - 1 Layout, meaning using a dual-DPS setup would be the way to go.
|
into a 2 - 1 - 1 Layout, meaning using a dual-DPS setup would be the way to go.
|
||||||
|
|
||||||
Looking at the stats for the first wave, it made sense to use Nitocris: Both Ghouls have a 100% Death Rate <a href="https://apps.atlasacademy.io/db/NA/enemy/9940700" target="_blank" rel="noopener noreferrer" style="font-style: unset">according to Atlas Academy</a>.
|
Looking at the stats for the first wave, it made sense to use Nitocris: Both Ghouls have a 100% Death Rate <a href="https://apps.atlasacademy.io/db/NA/enemy/9940700" target="_blank" rel="noopener noreferrer" >according to Atlas Academy</a>.
|
||||||
|
|
||||||
I went with using Nitocris, since she has a 100% NP charge and a skill that increases her Instant Death rate by 100%. Not needing any additional charge meant that no other buffs or AOE charges needed to be used on turn 1.
|
I went with using Nitocris, since she has a 100% NP charge and a skill that increases her Instant Death rate by 100%. Not needing any additional charge meant that no other buffs or AOE charges needed to be used on turn 1.
|
||||||
|
|
||||||
|
@ -103,9 +104,8 @@ For other skill levels, this looks like this (_Her S1 scales from 50% to 100%_)
|
||||||
|
|
||||||
In the end, this resulted in the following comp that worked like a charm.
|
In the end, this resulted in the following comp that worked like a charm.
|
||||||
|
|
||||||
<div style="width: 100%; display: flex; justify-content: center; padding-top: 0.75rem">
|
<YoutubeEmbed id="UwbNp_dB_VU" thumbnail={thumbnail.src}/>
|
||||||
<iframe width="640" height="360" src="https://www.youtube-nocookie.com/embed/UwbNp_dB_VU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen loading="lazy"></iframe>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
237
src/data/blog/unlighthouse-ci.md
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
---
|
||||||
|
title: 'Unlighthouse - Easy Website Testing'
|
||||||
|
pubDate: 2025-04-12
|
||||||
|
description: 'How I am automatically testing my site deployments using unlighthouse'
|
||||||
|
author: 'Firq'
|
||||||
|
tags: [ 'unlighthouse', 'coding' ]
|
||||||
|
---
|
||||||
|
|
||||||
|
Since one of the first iterations of this site, I was using Google Lighthouse to benchmark my results - and it really paid off at times. Missing ARIA roles, bad color contrast, the fact that the default Youtube embed is actually not that great - all of this was exposed by Lighthouse over time.
|
||||||
|
|
||||||
|
How this whole thing evolved and how I am currently using Unlighthouse to make all of this run even smoother will be covered in this article.
|
||||||
|
|
||||||
|
## Unlighthouse
|
||||||
|
|
||||||
|
First of all, I want to give a big shout-out to <a href="https://harlanzw.com/" target="_blank" rel="noopener noreferrer" >Harlan Wilton (harlan-zw)</a> for making `unlighthouse` - it makes analyzing larges sites so much easier. The package can be installed from `npm` and enables you to easily run Lighthouse for each of your subpages - even from a CI environment. This is a huge improvement given that the best alternative is the `Lighthouse` docker container that is provided by GitLab.
|
||||||
|
|
||||||
|
For ease of use, I also build my own version of that GitLab docker container, but with unlighthouse instead. This allows me to easily use it in other CI jobs, with pinned dependencies and resistance against random changes. However, it also requires me to regularly check for updated chromium versions, as this is usually what causes issues when updating the container.
|
||||||
|
|
||||||
|
The container is <a href="https://forgejo.neshweb.net/CI-Docker-Images/unlighthouse" target="_blank" rel="noopener noreferrer" >available from here</a> and can be pulled with the following url:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker pull forgejo.neshweb.net/ci-docker-images/unlighthouse:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Dockerfile for the unlighthouse container</summary>
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM node:22.14-bookworm
|
||||||
|
|
||||||
|
LABEL authorname="firq"
|
||||||
|
LABEL description="unlighthouse container for ci-based lighthouse testing"
|
||||||
|
WORKDIR /unlighthouse
|
||||||
|
|
||||||
|
ENV CHROMIUM_VERSION="135.0.7049.84-1~deb12u1"
|
||||||
|
ENV UNLIGHTHOUSE_VERSION="0.16.3"
|
||||||
|
ENV NODE_ENV='production'
|
||||||
|
|
||||||
|
# Update path so executable can be run globally
|
||||||
|
ENV PATH="/unlighthouse/node_modules/.bin:${PATH}"
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get -y install --no-install-recommends chromium=${CHROMIUM_VERSION} procps && rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN npm install @unlighthouse/cli@${UNLIGHTHOUSE_VERSION} puppeteer
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Automation with Forgejo
|
||||||
|
|
||||||
|
However, running `unlighthouse` manually after each page build is too much of a hassle - so it was time to automate it. For that reason, I created a corresponding actions job that would run the checks each time a preview version of the site was being built.
|
||||||
|
|
||||||
|
In the beginning, I would run the test against the actual deployed instance, but with time it became increasingly obvious that this would be inefficient, mainly because I would run this using two different tags (`x.x.x-pre.x` and `x.x.x-ulh.x`) to trigger the corresponding CI runs.
|
||||||
|
|
||||||
|
Regarding this, I decided to instead run everything using a service container. This means that the `unlighthouse` job runs in the main job task, while the site is running as a service container in parallel. This allows access to the container using an alias (`website`) instead of a URL or IP address. (Note: The yaml is a bit reduced, meaning lines that only provide visual context in the UI are removed).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
unlighthouse:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: forgejo.neshweb.net/ci-docker-images/unlighthouse:0.16.3
|
||||||
|
services:
|
||||||
|
website:
|
||||||
|
image: forgejo.neshweb.net/firq/firq-dev-website:${{ inputs.containertag }}
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- run: |
|
||||||
|
while [ "$(curl -o /dev/null -s -w '%{http_code}' http://website:8081)" -ne 200 ];
|
||||||
|
do echo "Waiting...";
|
||||||
|
sleep 5;
|
||||||
|
done;
|
||||||
|
- run: unlighthouse-ci --site "http://website:8081"
|
||||||
|
- run: find ./unlighthouse-reports -type f | xargs sed -i "s|http://website:8081|https://preview.firq.dev|g";
|
||||||
|
```
|
||||||
|
|
||||||
|
So what is happening here? The first few lines are just there for configuring the runner and starting up the service-container. The container tag is passed from the upstream pipeline, making this setup a lot more flexible. Afterwards, two things happen: The code in the git repo gets checked out, and the job gets put on hold until the service-container is available. This is necessary, as sometimes the service container takes a few seconds to start up.
|
||||||
|
|
||||||
|
Afterwards, `unlighthouse` runs against the given site inside the service-container, using the local config for any other settings. After the run concludes, the artifacts inside the `unlighthouse-reports` folder get search-and-replaced once to change the url from the service-container to the preview site. This is mainly for visual consistency, but also has the benefit of making the "Check with PageSpeedInsights" button work.
|
||||||
|
|
||||||
|
To enable this to work more fluently, I decided to run the `unlighthouse` CI as a downstream pipeline, which was triggered by one of the CI jobs after the `build-and-push` step concludes. This also allows me to easily pass the new container name to the downstream pipeline for usage.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
run-unlighthouse:
|
||||||
|
needs: [ build-site ]
|
||||||
|
if: success()
|
||||||
|
runs-on: docker
|
||||||
|
steps:
|
||||||
|
- name: Launch workflow
|
||||||
|
run: |
|
||||||
|
payload="{\"ref\": \"${GITHUB_REF_NAME}\", \"inputs\": { \"containertag\": \"${GITHUB_REF_NAME}\" }}"
|
||||||
|
curl -X "POST" \
|
||||||
|
-H "accept: application/json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-d "${payload}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/workflows/unlighthouse.yml/dispatches" -v
|
||||||
|
```
|
||||||
|
|
||||||
|
And with that, the general CI setup concludes. I won't go into too much detail about how the deployment of the reports works, as it is similar to the main site. If you are interested, see <a href="https://forgejo.neshweb.net/Firq/firq-dev-website/src/branch/dev" target="_blank" rel="noopener noreferrer" >the repository linked here</a> for further details.
|
||||||
|
|
||||||
|
## Fine-tuning the config
|
||||||
|
|
||||||
|
After setting everything up, it took some good time until I managed to fine-tune the configuration that unlighthouse uses. Don't get me wrong, running with the defaults would probably also work, but from my experience the defaults are set so strict that a perfect score is unobtainable - even if for example <a href="https://pagespeed.web.dev/" target="_blank" rel="noopener noreferrer" >PageSpeedInsights</a> doesn't see any issues.
|
||||||
|
|
||||||
|
My strategy for the config was to copy the settings that PageSpeedInsights uses, as this seemed like a good baseline for testing. This means that the Lighthouse settings look like this:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
lighthouseOptions: {
|
||||||
|
throttlingMethod: 'devtools',
|
||||||
|
throttling: {
|
||||||
|
cpuSlowdownMultiplier: 4,
|
||||||
|
requestLatencyMs: 150,
|
||||||
|
downloadThroughputKbps: 1638.4,
|
||||||
|
uploadThroughputKbps: 1638.4,
|
||||||
|
},
|
||||||
|
screenEmulation: { width: 412, height: 823, deviceScaleFactor: 1.75 },
|
||||||
|
skipAudits: [ 'is-on-https', 'redirects-http', 'uses-http2' ],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As mentioned previously, as I am using a docker network, I can't easily use HTTPS and HTTP/2. Given that, I decided to disable the corresponding audits, as I know for a fact that this always works for the real deployment.
|
||||||
|
|
||||||
|
The other configuration steps are for Puppeteer, which handles the simulation of the site in a CI environment. This is really straightforward:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
puppeteerOptions: {
|
||||||
|
args: [ '--no-sandbox', '--disable-setuid-sandbox' ]
|
||||||
|
},
|
||||||
|
puppeteerClusterOptions: {
|
||||||
|
maxConcurrency: 1
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
`maxConcurrency` ensures that there is only one inspection task running at the time, as having multiple run in parallel results in some weird situations where the report fails to correctly generate.
|
||||||
|
|
||||||
|
The rest of the configuration is for `unlighthouse` itself, regarding both the scanner and the ci environment, as well as the configuration of outputs and sites to check
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
ci: {
|
||||||
|
budget: 50,
|
||||||
|
buildStatic: true,
|
||||||
|
},
|
||||||
|
scanner: {
|
||||||
|
sitemap: true,
|
||||||
|
dynamicSampling: false,
|
||||||
|
samples: 3,
|
||||||
|
},
|
||||||
|
outputPath: 'unlighthouse-reports',
|
||||||
|
cache: true,
|
||||||
|
urls
|
||||||
|
```
|
||||||
|
|
||||||
|
However, there is one interesting entry there: `urls`. This is a dynamic list of URLs generated from the sitemap, which happens earlier in the config (what a blessing `export default async` config-files are). This is necessary, as scraping the site from the sitemap directly is not working in my environment (the crawler finds `https://preview.firq.dev`, while I need `http://website:8081` for it to run). After some digging, I raised that usecase with Harlan, who proceeded to enable me to do exactly that in no time (thank you again for doing this - <a href="https://github.com/harlan-zw/unlighthouse/issues/248" target="_blank" rel="noopener noreferrer" >see issue 248 here</a>)
|
||||||
|
|
||||||
|
This is where the magic snippet comes in, which 1. fetches the sitemap, 2. replaces the URLs and 3. fetches each of the URLs once to warm up the `serve` webserver to ensure that the server-caching correctly works (improves the performance by a lot).
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const sitemap = await (await fetch('http://website:8081/sitemap-0.xml')).text();
|
||||||
|
|
||||||
|
const urls = sitemap.match(/<loc>(.*?)<\/loc>/g)!.map(
|
||||||
|
(loc) => loc.replace(/<\/?loc>/g, '').replace(/https:\/\/firq.dev/g, 'http://website:8081')
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const url of urls) { await fetch(url) };
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards, `urls` can be used in the config to provide `unlighthouse` with a list of urls to check - already replaced and modified to work with the docker network.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>View the whole config here</summary>
|
||||||
|
|
||||||
|
(It's also really nice that unlighthouse provides type hints for the config, makes figuring out where goes what a lot easier).
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { UserConfig } from 'unlighthouse'
|
||||||
|
|
||||||
|
export default async (): Promise<UserConfig> => {
|
||||||
|
/* fetch sitemap from debug container */
|
||||||
|
const sitemap = await (await fetch('http://website:8081/sitemap-0.xml')).text();
|
||||||
|
|
||||||
|
/* format URLs to work with debug container */
|
||||||
|
const urls = sitemap.match(/<loc>(.*?)<\/loc>/g)!.map(
|
||||||
|
(loc) => loc.replace(/<\/?loc>/g, '').replace(/https:\/\/firq.dev/g, 'http://website:8081')
|
||||||
|
);
|
||||||
|
|
||||||
|
/* ensure serve is already "warm", preventing startup lag that reduces performance */
|
||||||
|
for (const url of urls) { await fetch(url) };
|
||||||
|
|
||||||
|
/* actual config */
|
||||||
|
return {
|
||||||
|
lighthouseOptions: {
|
||||||
|
throttlingMethod: 'devtools',
|
||||||
|
throttling: {
|
||||||
|
cpuSlowdownMultiplier: 4,
|
||||||
|
requestLatencyMs: 150,
|
||||||
|
downloadThroughputKbps: 1638.4,
|
||||||
|
uploadThroughputKbps: 1638.4,
|
||||||
|
},
|
||||||
|
screenEmulation: {
|
||||||
|
width: 412,
|
||||||
|
height: 823,
|
||||||
|
deviceScaleFactor: 1.75,
|
||||||
|
},
|
||||||
|
skipAudits: [ 'is-on-https', 'redirects-http', 'uses-http2' ],
|
||||||
|
},
|
||||||
|
puppeteerOptions: {
|
||||||
|
args: [ '--no-sandbox', '--disable-setuid-sandbox' ],
|
||||||
|
},
|
||||||
|
puppeteerClusterOptions: {
|
||||||
|
maxConcurrency: 1
|
||||||
|
},
|
||||||
|
ci: {
|
||||||
|
budget: 50,
|
||||||
|
buildStatic: true,
|
||||||
|
},
|
||||||
|
scanner: {
|
||||||
|
sitemap: true,
|
||||||
|
dynamicSampling: false,
|
||||||
|
samples: 3,
|
||||||
|
},
|
||||||
|
outputPath: 'unlighthouse-reports',
|
||||||
|
cache: true,
|
||||||
|
urls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## What's next?
|
||||||
|
|
||||||
|
After setting this whole thing up over the course of multiple months, with a variety of issues and shortcomings along the way, I hope that this is now ready for good.
|
||||||
|
I will, however, write a Forgejo Action for me to reuse in the future, as this would enable me to easily test other sites with the same concept.
|
||||||
|
|
||||||
|
If you want to check out the whole thing in action, check out <a href="https://forgejo.neshweb.net/Firq/firq-dev-website/src/branch/dev" target="_blank" rel="noopener noreferrer" >the website repository here </a>. In addition, you can find the reports that get generated <a href="https://unlighthouse.firq.dev" target="_blank" rel="noopener noreferrer" >here at unlighthouse.firq.dev</a>
|
||||||
|
|
||||||
|
Anyway, I wish I gave you an interesting insight in how Unlighthouse ensures good site quality - and how YOU can also profit from website testing in your CI.
|
|
@ -2,56 +2,56 @@
|
||||||
{
|
{
|
||||||
"name": "The Merciless One",
|
"name": "The Merciless One",
|
||||||
"imageFile": "merciless_one.png",
|
"imageFile": "merciless_one.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Ox-Demon King",
|
"name": "Ox-Demon King",
|
||||||
"imageFile": "ox-demon.png",
|
"imageFile": "ox-demon.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Holy Maiden's Teachings",
|
"name": "Holy Maiden's Teachings",
|
||||||
"imageFile": "maidens_teachings.png",
|
"imageFile": "maidens_teachings.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Kaleidoscope",
|
"name": "Kaleidoscope",
|
||||||
"imageFile": "kaleidoscope.png",
|
"imageFile": "kaleidoscope.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "The Black Grail",
|
"name": "The Black Grail",
|
||||||
"imageFile": "black_grail.png",
|
"imageFile": "black_grail.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "A Fragment Of 2030",
|
"name": "A Fragment Of 2030",
|
||||||
"imageFile": "2030.png",
|
"imageFile": "2030.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Outrage",
|
"name": "Outrage",
|
||||||
"imageFile": "outrage.png",
|
"imageFile": "outrage.png",
|
||||||
"mlb": "false"
|
"mlb": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Vessel of the Saint",
|
"name": "Vessel of the Saint",
|
||||||
"imageFile": "vessel.png",
|
"imageFile": "vessel.png",
|
||||||
"mlb": "false"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Devilish Bodhisattva",
|
"name": "Devilish Bodhisattva",
|
||||||
"imageFile": "devilish_bodhisattva.png",
|
"imageFile": "devilish_bodhisattva.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "500-Year Obsession",
|
"name": "500-Year Obsession",
|
||||||
"imageFile": "500.png",
|
"imageFile": "500.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Prisma Cosmos",
|
"name": "Prisma Cosmos",
|
||||||
"imageFile": "prisma_cosmos.png",
|
"imageFile": "prisma_cosmos.png",
|
||||||
"mlb": "true"
|
"mlb": true
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -10,5 +10,17 @@
|
||||||
"origin": "Fate/Grand Order",
|
"origin": "Fate/Grand Order",
|
||||||
"imageFile": "skadi.png",
|
"imageFile": "skadi.png",
|
||||||
"link": "https://twitter.com/firq_ow/status/1544321985995489280"
|
"link": "https://twitter.com/firq_ow/status/1544321985995489280"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Summer Skadi",
|
||||||
|
"origin": "Fate/Grand Order",
|
||||||
|
"imageFile": "summerskadi.png",
|
||||||
|
"link": "https://youtu.be/qmR4s3DHZXw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Summer Scathach",
|
||||||
|
"origin": "Fate/Grand Order",
|
||||||
|
"imageFile": "summerscathach.png",
|
||||||
|
"link": "https://youtu.be/k0E3kgx3eMA"
|
||||||
}
|
}
|
||||||
]
|
]
|
27
src/data/datafiles/navdata.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"link": "/",
|
||||||
|
"text": "Home",
|
||||||
|
"icon": "home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "/fgo",
|
||||||
|
"text": "FGO",
|
||||||
|
"icon": "fgo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "/projects",
|
||||||
|
"text": "Projects",
|
||||||
|
"icon": "projects"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "/blog",
|
||||||
|
"text": "Blog",
|
||||||
|
"icon": "blog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "/about",
|
||||||
|
"text": "About",
|
||||||
|
"icon": "about"
|
||||||
|
}
|
||||||
|
]
|
27
src/data/datafiles/projectdata.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "skyeweave",
|
||||||
|
"description": "A small sideproject for a Python package to easily generate expressions from spritesheets for FGO. Works using the AtlasAcademy API",
|
||||||
|
"url": "https://forgejo.neshweb.net/Firq/skyeweave"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dockge-cli",
|
||||||
|
"description": "A Python CLI script for controlling the docker tool dockge, both by a user and from a CI (uni project about websockets)",
|
||||||
|
"url": "https://forgejo.neshweb.net/Firq/dockge-cli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fgo-ta.com",
|
||||||
|
"description": "A site for archiving and organizing TA runs completed by the FGO commuity. Still WIP, with a preview available @ preview.fgo-ta.com",
|
||||||
|
"url": "https://fgo-ta.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "this website",
|
||||||
|
"description": "My first real webdev project, born from a hand-written HTML page on GitLab Pages - feel free to read the related blog posts",
|
||||||
|
"url": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "my personal blog",
|
||||||
|
"description": "My own small blog - I'm not posting that much, but trying to share my experience in developing software and playing FGO.",
|
||||||
|
"url": "/blog"
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,19 +1,49 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"name": "Scathach",
|
||||||
|
"level": "120",
|
||||||
|
"np": "5",
|
||||||
|
"skills": "10 / 10 / 10",
|
||||||
|
"ml": "10",
|
||||||
|
"servantImageFile": "scathach_03.png",
|
||||||
|
"bondceImageFile": "scathach.png",
|
||||||
|
"bond10": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Summer Skadi",
|
||||||
|
"level": "120",
|
||||||
|
"np": "5",
|
||||||
|
"skills": "10 / 10 / 10",
|
||||||
|
"ml": "10",
|
||||||
|
"servantImageFile": "summerskadi_03.png",
|
||||||
|
"bondceImageFile": "summerskadi.png",
|
||||||
|
"bond10": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Skadi",
|
"name": "Skadi",
|
||||||
"level": "120",
|
"level": "120",
|
||||||
"np": "5",
|
"np": "5",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "10",
|
"ml": "10",
|
||||||
"servantImageFile": "skadi_03.png",
|
"servantImageFile": "skadi_03.png",
|
||||||
"bondceImageFile": "skadi.png",
|
"bondceImageFile": "skadi.png",
|
||||||
"bond10": true
|
"bond10": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Summer Skadi",
|
||||||
|
"level": "70",
|
||||||
|
"np": "1",
|
||||||
|
"skills": "10 / 10 / 10",
|
||||||
|
"ml": "1",
|
||||||
|
"servantImageFile": "summerskadi_02.png",
|
||||||
|
"bondceImageFile": "summerskadi.png",
|
||||||
|
"bond10": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Skadi",
|
"name": "Skadi",
|
||||||
"level": "70",
|
"level": "70",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "1",
|
"ml": "1",
|
||||||
"servantImageFile": "skadi_02.png",
|
"servantImageFile": "skadi_02.png",
|
||||||
"bondceImageFile": "skadi.png",
|
"bondceImageFile": "skadi.png",
|
||||||
|
@ -23,7 +53,7 @@
|
||||||
"name": "Skadi",
|
"name": "Skadi",
|
||||||
"level": "1",
|
"level": "1",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / X / X ",
|
"skills": "10 / X / X",
|
||||||
"ml": "1",
|
"ml": "1",
|
||||||
"servantImageFile": "skadi_01.png",
|
"servantImageFile": "skadi_01.png",
|
||||||
"bondceImageFile": "skadi.png",
|
"bondceImageFile": "skadi.png",
|
||||||
|
@ -33,18 +63,28 @@
|
||||||
"name": "Koyanskaya of Light",
|
"name": "Koyanskaya of Light",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "koyanlight_01.png",
|
"servantImageFile": "koyanlight_01.png",
|
||||||
"bondceImageFile": "koyanlight.png",
|
"bondceImageFile": "koyanlight.png",
|
||||||
"bond10": false
|
"bond10": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Koyanskaya of Light",
|
||||||
|
"level": "70",
|
||||||
|
"np": "1",
|
||||||
|
"skills": "10 / 10 / 10",
|
||||||
|
"ml": "Not Unlocked",
|
||||||
|
"servantImageFile": "koyanlight_02.png",
|
||||||
|
"bondceImageFile": "koyanlight.png",
|
||||||
|
"bond10": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Castoria",
|
"name": "Castoria",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "7",
|
"ml": "10",
|
||||||
"servantImageFile": "castoria_02.png",
|
"servantImageFile": "castoria_02.png",
|
||||||
"bondceImageFile": "castoria.png",
|
"bondceImageFile": "castoria.png",
|
||||||
"bond10": true
|
"bond10": true
|
||||||
|
@ -53,7 +93,7 @@
|
||||||
"name": "Castoria",
|
"name": "Castoria",
|
||||||
"level": "70",
|
"level": "70",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "1",
|
"ml": "1",
|
||||||
"servantImageFile": "castoria_01.png",
|
"servantImageFile": "castoria_01.png",
|
||||||
"bondceImageFile": "castoria.png",
|
"bondceImageFile": "castoria.png",
|
||||||
|
@ -63,17 +103,17 @@
|
||||||
"name": "Oberon",
|
"name": "Oberon",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "1",
|
||||||
"servantImageFile": "oberon_02.png",
|
"servantImageFile": "oberon_02.png",
|
||||||
"bondceImageFile": "oberon.png",
|
"bondceImageFile": "oberon.png",
|
||||||
"bond10": false
|
"bond10": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Merlin",
|
"name": "Merlin",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "merlin_03.png",
|
"servantImageFile": "merlin_03.png",
|
||||||
"bondceImageFile": "merlin.png",
|
"bondceImageFile": "merlin.png",
|
||||||
|
@ -83,7 +123,7 @@
|
||||||
"name": "Merlin",
|
"name": "Merlin",
|
||||||
"level": "70",
|
"level": "70",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "9 / 1 / 9 ",
|
"skills": "9 / 1 / 9",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "merlin_01.png",
|
"servantImageFile": "merlin_01.png",
|
||||||
"bondceImageFile": "merlin.png",
|
"bondceImageFile": "merlin.png",
|
||||||
|
@ -93,7 +133,7 @@
|
||||||
"name": "Waver",
|
"name": "Waver",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "waver_02.png",
|
"servantImageFile": "waver_02.png",
|
||||||
"bondceImageFile": "waver.png",
|
"bondceImageFile": "waver.png",
|
||||||
|
@ -103,7 +143,7 @@
|
||||||
"name": "Reines",
|
"name": "Reines",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "9",
|
"ml": "9",
|
||||||
"servantImageFile": "reines_01.png",
|
"servantImageFile": "reines_01.png",
|
||||||
"bondceImageFile": "reines.png",
|
"bondceImageFile": "reines.png",
|
||||||
|
@ -113,7 +153,7 @@
|
||||||
"name": "Tamamo-no-Mae",
|
"name": "Tamamo-no-Mae",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "2",
|
"np": "2",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "tamamo_03.png",
|
"servantImageFile": "tamamo_03.png",
|
||||||
"bondceImageFile": "tamamo.png",
|
"bondceImageFile": "tamamo.png",
|
||||||
|
@ -123,7 +163,7 @@
|
||||||
"name": "Van Gogh",
|
"name": "Van Gogh",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "10",
|
"ml": "10",
|
||||||
"servantImageFile": "gogh_01.png",
|
"servantImageFile": "gogh_01.png",
|
||||||
"bondceImageFile": "gogh.png",
|
"bondceImageFile": "gogh.png",
|
||||||
|
@ -133,7 +173,7 @@
|
||||||
"name": "Miss Crane",
|
"name": "Miss Crane",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 10 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "crane_01.png",
|
"servantImageFile": "crane_01.png",
|
||||||
"bondceImageFile": "crane.png",
|
"bondceImageFile": "crane.png",
|
||||||
|
@ -143,37 +183,57 @@
|
||||||
"name": "Ashiya Douman",
|
"name": "Ashiya Douman",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "9 / 9 / 10 ",
|
"skills": "9 / 9 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "10",
|
||||||
"servantImageFile": "douman_01.png",
|
"servantImageFile": "douman_01.png",
|
||||||
"bondceImageFile": "douman.png",
|
"bondceImageFile": "douman.png",
|
||||||
"bond10": false
|
"bond10": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Himiko",
|
"name": "Himiko",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "9 / 9 / 9 ",
|
"skills": "9 / 9 / 9",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "himiko_03.png",
|
"servantImageFile": "himiko_03.png",
|
||||||
"bondceImageFile": "himiko.png",
|
"bondceImageFile": "himiko.png",
|
||||||
"bond10": false
|
"bond10": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Sherlock Holmes",
|
||||||
|
"level": "90",
|
||||||
|
"np": "1",
|
||||||
|
"skills": "9 / 10 / 9",
|
||||||
|
"ml": "Not Unlocked",
|
||||||
|
"servantImageFile": "sherlock_03.png",
|
||||||
|
"bondceImageFile": "sherlock.png",
|
||||||
|
"bond10": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Nero (Bride)",
|
"name": "Nero (Bride)",
|
||||||
"level": "90",
|
"level": "90",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "10 / 10 / 9 ",
|
"skills": "10 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "bride_03.png",
|
"servantImageFile": "bride_03.png",
|
||||||
"bondceImageFile": "bride.png",
|
"bondceImageFile": "bride.png",
|
||||||
"bond10": false
|
"bond10": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Taigong Wang",
|
||||||
|
"level": "70",
|
||||||
|
"np": "1",
|
||||||
|
"skills": "10 / 9 / 10",
|
||||||
|
"ml": "Not Unlocked",
|
||||||
|
"servantImageFile": "taigong_03.png",
|
||||||
|
"bondceImageFile": "taigong.png",
|
||||||
|
"bond10": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Nightingale (Santa)",
|
"name": "Nightingale (Santa)",
|
||||||
"level": "80",
|
"level": "80",
|
||||||
"np": "5",
|
"np": "5",
|
||||||
"skills": "9 / 10 / 10 ",
|
"skills": "9 / 10 / 9",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "santagale_01.png",
|
"servantImageFile": "santagale_01.png",
|
||||||
"bondceImageFile": "santagale.png",
|
"bondceImageFile": "santagale.png",
|
||||||
|
@ -183,7 +243,7 @@
|
||||||
"name": "Nightingale (Santa)",
|
"name": "Nightingale (Santa)",
|
||||||
"level": "60",
|
"level": "60",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "1 / 10 / 10 ",
|
"skills": "1 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "santagale_01.png",
|
"servantImageFile": "santagale_01.png",
|
||||||
"bondceImageFile": "santagale.png",
|
"bondceImageFile": "santagale.png",
|
||||||
|
@ -193,7 +253,17 @@
|
||||||
"name": "Elisabeth Báthory",
|
"name": "Elisabeth Báthory",
|
||||||
"level": "40",
|
"level": "40",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "9 / 9 / X ",
|
"skills": "9 / 9 / X",
|
||||||
|
"ml": "Not Unlocked",
|
||||||
|
"servantImageFile": "liz_02.png",
|
||||||
|
"bondceImageFile": "liz.png",
|
||||||
|
"bond10": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Elisabeth Báthory",
|
||||||
|
"level": "1",
|
||||||
|
"np": "1",
|
||||||
|
"skills": "9 / X / X",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "liz_01.png",
|
"servantImageFile": "liz_01.png",
|
||||||
"bondceImageFile": "liz.png",
|
"bondceImageFile": "liz.png",
|
||||||
|
@ -203,7 +273,7 @@
|
||||||
"name": "Wu Zetian",
|
"name": "Wu Zetian",
|
||||||
"level": "60",
|
"level": "60",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "9 / 1 / 9 ",
|
"skills": "10 / 9 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "wu_01.png",
|
"servantImageFile": "wu_01.png",
|
||||||
"bondceImageFile": "wu.png",
|
"bondceImageFile": "wu.png",
|
||||||
|
@ -213,7 +283,7 @@
|
||||||
"name": "Ibaraki-Douji",
|
"name": "Ibaraki-Douji",
|
||||||
"level": "40",
|
"level": "40",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "8 / 8 / X ",
|
"skills": "9 / 10 / X ",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "ibaraki_02.png",
|
"servantImageFile": "ibaraki_02.png",
|
||||||
"bondceImageFile": "ibaraki.png",
|
"bondceImageFile": "ibaraki.png",
|
||||||
|
@ -223,7 +293,7 @@
|
||||||
"name": "Calamity Jane",
|
"name": "Calamity Jane",
|
||||||
"level": "60",
|
"level": "60",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "1 / 9 / 9 ",
|
"skills": "1 / 9 / 9",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "jane_02.png",
|
"servantImageFile": "jane_02.png",
|
||||||
"bondceImageFile": "jane.png",
|
"bondceImageFile": "jane.png",
|
||||||
|
@ -233,7 +303,7 @@
|
||||||
"name": "Chiron",
|
"name": "Chiron",
|
||||||
"level": "60",
|
"level": "60",
|
||||||
"np": "1",
|
"np": "1",
|
||||||
"skills": "1 / 9 / 9 ",
|
"skills": "1 / 10 / 10",
|
||||||
"ml": "Not Unlocked",
|
"ml": "Not Unlocked",
|
||||||
"servantImageFile": "chiron_01.png",
|
"servantImageFile": "chiron_01.png",
|
||||||
"bondceImageFile": "chiron.png",
|
"bondceImageFile": "chiron.png",
|
42
src/data/datafiles/socials.json
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"link": "https://www.youtube.com/@Firq_",
|
||||||
|
"icon": "youtube",
|
||||||
|
"text": "Youtube"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://mastodon.neshweb.net/@Firq",
|
||||||
|
"icon": "mastodon",
|
||||||
|
"text": "Mastodon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://bsky.app/profile/firq.dev",
|
||||||
|
"icon": "bluesky",
|
||||||
|
"text": "Bluesky"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://twitch.tv/Firq",
|
||||||
|
"icon": "twitch",
|
||||||
|
"text": "Twitch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://discord.com/users/186014064835690496",
|
||||||
|
"icon": "discord",
|
||||||
|
"text": "Discord"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://www.reddit.com/user/Firq_ow/",
|
||||||
|
"icon": "reddit",
|
||||||
|
"text": "Reddit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://forgejo.neshweb.net/Firq",
|
||||||
|
"icon": "forgejo",
|
||||||
|
"text": "Forgejo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "https://rayshift.io/na/firq",
|
||||||
|
"icon": "rayshift",
|
||||||
|
"text": "Rayshift"
|
||||||
|
}
|
||||||
|
]
|
|
@ -310,5 +310,21 @@
|
||||||
"date": "2023-12-16",
|
"date": "2023-12-16",
|
||||||
"servantImageFile": "shishou.png",
|
"servantImageFile": "shishou.png",
|
||||||
"turns": "3T"
|
"turns": "3T"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tunguska - Beast of Taming 4T (FLO)",
|
||||||
|
"link": "https://www.youtube.com/watch?v=g6aASJeL-ao",
|
||||||
|
"targetImageFile": "koyanskayaofdark.png",
|
||||||
|
"date": "2023-12-26",
|
||||||
|
"servantImageFile": "shishou.png",
|
||||||
|
"turns": "4T"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tunguska - Beast IV:L 4T",
|
||||||
|
"link": "https://www.youtube.com/watch?v=brz21NKMMsY",
|
||||||
|
"targetImageFile": "beast_4_l.png",
|
||||||
|
"date": "2023-12-31",
|
||||||
|
"servantImageFile": "shishou.png",
|
||||||
|
"turns": "4T"
|
||||||
}
|
}
|
||||||
]
|
]
|
1
src/env.d.ts
vendored
|
@ -1 +1,2 @@
|
||||||
|
/// <reference path="../.astro/types.d.ts" />
|
||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
---
|
---
|
||||||
import Navbar from '../components/navbar.astro'
|
import '@fontsource-variable/work-sans'
|
||||||
import NavbarEntry from '../components/navbarEntry.astro'
|
import workSans from '@fontsource-variable/work-sans/files/work-sans-latin-wght-normal.woff2?url'
|
||||||
import navdata from '../../static/data/_navdata.json'
|
|
||||||
import embed from '../assets/embed.png'
|
import Navbar from '@components/navbar/navbar.astro'
|
||||||
|
import NavbarEntry from '@components/navbar/navbarEntry.astro'
|
||||||
|
import navdata from '@datafiles/navdata.json'
|
||||||
|
import embed from '@assets/embed.png'
|
||||||
|
import favicon from '@assets/favicon.ico'
|
||||||
|
|
||||||
|
import home from 'iconoir/icons/home-simple.svg'
|
||||||
|
import fgo from 'iconoir/icons/gamepad.svg'
|
||||||
|
import blog from 'iconoir/icons/bookmark-book.svg'
|
||||||
|
import about from 'iconoir/icons/mail.svg'
|
||||||
|
import projects from 'iconoir/icons/network-right.svg'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
@ -10,6 +20,18 @@ export interface Props {
|
||||||
descriptionOverride?: string
|
descriptionOverride?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IconsLookup {
|
||||||
|
[key: string]: ImageMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons: IconsLookup = {
|
||||||
|
home: home,
|
||||||
|
fgo: fgo,
|
||||||
|
projects: projects,
|
||||||
|
blog: blog,
|
||||||
|
about: about,
|
||||||
|
}
|
||||||
|
|
||||||
const { descriptionOverride, currentpage, title } = Astro.props
|
const { descriptionOverride, currentpage, title } = Astro.props
|
||||||
let description
|
let description
|
||||||
|
|
||||||
|
@ -23,9 +45,14 @@ let currPage = 'https://firq.dev/'
|
||||||
if (currentpage !== 'home') {
|
if (currentpage !== 'home') {
|
||||||
currPage += currentpage
|
currPage += currentpage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapped_navdata = navdata.map((item) => ({
|
||||||
|
...item,
|
||||||
|
...{ icon: icons[item.icon] },
|
||||||
|
}))
|
||||||
---
|
---
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
@ -45,42 +72,95 @@ if (currentpage !== 'home') {
|
||||||
name="google-site-verification"
|
name="google-site-verification"
|
||||||
content="SmcWcewh7DCANcLeTe3ntU0R-LESbo_bsolICJnmulE"
|
content="SmcWcewh7DCANcLeTe3ntU0R-LESbo_bsolICJnmulE"
|
||||||
/>
|
/>
|
||||||
|
<!-- Disable DarkReader, as site is already in dark mode -->
|
||||||
|
<meta name="darkreader-lock" content="this site only has darkmode" />
|
||||||
<!-- Links -->
|
<!-- Links -->
|
||||||
<link rel="icon" type="image/ico" href="/favicon.ico" />
|
<link
|
||||||
|
rel="preload"
|
||||||
|
as="font"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
href={workSans}
|
||||||
|
type="font/woff2"
|
||||||
|
/>
|
||||||
|
<link rel="icon" type="image/ico" href={favicon} />
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
<link href="https://mastodon.neshweb.net/@Firq" rel="me" />
|
<link href="https://mastodon.neshweb.net/@Firq" rel="me" />
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css"
|
|
||||||
/>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<Navbar>
|
<Navbar>
|
||||||
{
|
{
|
||||||
navdata.map((item) => (
|
mapped_navdata.map((item) => (
|
||||||
<NavbarEntry currentPage={currentpage} {...item} />
|
<NavbarEntry
|
||||||
|
currentPage={currentpage}
|
||||||
|
navtype="desktop"
|
||||||
|
{...item}
|
||||||
|
slot="desktop"
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mapped_navdata.map((item) => (
|
||||||
|
<NavbarEntry
|
||||||
|
currentPage={currentpage}
|
||||||
|
navtype="mobile"
|
||||||
|
{...item}
|
||||||
|
slot="mobile"
|
||||||
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<slot />
|
<slot />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
:root {
|
:root {
|
||||||
--hover-scale: 1.05;
|
/* Animations */
|
||||||
--speed: 50%;
|
--a-time-default: 200ms;
|
||||||
--ease: 50%;
|
--a-time-short: 100ms;
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
--a-time-slow: 1000ms;
|
||||||
--c-darkgray: #1e1e1e;
|
--a-animation-1: ease-in-out;
|
||||||
--c-duskgray: #242424;
|
|
||||||
--c-gray: #2e2e2e;
|
/* Fonts */
|
||||||
--c-lightgray: #3e3e3e;
|
--f-default: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
--c-darkpurple: #b86cff;
|
--f-title: 'Work Sans Variable', system-ui, 'Segoe UI', sans-serif;
|
||||||
--c-purplepink: #c105ff;
|
|
||||||
--c-darkergray: #1b1b1b;
|
font-family: var(--f-default);
|
||||||
|
|
||||||
|
/* Colors */
|
||||||
|
--c-primary-background: #1b1b1b;
|
||||||
|
--c-secondary-background: #333;
|
||||||
|
--c-primary-text: #eee;
|
||||||
|
--c-accent-1: #b86cff;
|
||||||
|
--c-accent-1-alt: #c105ff;
|
||||||
|
--c-accent-2: #ff0077;
|
||||||
|
|
||||||
|
--c-rgb-primary-background: 27, 27, 27;
|
||||||
|
--c-rgb-secondary-background: 51, 51, 51;
|
||||||
|
--c-rgb-primary-text: 238, 238, 238;
|
||||||
|
--c-rgb-accent-1: 184, 108, 255;
|
||||||
|
--c-rgb-accent-1-alt: 193, 5, 255;
|
||||||
|
--c-rgb-accent-2: 255, 0, 119;
|
||||||
|
|
||||||
|
/* Scrollbar */
|
||||||
|
scrollbar-color: var(--c-secondary-background) var(--c-primary-background);
|
||||||
|
|
||||||
|
/* Font Size Adjusting to prevent LCP issue */
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--c-lightgray);
|
background: var(--c-secondary-background);
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
border: 0;
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
---
|
|
||||||
export interface Props {
|
|
||||||
title: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title } = Astro.props
|
|
||||||
---
|
|
||||||
|
|
||||||
<section class="base">
|
|
||||||
<h1>{title}</h1>
|
|
||||||
<div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
h1 {
|
|
||||||
font-size: 40px;
|
|
||||||
line-height: 48px;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
color: white;
|
|
||||||
font-size: 2.25rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
max-width: max-content;
|
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
padding: 0.25rem 1.5rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
row-gap: 1em;
|
|
||||||
column-gap: 1em;
|
|
||||||
justify-content: center;
|
|
||||||
align-self: center;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
padding: 1em;
|
|
||||||
color: white;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.base {
|
|
||||||
margin-left: 1rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 400px) {
|
|
||||||
.base {
|
|
||||||
margin-left: 3rem;
|
|
||||||
margin-right: 3rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 1500px) {
|
|
||||||
.base {
|
|
||||||
margin-left: 10%;
|
|
||||||
margin-right: 10%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: 512px) {
|
|
||||||
div {
|
|
||||||
justify-content: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 520px) {
|
|
||||||
h1 {
|
|
||||||
margin: 0.5rem 0.75rem 0.5rem 0.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,13 +1,15 @@
|
||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title: string
|
title: string,
|
||||||
|
titleHidden?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title } = Astro.props
|
const { title, titleHidden } = Astro.props
|
||||||
|
const hidden = titleHidden || false ? "visually-hidden" : ""
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="base">
|
<div class="base">
|
||||||
<h1>{title}</h1>
|
<h1 class={hidden}>{title}</h1>
|
||||||
<div>
|
<div>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,36 +29,25 @@ const { title } = Astro.props
|
||||||
align-self: center;
|
align-self: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
padding: 1em;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
div h1 {
|
h1 {
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
line-height: 48px;
|
|
||||||
letter-spacing: -1px;
|
letter-spacing: -1px;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
margin-top: 2rem;
|
margin: 0 auto 0px;
|
||||||
margin-bottom: 0px;
|
padding: 0.25rem 1.5rem 0.5rem;
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
padding: 0.25rem 1.5rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
max-width: max-content;
|
max-width: max-content;
|
||||||
background-color: var(--c-darkgray);
|
&:after {
|
||||||
padding-bottom: 0.5rem;
|
content:' ';
|
||||||
}
|
display: block;
|
||||||
@media (min-width: 512px) {
|
width: 65%;
|
||||||
div {
|
height: 3px;
|
||||||
row-gap: 1.5em;
|
margin: auto;
|
||||||
column-gap: 1.5em;
|
background-color: var(--c-accent-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.base {
|
|
||||||
margin-left: 1rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1000px) {
|
@media (min-width: 1000px) {
|
||||||
.base {
|
.base {
|
||||||
margin-left: 3rem;
|
margin-left: 3rem;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
---
|
---
|
||||||
|
import SmallTitle from '@components/titles/smallTitle.astro'
|
||||||
import Layout from './Layout.astro'
|
import Layout from './Layout.astro'
|
||||||
const { frontmatter } = Astro.props
|
const { frontmatter } = Astro.props
|
||||||
|
|
||||||
const title = `${frontmatter.title} - Firq FGO Site`
|
const title = `${frontmatter.title} - firq.dev`
|
||||||
const description = frontmatter.description
|
const description = frontmatter.description
|
||||||
const options_date: Intl.DateTimeFormatOptions = {
|
const options_date: Intl.DateTimeFormatOptions = {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
|
@ -13,108 +14,90 @@ const date = new Date(frontmatter.pubDate).toLocaleDateString(
|
||||||
'en-GB',
|
'en-GB',
|
||||||
options_date
|
options_date
|
||||||
)
|
)
|
||||||
|
const subtext = `Written by ${frontmatter.author} • Published on ${date}`
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={title} currentpage="blog" descriptionOverride={description}>
|
<Layout title={title} currentpage="blog" descriptionOverride={description}>
|
||||||
<a href="/blog"><i class="iconoir-fast-arrow-left"></i>Back to all posts</a>
|
<SmallTitle maintext={frontmatter.title} subtext={subtext} fadeout={true} returnbutton={true} baseurl='blog'/>
|
||||||
<div>
|
<article>
|
||||||
<h1>
|
<slot />
|
||||||
{frontmatter.title}
|
</article>
|
||||||
</h1>
|
|
||||||
<p>by {frontmatter.author} • Published on {date}</p>
|
|
||||||
<article>
|
|
||||||
<slot />
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
a {
|
article {
|
||||||
display: flex;
|
color: var(--c-primary-text);
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
|
||||||
background-color: var(--c-gray);
|
|
||||||
padding: 0.5rem 0px;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 34px;
|
|
||||||
line-height: 40px;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
color: white;
|
|
||||||
margin: 0.5rem 3rem 0.5rem 3rem;
|
margin: 0.5rem 3rem 0.5rem 3rem;
|
||||||
padding: 0.25rem 0.75rem;
|
font-size: 1.1em;
|
||||||
max-width: max-content;
|
text-align: justify;
|
||||||
background-color: var(--c-darkgray);
|
padding-bottom: 5rem;
|
||||||
padding: 0.25rem 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
color: white;
|
|
||||||
margin: 0.5rem 3rem 0.5rem 3rem;
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
max-width: max-content;
|
|
||||||
background-color: var(--c-gray);
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article :global(h2) {
|
article :global(h2) {
|
||||||
margin-right: 3rem;
|
font-size: 32px;
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
max-width: max-content;
|
max-width: max-content;
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
padding: 0.25rem 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 32px;
|
||||||
|
&:after {
|
||||||
|
content:' ';
|
||||||
|
display: block;
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 2px;
|
||||||
|
border: 1px solid var(--c-accent-1);
|
||||||
|
box-shadow:inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
article :global(h3) {
|
article :global(h3) {
|
||||||
margin-bottom: 0.5rem;
|
font-size: 24px;
|
||||||
margin-right: 3rem;
|
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
max-width: max-content;
|
max-width: max-content;
|
||||||
background-color: var(--c-darkgray);
|
|
||||||
padding: 0.25rem 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 32px;
|
||||||
|
&:after {
|
||||||
|
content:' ';
|
||||||
|
display: block;
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 2px;
|
||||||
|
border: 1px solid var(--c-accent-1);
|
||||||
|
box-shadow:inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
article :global(h4) {
|
article :global(h4) {
|
||||||
margin-bottom: 0.5rem;
|
font-size: 18px;
|
||||||
margin-right: 3rem;
|
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
max-width: max-content;
|
max-width: max-content;
|
||||||
background-color: var(--c-darkgray);
|
margin-bottom: 0px;
|
||||||
padding: 0.25rem 1rem;
|
text-align: left;
|
||||||
border-radius: 0.5rem;
|
line-height: 32px;
|
||||||
padding-bottom: 0.5rem;
|
&:after {
|
||||||
|
content:' ';
|
||||||
|
display: block;
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 1px;
|
||||||
|
border: 1px solid var(--c-accent-1);
|
||||||
|
box-shadow:inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
article :global(a) {
|
article :global(a) {
|
||||||
color: #dcb7ff;
|
color: #dcb7ff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
article {
|
|
||||||
color: white;
|
|
||||||
margin: 0.5rem 3rem 0.5rem 3rem;
|
|
||||||
font-size: 1.1em;
|
|
||||||
padding-bottom: 5rem;
|
|
||||||
}
|
|
||||||
article :global(.astro-code) {
|
article :global(.astro-code) {
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 1rem 1rem 1rem 2rem;
|
padding: 1rem 1rem 1rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
article :global(code) {
|
article :global(code) {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: orange;
|
color: orange;
|
||||||
|
@ -122,31 +105,16 @@ const date = new Date(frontmatter.pubDate).toLocaleDateString(
|
||||||
|
|
||||||
@media (min-width: 1000px) {
|
@media (min-width: 1000px) {
|
||||||
article {
|
article {
|
||||||
margin-left: 10rem;
|
margin-left: 22.5%;
|
||||||
margin-right: 10rem;
|
margin-right: 22.5%;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
margin-left: 10rem;
|
margin-left: 20%;
|
||||||
margin-right: 10rem;
|
margin-right: 20%;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
margin-left: 10rem;
|
margin-left: 20%;
|
||||||
margin-right: 10rem;
|
margin-right: 20%;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1500px) {
|
|
||||||
article {
|
|
||||||
margin-left: 20rem;
|
|
||||||
margin-right: 20rem;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin-left: 20rem;
|
|
||||||
margin-right: 20rem;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin-left: 20rem;
|
|
||||||
margin-right: 20rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,36 +1,59 @@
|
||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title: string
|
title: string
|
||||||
|
titlehidden?: boolean
|
||||||
|
displayLine?: boolean
|
||||||
}
|
}
|
||||||
|
const { title, titlehidden, displayLine } = Astro.props
|
||||||
const { title } = Astro.props
|
const display = titlehidden ? "display: none" : ""
|
||||||
|
const line = displayLine ? "flex" : "none"
|
||||||
---
|
---
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h1>{title}</h1>
|
<h1 style={display}>{title}</h1>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
<div class="start hightlighter"></div>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<slot />
|
<slot />
|
||||||
<div class="drop"></div>
|
<div class="drop hightlighter"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style define:vars={{ line }}>
|
||||||
.drop {
|
section {
|
||||||
|
padding-left: 0.25em;
|
||||||
|
padding-right: 0.25em;
|
||||||
|
padding-bottom: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hightlighter {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: -5rem;
|
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop {
|
||||||
|
bottom: -5rem;
|
||||||
border-radius: 0% 50% 50% 50%;
|
border-radius: 0% 50% 50% 50%;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
background-color: var(--c-darkpurple);
|
background-color: var(--c-accent-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.start {
|
||||||
|
top: -2rem;
|
||||||
|
border-radius: 40%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0.25rem;
|
||||||
|
border-color: var(--c-secondary-background);
|
||||||
|
background-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -39,40 +62,41 @@ const { title } = Astro.props
|
||||||
right: 0;
|
right: 0;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
background-color: var(--c-darkpurple);
|
background-color: var(--c-accent-1);
|
||||||
height: calc(100% + 5rem);
|
height: calc(100% + 5rem);
|
||||||
translate: 0% 2rem;
|
translate: 0% 2rem;
|
||||||
width: 0.25rem;
|
width: 0.25rem;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 40px;
|
|
||||||
line-height: 48px;
|
line-height: 48px;
|
||||||
letter-spacing: -1px;
|
letter-spacing: -1px;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
font-size: 2.25rem;
|
font-size: 1.6rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
max-width: max-content;
|
max-width: max-content;
|
||||||
background-color: var(--c-darkgray);
|
background-color: var(--c-primary-background);
|
||||||
padding: 0.25rem 1.5rem;
|
padding: 0.25rem 1.5rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
justify-self: center;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.wrapper {
|
.wrapper {
|
||||||
margin: 2rem 3rem 0.5rem 3rem;
|
margin: 2rem 2rem 0.5rem 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column wrap;
|
flex-flow: column wrap;
|
||||||
row-gap: 1em;
|
row-gap: 1em;
|
||||||
column-gap: 1em;
|
column-gap: 1em;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
align-content: center;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
padding: 1em;
|
padding: 1em 0px;
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -80,21 +104,44 @@ const { title } = Astro.props
|
||||||
.drop {
|
.drop {
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
}
|
}
|
||||||
|
.start {
|
||||||
|
margin-left: 1.25rem;
|
||||||
|
}
|
||||||
.line {
|
.line {
|
||||||
margin-left: 2.1rem;
|
margin-left: 2.1rem;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
margin-left: 3rem;
|
margin-left: 3rem;
|
||||||
|
font-size: 1.85rem;
|
||||||
|
}
|
||||||
|
.wrapper {
|
||||||
|
padding: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 1500px) {
|
@media (min-width: 1100px) {
|
||||||
.wrapper {
|
.wrapper {
|
||||||
margin-left: 20rem;
|
margin-left: 20%;
|
||||||
margin-right: 20rem;
|
margin-right: 20%;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
padding-bottom: unset;
|
||||||
|
}
|
||||||
|
.drop, .start, .line {
|
||||||
|
display: var(--line);
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
.start {
|
||||||
|
margin-left: 1.75rem;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
margin-left: 2.625rem;
|
||||||
|
height: calc(100% + 6rem);
|
||||||
|
translate: 0px -2rem;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
margin-left: 20rem;
|
margin-left: 15rem;
|
||||||
margin-right: 20rem;
|
margin-right: 15rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -15,18 +15,23 @@ const { title } = Astro.props
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h1 {
|
h1 {
|
||||||
color: white;
|
color: var(--c-primary-text);
|
||||||
font-size: 26px;
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
letter-spacing: -1px;
|
font-family: 'Work Sans Variable', sans-serif;
|
||||||
margin: 0.5rem 0px 0.5rem 0.5em;
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
width: max-content;
|
width: max-content;
|
||||||
background-color: var(--c-darkgray);
|
margin: 0 auto 0.5rem;
|
||||||
padding: 0.25rem 1rem;
|
&:after {
|
||||||
border-radius: 0.5rem;
|
content:' ';
|
||||||
padding-bottom: 0.5rem;
|
display: block;
|
||||||
|
width: 75%;
|
||||||
|
height: 2px;
|
||||||
|
margin: -1px auto auto;
|
||||||
|
background-color: var(--c-accent-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
row-gap: 1em;
|
row-gap: 1em;
|
||||||
column-gap: 1em;
|
column-gap: 1em;
|
||||||
|
@ -51,6 +56,14 @@ const { title } = Astro.props
|
||||||
margin-left: 3rem;
|
margin-left: 3rem;
|
||||||
margin-right: 3rem;
|
margin-right: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
&:after {
|
||||||
|
margin: -2px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 1500px) {
|
@media (min-width: 1500px) {
|
||||||
.base {
|
.base {
|
||||||
|
|