Compare commits

...

140 commits

Author SHA1 Message Date
ac85131e69 Update package.json 2025-08-01 10:04:39 +00:00
2c42f18e81 Update unlighthouse.config.ts 2025-08-01 10:01:31 +00:00
d56f70c1ba
Bumped ver 2025-08-01 07:51:39 +02:00
36e0a056fa
Merge branch 'dev' 2025-08-01 07:50:48 +02:00
31c3f76a9b
Release Candidate 2025-08-01 07:47:08 +02:00
693f0f2eae
added ai.txt 2025-04-27 18:29:08 +02:00
8d6ededac6
updated robots with ai.robots.txt 2025-04-27 15:57:02 +02:00
67e565493c
updated robots with ai.robots.txt 2025-04-27 15:56:28 +02:00
cc6b3c22b8
fixed animations 2025-04-21 21:55:54 +02:00
13ccaa1977
applied faster phone settings 2025-04-17 23:23:38 +02:00
52dd87b52d
test theory 2025-04-17 22:10:16 +02:00
94687c6866
LCP fixes 2025-04-17 21:46:30 +02:00
ce4e7777e0
Animations fix 2025-04-17 20:06:18 +02:00
14cc9e0b7e
Updated ligthouse config 2025-04-17 19:05:46 +02:00
e402b7e321
Adapt lighthouse 2025-04-16 21:42:27 +02:00
f11a53cd11
Test font changes 2025-04-16 19:25:49 +02:00
f7c7408fdd
switched fonts stuff 2025-04-16 00:21:33 +02:00
3f0d0ab3c4
removed sh 2025-04-15 23:44:53 +02:00
1cbec0fe78
debugging 2 2025-04-15 23:23:45 +02:00
3814b179ce
debugging 2025-04-15 23:09:00 +02:00
34df58168b
minified markdown a bit 2025-04-15 22:32:31 +02:00
d53bae9537
sh start script 2025-04-15 22:19:47 +02:00
1cae67023f
Added version output 2025-04-15 22:02:01 +02:00
edb548144a
switched container 2025-04-15 21:37:44 +02:00
a4e306b45a
Testing astro-critical-css with bookworm 2 2025-04-14 23:05:16 +02:00
2e513cb72f
Testing astro-critical-css with bookworm 2025-04-14 22:46:48 +02:00
120af666cb
Testing astro-critical-css 2025-04-14 22:32:04 +02:00
805ce53362
fixed accessibility 2025-04-12 19:16:09 +02:00
08b2227b22
Changed Firq FGO Site to firq.dev 2025-04-12 19:05:52 +02:00
66c9220468
Unlighthouse blogpost + version in docker 2025-04-12 18:43:57 +02:00
753a39e13c
fixed unlighthouse 11 2025-04-12 14:08:37 +02:00
215b803749
fixed unlighthouse 10 2025-04-12 00:06:46 +02:00
278befbb50
fixed unlighthouse 9 2025-04-11 23:38:41 +02:00
02d5af5a2d
fixed unlighthouse 8 2025-04-11 23:27:57 +02:00
ae98709260
fixed unlighthouse 7 2025-04-11 22:46:38 +02:00
cea02754ce
fixed unlighthouse 6 2025-04-11 22:04:59 +02:00
63024eb324
fixed unlighthouse 5 2025-04-11 21:21:50 +02:00
3da3625d16
fixed unlighthouse 4 2025-04-08 21:15:42 +02:00
2ec229f40b
fixed unlighthouse 3 2025-04-08 20:44:47 +02:00
ba2710f4b3
fixed unlighthouse 2 2025-04-08 20:37:59 +02:00
83cda5dbb5
fixed unlighthouse 2025-04-08 20:31:56 +02:00
b75fb2499e
enable logging for debug 5 2025-04-08 20:18:43 +02:00
d0d43d8caf
enable logging for debug 4 2025-04-08 20:07:38 +02:00
199758f822
enable logging for debug 3 2025-04-08 19:58:59 +02:00
c50c8eb9ce
enable logging for debug 2 2025-04-08 19:50:21 +02:00
e6ee5799a5
enable logging for debug 2025-04-08 19:40:19 +02:00
3b72c24361
test 2025-04-08 18:04:43 +02:00
88e4b92b8d Apply patch 2025-04-08 09:32:41 +00:00
c013c7a298 Update package.json 2025-04-07 22:58:39 +00:00
4c99e009f4 Update Dockerfile 2025-04-07 22:55:57 +00:00
b71a83de60
ci fix 4? 2025-04-08 00:34:12 +02:00
7e19e5f76b
ci fix 3#? 2025-04-08 00:24:11 +02:00
62e429329b
ci fix 2? 2025-04-07 23:59:57 +02:00
b2e4ac27b6
ci fix? 2025-04-07 23:27:36 +02:00
d19ded377f
resolution fix 2025-04-07 23:17:32 +02:00
7d6f6eb4d5
fixed icons and links (and ci?) 2025-04-07 22:47:03 +02:00
1782b69664
lock update 2025-04-07 21:54:09 +02:00
aa59137748
restructure donw, rearing 1.0.0 release 2025-04-07 21:53:48 +02:00
aed70ccdd9
removed twitter, added bluesky 2025-04-06 22:12:42 +02:00
ed8549d6aa
Dependencies 2025-02-28 22:26:47 +01:00
19492fc41b
Bumped astro to 5.3.0 2025-02-14 16:10:14 +01:00
81c1def4c4
Bumped embed to fix accessibility issue 2025-02-07 11:37:16 +01:00
d5e4818b2b
fix ver 2 2025-02-06 20:40:14 +01:00
e81dbd6048
Bumped ver 2025-02-06 19:43:16 +01:00
621b17f5b4
Bumped ULH Container 2025-02-06 19:42:55 +01:00
03e341710f
Testing new unlighthouse shenanigans 2025-02-06 19:31:18 +01:00
eec488ddff
Upgraded to Astro 5.2.5 and fixed content collections 2025-02-06 19:08:51 +01:00
298b91262e
Bumped astro, need to migrate content collections 2025-01-15 15:22:29 +01:00
51b8817dc7
Scrollbar 2024-11-03 20:58:35 +01:00
dd78cc13ef
Unlighthouse rework with docker 6 2024-11-01 16:17:38 +01:00
9db0b71ee4
Unlighthouse rework with docker 5 2024-11-01 16:09:53 +01:00
c7ae9fa64b
Unlighthouse rework with docker 4 2024-11-01 16:05:41 +01:00
c754871a9b
Unlighthouse rework with docker 3 2024-11-01 15:59:40 +01:00
7f4b1e7259
Unlighthouse rework with docker 2 2024-11-01 15:55:08 +01:00
9171fda784
Unlighthouse rework with docker 2024-11-01 15:43:20 +01:00
2321c5e5ab
Checkbox fix 2 2024-10-30 21:41:38 +01:00
6ff1e5ec1c
JS version 2024-10-30 21:24:21 +01:00
7a3c4f14ae
Checkbox lag fix 2024-10-30 20:51:09 +01:00
f9fb025622
Menu Fix 2 2024-10-30 20:45:41 +01:00
cc885e7740
Menu Fix 2024-10-30 20:41:11 +01:00
7be69a0ce5
Accessibility 2024-10-30 20:36:42 +01:00
99fe35aa23
Dropdown change on mobile 2024-10-30 20:28:45 +01:00
d2dddbeb2a
Satisfying 2024-10-29 22:17:38 +01:00
7222d1c58b
Ver Bump 2024-10-29 21:03:12 +01:00
d9e4f5b5f9
Social restructure 2024-10-29 21:02:37 +01:00
19e7d02520
Birthday and small fix 2024-10-27 14:58:32 +01:00
fa88fede29
Subpixel fix 2024-10-26 22:51:03 +02:00
a77312c17c
Restructure and extra stuff 2024-10-26 22:41:24 +02:00
b0fc3a05c5
First about version and rescaling 2024-10-26 17:37:35 +02:00
e717b4b8dd
Vari in name 2024-10-26 14:29:24 +02:00
86b6263ec1
Fixed spaces 2024-10-26 14:24:59 +02:00
49f270974f
runname? 2024-10-26 14:16:28 +02:00
c3dc83ff1d
Test run name 2024-10-26 14:14:10 +02:00
1945b41b4b
Link markdown, robots and contact vars 2024-10-26 13:51:37 +02:00
b32fa9e4ad
Further redesign 2 2024-10-26 00:22:08 +02:00
afe18e37b6
Further redesign 2024-10-25 15:26:25 +02:00
4eb390d0b1
unlighthouse change + accessibility 2024-10-24 20:07:39 +02:00
7a0863d9a2
Find more pages and stuff 2024-10-24 19:12:56 +02:00
20c6753ee1
Ficked package-lock.json 2024-10-24 19:03:11 +02:00
cdda005d47
Fixed package-lock 2024-10-24 18:58:35 +02:00
34e24346d1
Colors and other stuff 2024-10-24 18:52:33 +02:00
aab26e63b2
Fixed TA site part 2024-10-23 17:28:06 +02:00
5dfbd2cccd
PageSpeed Insights config 2024-10-23 00:20:44 +02:00
8c425d8690
More samples 2024-10-23 00:01:07 +02:00
4a4d5434f5
try to cheat LCP 2024-10-22 23:55:30 +02:00
d6fb3bccbe
Re-enable prefetch because 2024-10-22 23:38:26 +02:00
0e637384b6
Eager load less 2024-10-22 23:29:08 +02:00
f4e8a39c08
Made favicon smaller, removed prefetch 2024-10-22 23:18:30 +02:00
cf84c65beb
Made favicon smaller, removed prefetch 2024-10-22 23:18:17 +02:00
90ac6d527c
Test prefetch 2024-10-22 23:07:34 +02:00
da582980b9
Curl index for availability 2024-10-22 22:58:43 +02:00
c0588ee760
Finding the best font solution 2024-10-22 22:54:02 +02:00
e11130ce1a
mobile and images 2024-10-22 22:18:33 +02:00
500080be9f
sed and desktop 2024-10-22 22:12:02 +02:00
9922507975
fixed deps 2024-10-22 21:58:06 +02:00
e4252aa6dc
Not preloading fonts 2024-10-22 21:54:18 +02:00
4cb0f2db10
Image sizes 2 2024-10-22 21:34:18 +02:00
031d057811
Image sizes 2024-10-22 21:15:24 +02:00
aaa9611b1c
Version bump 2024-10-22 20:53:09 +02:00
8e6be5ae82
Revert "Source Sets"
This reverts commit d93d0416e5.
2024-10-22 20:51:48 +02:00
d93d0416e5
Source Sets 2024-10-22 20:45:39 +02:00
564e902b69
Test with tag 2024-10-22 20:00:14 +02:00
ed91be36d7
Test with tag 2024-10-22 19:59:19 +02:00
3d729731a5
fix input 2024-10-22 19:54:26 +02:00
2bd68c106c
fix input 2024-10-22 19:53:51 +02:00
34421fdb76
fix payload 2024-10-22 19:52:16 +02:00
d906df3fec
trace2 2024-10-22 19:50:35 +02:00
8a84de8807
trace 2024-10-22 19:48:57 +02:00
014ea40dce
WHHAT THE ACTUAL FUCK IS WRONG WITH THOSE ******************* 2024-10-22 19:46:48 +02:00
0473c836d7
auth6 2024-10-22 19:44:24 +02:00
3587ee38a7
auth4 2024-10-22 19:38:09 +02:00
c0d6d8a516
auth4 2024-10-22 19:36:56 +02:00
4387207c14
auth3 2024-10-22 19:36:04 +02:00
f26fdbe579
auth2 2024-10-22 19:35:02 +02:00
2696d0e146
auth 2024-10-22 19:34:29 +02:00
0b7b7fa071
fixed ref 2024-10-22 19:32:22 +02:00
2d07dbbbdb
Test dispatch 2024-10-22 19:29:01 +02:00
6e3d23e8f7
work? 2024-10-22 18:58:09 +02:00
86ee37e441
test stupid 2024-10-22 18:56:16 +02:00
f5bb4bf78e Merge pull request 'Mergin dev - new header, updated servants' () from dev into main
Reviewed-on: 
2024-06-15 17:20:51 +00:00
91 changed files with 15150 additions and 8116 deletions

View file

@ -1,3 +1,5 @@
name: Build and deploy preview site
run-name: Building and deploying preview for version ${{ github.ref_name }}
on:
push:
tags:
@ -49,6 +51,8 @@ jobs:
with:
push: true
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 }}
create-release:
needs: [ build-site ]
@ -80,3 +84,18 @@ jobs:
dockge status firq-dev-preview
dockge update firq-dev-preview
dockge status firq-dev-preview
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

View file

@ -1,3 +1,5 @@
name: Build and deploy production site
run-name: Building and deploying release for version ${{ github.ref_name }}
on:
push:
tags:
@ -49,6 +51,8 @@ jobs:
with:
push: true
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 }}
auto-deploy-dockge:
needs: [ build-site ]
@ -65,6 +69,21 @@ jobs:
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 ]

View file

@ -1,3 +1,5 @@
name: Linting and checking code
run-name: Linting and checking code
on:
push:
branches:
@ -9,7 +11,7 @@ jobs:
steps:
- name: Checking Out Repository Code
uses: https://code.forgejo.org/actions/checkout@v3
- name: Check if Version in package.json matches Tag
- name: Get version from package.json
run: |
VERSION=$(npm pkg get version --workspaces=false | tr -d \")
echo "Version is: '$VERSION'";

View file

@ -1,39 +1,36 @@
name: Run unlighthouse-tests [Downstream pipeline]
run-name: Run unlighthouse-tests [Downstream pipeline] for version ${{ inputs.containertag }}
on:
push:
tags:
- '[0-9]+\.[0-9]+\.[0-9]+-ulh\.[0-9]+'
workflow_dispatch:
inputs:
containertag:
description: "Tag of the container to test"
default: "preview"
type: "string"
jobs:
unlighthouse:
runs-on: docker
container:
image: forgejo.neshweb.net/ci-docker-images/unlighthouse:0.3.0
image: forgejo.neshweb.net/ci-docker-images/unlighthouse:0.16.3
services:
website:
image: forgejo.neshweb.net/firq/firq-dev-website:preview
image: forgejo.neshweb.net/firq/firq-dev-website:${{ inputs.containertag }}
options: >-
--workdir /
ports:
- 26400:8081
--hostname website
steps:
- name: Checkout repository
uses: https://code.forgejo.org/actions/checkout@v3
- name: test
run: curl -v -o - "http://localhost:26400/" || true
- name: test
run: curl -v -o - "http://website:26400/" || true
- name: test
run: curl -v -o - "http://website:8081/" || true
- name: test
run: curl -v -o - "http://website/" || true
- name: test
run: curl -v -o - "website" || true
- name: test
run: curl -v -o - "website:26400" || true
- name: test
run: curl -v -o - "website:8081" || true
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
run: unlighthouse-ci --site "http://localhost:26400/" --disable-dynamic-sampling
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
run: cp serve.json unlighthouse-reports
- name: Upload reports
@ -41,79 +38,58 @@ jobs:
with:
name: 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 ]
if: success()
runs-on: docker
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
runs-on: dind
steps:
- name: Install and update ssh + rsync
run: |
which rsync || ( apt update -y && apt install rsync -y)
which ssh-agent || ( apt update -y && apt install openssh-client -y)
- name: Downloading static site artifacts
uses: actions/download-artifact@v3
with:
name: dockerfile
- name: Downloading static site artifacts
uses: actions/download-artifact@v3
with:
name: unlighthouse-reports
path: public
- name: Install SSH Key
uses: https://github.com/shimataro/ssh-key-action@v2
path: reports
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log into Docker Package Registry
uses: docker/login-action@v3
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
known_hosts: unnecessary
- name: Adding Known Hosts
run: ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- name: Stop screen session, delete old files
uses: https://github.com/appleboy/ssh-action@master
registry: forgejo.neshweb.net
username: ${{ secrets.FORGEJO_USERNAME }}
password: ${{ secrets.FORGEJO_TOKEN }}
- name: Build and push to Docker Package Registry
uses: docker/build-push-action@v5
with:
host: ${{ env.DEPLOY_HOST }}
username: ${{ env.DEPLOY_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
screen -X -S firq_dev-unlighthouse kill
rm -r -f firq_dev/unlighthouse/*
- 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"
context: .
file: Dockerfile.reports
push: true
tags: forgejo.neshweb.net/firq/firq-dev-website-unlighthouse:latest
build-args: |
version=${{ inputs.containertag }}
deploy-unlighthouse-site:
needs: [ deploy-unlighthouse-files ]
auto-deploy-dockge:
needs: [ build-site ]
if: success()
runs-on: docker
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
container: forgejo.neshweb.net/firq/dockge-cli:0.1.2
steps:
- name: Install and update ssh + rsync
- name: Configure Dockge CLI
run: |
which rsync || ( apt update -y && apt install rsync -y)
which ssh-agent || ( apt update -y && apt install openssh-client -y)
- name: Install SSH Key
uses: https://github.com/shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
known_hosts: unnecessary
- name: Adding Known Hosts
run: ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- 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"
screen -ls | grep 'firq_dev-unlighthouse' | awk '{print $1}' | xargs -I % -t screen -X -S % quit
cd firq_dev
screen -S firq_dev-unlighthouse -dm serve unlighthouse/ -p ${{ secrets.UNLIGHTHOUSE_DEPLOY_PORT }}
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-unlighthouse
dockge down firq-dev-unlighthouse
dockge update firq-dev-unlighthouse
dockge start firq-dev-unlighthouse
dockge status firq-dev-unlighthouse

8
.gitignore vendored
View file

@ -1,7 +1,6 @@
# build output
dist/
.output/
public/
maintenance/
# astro stuff
@ -29,10 +28,3 @@ pnpm-debug.log*
# macOS-specific files
.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
View file

@ -0,0 +1,5 @@
{
"MD013":false,
"MD033": false,
"MD036": false
}

View file

@ -3,4 +3,5 @@
**/.vscode
**/public
**/static
**/scripts
**/scripts
**/dist

View file

@ -1,15 +1,21 @@
FROM node:22-alpine AS build
FROM forgejo.neshweb.net/ci-docker-images/node-chromium:22 AS build
WORKDIR /app
ENV CI=true
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/serve.json /public/serve.json
RUN rm -r /public/data/
EXPOSE 8081
CMD serve public/ -p 8081
CMD echo "Website version ${version} - powered by @Firq"; serve --listen 8081 --no-clipboard /public

10
Dockerfile.reports Normal file
View 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

View file

@ -1,14 +1,15 @@
import { defineConfig } from 'astro/config';
import sitemap from "@astrojs/sitemap";
import mdx from "@astrojs/mdx";
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
export default defineConfig({
sitemap: true,
base: '/',
outDir: 'dist',
publicDir: 'static',
publicDir: 'public',
site: 'https://firq.dev/',
integrations: [sitemap(), mdx()]
integrations: [sitemap(), mdx(), astroMetaTags(), criticalCss()],
});

20563
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
{
"name": "@firq/fgosite",
"type": "module",
"version": "0.2.0-pre.13",
"version": "0.2.1-pre.1",
"private": true,
"scripts": {
"dev": "astro dev",
@ -11,17 +11,21 @@
"astro": "astro"
},
"dependencies": {
"@astro-community/astro-embed-youtube": "^0.5.2",
"@astro-community/astro-embed-youtube": "^0.5.6",
"@astrojs/check": "^0.9.4",
"@astrojs/mdx": "^3.1.8",
"@astrojs/sitemap": "^3.2.1",
"@fontsource/work-sans": "^5.0.18",
"astro": "^4.16.6",
"astro-meta-tags": "^0.3.0",
"autoprefixer": "^10.4.19",
"iconoir": "^7.7.0",
"postcss-preset-env": "^9.6.0",
"typescript": "^5.5.3"
"@astrojs/mdx": "^4.3.2",
"@astrojs/sitemap": "^3.4.2",
"@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",

View file

Before

Width: 256px  |  Height: 256px  |  Size: 123 KiB

After

(image error) Size: 123 KiB

Before After
Before After

6
public/ai.txt Normal file
View 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
View 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

View file

@ -22,7 +22,7 @@
"headers": [
{
"key": "Cache-Control",
"value": "no-cache"
"value" : "max-age=31536000"
}
]
},

View 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

View 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

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width: 32px  |  Height: 32px  |  Size: 4.2 KiB

Binary file not shown.

Before

(image error) Size: 425 KiB

After

(image error) Size: 248 KiB

Before After
Before After

Binary file not shown.

Before

(image error) Size: 421 KiB

After

(image error) Size: 268 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 238 KiB

Binary file not shown.

Before

(image error) Size: 436 KiB

After

(image error) Size: 283 KiB

Before After
Before After

Binary file not shown.

Before

(image error) Size: 20 KiB

After

(image error) Size: 22 KiB

Before After
Before After

View 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>

View file

@ -41,21 +41,21 @@ const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
width: 100%;
}
p {
color: white;
color: var(--c-primary-text);
text-align: left;
font-size: 1.1em;
margin: 0.5em;
}
article > h2 {
margin: 0.3rem 0.5rem;
color: var(--c-darkpurple);
color: var(--c-accent-1);
font-size: 1.5rem;
line-height: normal;
text-decoration: none;
}
article > h3 {
margin: 0.2em 0.5rem;
color: white;
color: var(--c-primary-text);
font-size: 1rem;
line-height: normal;
text-decoration: none;
@ -68,17 +68,17 @@ const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
align-items: center;
align-content: center;
justify-content: center;
background-color: var(--c-darkergray);
background-color: var(--c-primary-background);
padding: 10px;
text-align: center;
transition: transform var(--speed) var(--ease);
min-height: 100%;
border: 2px solid var(--c-primary-background);
border-radius: 1.25rem;
transition: border-color var(--a-time-default) var(--a-animation-1);
}
a:hover > article {
transform: scaleY(102.5%) scaleX(101%);
transition: transform var(--speed) var(--ease);
a:hover article {
border-color: var(--c-accent-1);
}
@media (min-width: 900px) {
@ -89,30 +89,27 @@ const date = new Date(pubdate).toLocaleDateString('en-GB', options_date)
visibility: visible;
height: 1rem;
width: 1rem;
background-color: var(--c-darkpurple);
background-color: var(--c-accent-1);
border-style: solid;
border-width: 0.25rem;
border-color: var(--c-lightgray);
border-color: var(--c-secondary-background);
border-radius: 40%;
transition: transform var(--speed) var(--ease);
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;
translate: -0.125rem;
transform: translateX(-0.125rem);
margin-right: 4px;
}
a:hover article {
border-color: var(--c-darkpurple);
transform: none;
}
article {
border-style: solid;
border-width: 2px;
border-color: var(--c-darkergray);
border: 2px solid var(--c-primary-background);
align-items: flex-start;
align-content: flex-start;
margin-left: 0.5rem;

View file

@ -1,6 +1,6 @@
---
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'
@ -34,9 +34,10 @@ const mlb_image = mlb ? 'mlbalign' : 'hidemlb'
}
article {
transition: border-color var(--a-time-default) var(--a-animation-1);
border-radius: 1.25rem;
border: 2px var(--c-darkgray) solid;
background-color: var(--c-darkergray);
border: 2px var(--c-primary-background) solid;
background-color: var(--c-primary-background);
width: 35%;
height: auto;
justify-content: center;
@ -53,7 +54,7 @@ const mlb_image = mlb ? 'mlbalign' : 'hidemlb'
}
article:hover {
border: 2px var(--c-darkpurple) solid;
border: 2px var(--c-accent-1) solid;
}
article div {

View file

@ -10,7 +10,6 @@ export interface Props {
}
const { imageFile, link, site } = Astro.props
const logoAltText = `${site} Logo`
const imagePath = `/src/assets/social/${imageFile}`
const images_logos = import.meta.glob<GlobImage>(
'/src/assets/social/*.{png,webp}'
@ -25,78 +24,67 @@ const loadedLogoImage = plsLoadImage(images_logos, imagePath)
aria-label=`${site} - new window`
>
<article class="contact do-hover">
<Image src={loadedLogoImage} alt={logoAltText} loading={'eager'}/>
<Image src={loadedLogoImage} alt="" loading={'eager'} width={128} height={128} quality={100}/>
<div>
<h2>{site}</h2>
</div>
</article>
<span class="visually-hidden">{logoAltText}</span>
<span class="visually-hidden">{site}</span>
</a>
<style>
a {
text-decoration: none;
--s-image: 80px;
}
article {
--size-value: 6.25rem;
border-radius: 1.25rem;
display: flex;
justify-content: center;
position: relative;
align-items: center;
background-color: var(--c-darkergray);
border-color: var(--c-darkgray);
padding: 10px;
justify-content: 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;
width: auto;
position: relative;
}
article img {
border-radius: 1.25rem;
width: var(--size-value);
height: var(--size-value);
}
article:hover div {
border-radius: 1.25rem;
padding: 0 0.5rem;
margin: unset;
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%;
border-style: solid;
border-width: 10px;
border-color: var(--c-darkgray);
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;
width: var(--s-image);
height: auto;
}
article div {
display: none;
}
a:hover {
transform: scale(var(--hover-scale));
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: 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>

View file

@ -25,7 +25,7 @@ const loadedImage = plsLoadImage(images, imagePath)
aria-label=`${name} - new window`
>
<div class="heading">{name}</div>
<Image src={loadedImage} alt={name} loading={'eager'}/>
<Image src={loadedImage} alt={name} loading={'eager'} width={128} height={128} quality={100}/>
<h2 class="subtext">
{origin}
</h2>
@ -34,39 +34,41 @@ const loadedImage = plsLoadImage(images, imagePath)
<style>
.heading {
display: flex;
height: 2.5rem;
height: 3.5rem;
min-height: 2.5rem;
line-height: 1.75rem;
font-size: 24px;
color: white;
max-width: 200px;
color: var(--c-primary-text);
max-width: 150px;
padding-bottom: 0.3rem;
font-weight: bold;
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-darkergray);
padding: 0em 0.5em;
width: max(40%, 200px);
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;
transition: transform var(--speed) var(--ease);
text-decoration: none;
border: 2px var(--c-darkgray) solid;
border: 2px var(--c-primary-background) solid;
&:hover {
border: 2px var(--c-darkpurple) solid;
border: 2px var(--c-accent-1) solid;
}
img {
width: min(90%, 160px);
width: min(90%, 100px);
margin: 0px 0.5rem;
height: auto;
border-radius: 1rem;
@ -79,15 +81,15 @@ const loadedImage = plsLoadImage(images, imagePath)
width: auto;
height: auto;
img {
width: 160px;
width: 128px;
}
}
}
.subtext {
color: white;
color: var(--c-primary-text);
font-size: 16px;
font-weight: 600;
font-weight: 400;
margin: 0.5rem;
line-height: 20px;
}

View 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>

View file

@ -12,9 +12,10 @@ export interface Props {
bondceImageFile: string
ml: string
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 bondceImagePath = `/src/assets/ce/bond-ce/${bondceImageFile}`
const images_servants = import.meta.glob<GlobImage>('/src/assets/servant/*.{png,webp}')
@ -27,10 +28,12 @@ let bondce_css: string = 'bond-ce'
if (bond10 === false) {
bondce_css += ' unobtained'
}
let loading: "eager" | "lazy" = index <= 3 ? "eager" : "lazy";
---
<article>
<Image src={loadedServantImage} alt={name} />
<Image src={loadedServantImage} width={128} height={128} alt={name} quality={100} loading={loading}/>
<h2 class="subtext">
Level {level}<br />
{skills}<br />
@ -43,37 +46,18 @@ if (bond10 === false) {
</article>
<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 {
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-darkpurple);
border-color: var(--c-accent-1);
}
article {
display: flex;
flex-wrap: wrap;
flex-direction: column;
background-color: var(--c-darkergray);
border: 2px var(--c-darkgray) solid;
background-color: var(--c-primary-background);
border: 2px var(--c-primary-background) solid;
padding: 10px;
width: max(30%, 100px);
height: auto;
@ -90,7 +74,7 @@ if (bond10 === false) {
margin-top: 0.75rem;
}
.subtext {
color: white;
color: var(--c-primary-text);
font-size: 16px;
font-weight: 600;
margin: 5;
@ -98,8 +82,8 @@ if (bond10 === false) {
}
.expand-on-hover {
background-color: var(--c-darkergray);
border: 2px var(--c-darkpurple) solid;
background-color: var(--c-primary-background);
border: 2px var(--c-primary-background) solid;
border-top: 0px;
z-index: 99;
transform: scaleY(0);
@ -112,7 +96,8 @@ if (bond10 === false) {
article:hover .expand-on-hover {
transform: scaleY(1);
transition: transform 200ms ease-in-out;
border-color: var(--c-accent-1);
transition: border-color var(--a-time-default) var(--a-animation-1), transform var(--a-time-default) var(--a-animation-1);
}
.bond-ce {

View file

@ -36,130 +36,103 @@ const servant_images = import.meta.glob<GlobImage>(
const loadedServantImage = plsLoadImage(servant_images, servantImagePath)
const loadedTargetImage = plsLoadImage(target_images, targetImagePath)
let hasuser = ''
if (user !== undefined) {
hasuser = 'hasuser'
}
const hasuser = user !== undefined ? 'display: flex' : 'display: none'
---
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
<article>
<Image src={loadedTargetImage} alt={title} class="icon" />
<div class="imagecontainer">
<Image src={loadedTargetImage} alt={title} />
<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={loadedServantImage} alt="" />
<h2>{turns}</h2>
</div>
</article>
</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>
div {
display: none;
}
span {
display: none;
}
.hasuser {
display: flex;
}
a {
text-decoration: none;
}
article {
background-color: var(--c-darkergray);
border: 2px var(--c-darkgray) solid;
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;
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;
z-index: 1;
}
article:hover {
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-darkpurple);
border-color: var(--c-accent-1);
z-index: 10;
}
article > .icon {
.imagecontainer {
display: flex;
width: 90%;
height: auto;
position: relative;
}
.imagecontainer > img {
width: 100%;
height: auto;
border-radius: 1.25rem;
width: var(--size-value);
height: var(--size-value);
margin: 0.5rem;
}
article:hover .title {
a:hover .title {
display: flex;
position: absolute;
align-items: center;
justify-content: center;
text-align: center;
background-color: var(--c-darkgray);
height: var(--size-value);
width: var(--size-value);
padding: 1px;
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;
top: calc(0.5rem + 9px);
}
article:hover .title h2 {
margin: 0;
a:hover .title h2 {
margin: 4px;
display: inline-flex;
font-weight: bold;
color: white;
font-size: 18px;
font-weight: 500;
color: var(--c-primary-text);
font-size: 1rem;
line-height: 150%;
padding: 0.5rem;
}
article .title h2 {
a .title {
display: none;
}
article .title {
display: none;
}
.icon {
display: flex;
justify-content: center;
align-items: center;
}
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: white;
color: var(--c-primary-text);
font-size: 1rem;
font-weight: bold;
padding-top: 0.5rem;
margin: 0.5rem 0px;
flex-wrap: wrap;
flex-direction: column;
font-weight: 400;
margin: 0.5rem 0px 0.625rem 0px;
}
.expand-on-hover {
@ -168,22 +141,24 @@ if (user !== undefined) {
flex-wrap: wrap;
align-items: top;
justify-content: space-evenly;
background-color: var(--c-darkergray);
border: 2px var(--c-darkpurple) solid;
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: 90%;
color: white;
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: 3rem;
height: 3rem;
width: 2.5rem;
height: auto;
margin-bottom: 0.5rem;
border-radius: 0.5rem;
}
@ -192,9 +167,23 @@ if (user !== undefined) {
margin: 0.5rem;
}
article:hover .expand-on-hover {
a:hover .expand-on-hover {
transform: scaleY(1);
transition: transform 200ms ease-in-out;
background-color: var(--c-darkergray);
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>

View file

@ -10,7 +10,6 @@ export interface Props {
}
const { imageFile, link, title } = Astro.props
const logoAltText = `${title} Logo`
const imagePath = `/src/assets/technologies/${imageFile}`
const images_logos = import.meta.glob<GlobImage>(
'/src/assets/technologies/*.{png,webp}'
@ -20,76 +19,67 @@ const loadedImage = plsLoadImage(images_logos, imagePath)
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
<article>
<Image src={loadedImage} alt={logoAltText} loading={'eager'}/>
<Image src={loadedImage} alt="" loading={'eager'} width={150} height={150} quality={100}/>
<div>
<h2>{title}</h2>
</div>
</article>
<span class="visually-hidden">{logoAltText}</span>
<span class="visually-hidden">{title}</span>
</a>
<style>
a {
text-decoration: none;
}
article {
--size-value: 6.25rem;
background-color: var(--c-darkergray);
border-color: var(--c-darkgray);
padding: 10px;
transition: border-color var(--a-time-default) var(--a-animation-1);
display: flex;
justify-content: center;
position: relative;
align-items: center;
justify-content: 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;
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;
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;
border-style: solid;
border-width: 10px;
border-color: var(--c-darkgray);
}
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;
width: 80px;
height: auto;
}
article div {
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>

View file

@ -1,6 +1,4 @@
---
import '@fontsource/work-sans/500.css'
export interface Props {
fadeout?: boolean
}
@ -12,7 +10,7 @@ const display = fadeout ? "": "display: none"
<span>
I am currently creating a site to catalogue past and future TAs for the whole community.
</span>
<a href=`https://fgo-ta.com/`><span class="fancy">Visit fgo-ta.com &gt;&gt;</span></a>
<a href=`https://fgo-ta.com/` target="_blank" rel="noopener noreferrer"><span class="fancy">Visit fgo-ta.com &gt;&gt;</span></a>
<div class="fade" style={display}></div>
</div>
@ -22,11 +20,11 @@ const display = fadeout ? "": "display: none"
width: 100%;
flex-wrap: nowrap;
flex-flow: column;
background-color: var(--c-darkergray);
background-color: var(--c-primary-background);
text-align: center;
align-items: center;
justify-content: center;
color: white;
color: var(--c-primary-text);
font-size: 1.5em;
padding: 2rem 0rem 0rem 0rem;
font-weight: 400;
@ -35,34 +33,40 @@ const display = fadeout ? "": "display: none"
a {
font-weight: 500;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif;
color: white;
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-duskgray);
background: var(--c-primary-background);
border-radius: 10px;
border: 2px var(--c-darkpurple) solid;
border: 2px var(--c-accent-1) solid;
transition: border-color var(--a-time-default) var(--a-animation-1);
}
a:hover {
border-color: var(--c-purplepink);
border-color: var(--c-accent-1-alt);
}
.fade {
margin-top: 3rem;
background: linear-gradient(to bottom, transparent, var(--c-lightgray));
background: linear-gradient(to bottom, transparent, var(--c-secondary-background));
height: 2.5rem;
width: 100%;
}
span {
margin: 0 .5rem;
}
.fancy {
color: var(--c-darkpurple);
color: var(--c-accent-1);
}
@supports (background-clip: text) {
a:hover .fancy {
background: linear-gradient(125deg, var(--c-darkpurple), var(--c-purplepink), var(--c-reddish) );
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;
}

View file

@ -12,11 +12,11 @@
display: flex;
width: 100%;
height: 5em;
background-color: var(--c-gray);
background-color: var(--c-secondary-background);
text-align: center;
align-items: center;
justify-content: center;
color: white;
color: var(--c-primary-text);
font-size: 1.5em;
}
</style>

View 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>

View 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>

View 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,
}

View file

@ -1,145 +1,37 @@
---
import { Image } from 'astro:assets'
import logo from '../../assets/logo.svg'
import hamburger from 'iconoir/icons/menu.svg'
const hamburger_src_url = `url("${hamburger.src}")`
import NavbarDesktop from './navbarDesktop.astro'
import NavbarLogo from './navbarLogo.astro'
import NavbarMobile from './navbarMobile.astro'
---
<header>
<a href="/" rel="noopener noreferrer" aria-label="Home" role="button">
<Image src={logo} alt="" loading="eager"/>
<span class="visually-hidden">Logo</span>
</a>
<ul class="desktop">
<NavbarLogo/>
<NavbarDesktop>
<slot name="desktop" />
</ul>
<button
class="mobile"
aria-label="Navigation Button"
tabindex="0"
onclick="this.focus()"
role="button"
>
<ul>
<slot name="mobile" />
</ul>
<div class="placeholder"></div>
<div class="hamburger-menu" role="navigation"></div>
</button>
</NavbarDesktop>
<NavbarMobile>
<slot name="mobile" />
</NavbarMobile>
</header>
<style define:vars={{ hamburger_src_url }}>
<style>
header {
z-index: 1000;
position: sticky;
top: 0px;
background-color: var(--c-darkergray);
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-darkpurple) ;
}
header > a {
margin-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-darkergray);
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-darkergray);
border: 0px;
width: 100%;
height: 64px;
}
.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;
}
.hamburger-menu {
mask: var(--hamburger_src_url) no-repeat center;
mask-size: cover;
background-color: white;
width: 2.25rem;
height: 2.25rem;
position: static;
align-self: flex-start;
margin-right: 1rem;
margin-top: 0.825rem;
border-bottom: 2px solid var(--c-accent-1) ;
}
@media (min-width: 1140px) {
.mobile {
display: none;
}
.desktop {
display: flex;
}
header {
height: 64px;
align-items: center;
}
header > a {
padding-top: 0px;
}
}
</style>

View 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>

View file

@ -39,17 +39,19 @@ let extraattributes = navtype === 'mobile' ? { tabindex: '0' } : {}
</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;
width: 200px;
}
li > a {
display: inline-flex;
color: white;
color: var(--c-primary-text);
text-decoration: none;
justify-content: center;
align-items: center;
@ -60,25 +62,31 @@ let extraattributes = navtype === 'mobile' ? { tabindex: '0' } : {}
}
li > a:hover {
color: var(--c-purplepink) !important;
color: var(--c-accent-1-alt) !important;
}
li > a:hover > .icon {
background-color: var(--c-purplepink) !important;
background-color: var(--c-accent-1-alt) !important;
}
.current {
color: var(--c-darkpurple) !important;
color: var(--c-accent-1) !important;
}
.current > .icon {
background-color: var(--c-darkpurple) !important;
background-color: var(--c-accent-1) !important;
}
.icon {
mask: var(--icon_src_url) no-repeat center;
background-color: white;
background-color: var(--c-primary-text);
width: 1.4em;
height: 1.4em;
}
@media (min-width: 304px) {
li {
width: 200px;
}
}
</style>

View 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>

View 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>

View 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>

View file

@ -1,43 +1,44 @@
---
import '@fontsource/work-sans/700.css'
import '@fontsource/work-sans/500.css'
export interface Props {
maintext: string
subtext: string
fadeout?: boolean
baseurl?: string
returnbutton?: boolean
buttontext?: string
}
const { maintext, subtext, fadeout, baseurl, returnbutton } = Astro.props
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}>&lt;&lt; Back to {baseurl}</a>
<a href=`/${baseurl}` style={displayBackButton}>&lt;&lt; Back to {text}</a>
<div class="fade" style={displayFadeout}></div>
</div>
<style>
a {
font-weight: 500;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif;
color: white;
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-lightgray);
background-color: var(--c-secondary-background);
border-radius: 10px;
border-style: solid;
border-width: 2px;
border-color: var(--c-lightgray);
border-color: var(--c-secondary-background);
text-transform: capitalize;
}
a:hover {
border-color: var(--c-darkpurple);
transition: border-color var(--a-time-default) var(--a-animation-1);
border-color: var(--c-accent-1);
}
.wrap {
@ -48,41 +49,44 @@ const displayBackButton = returnbutton ? "": "display: none"
align-items: center;
flex-wrap: wrap;
flex-direction: column;
color: var(--c-lighter);
background-color: var(--c-darkergray);
color: var(--c-primary-text);
background-color: var(--c-primary-background);
}
.fade {
margin-top: 2rem;
background: linear-gradient(to bottom, transparent, var(--c-lightgray));
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: 2rem;
font-size: 3.25rem;
font-weight: 700;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial,
sans-serif;
font-family: 'Work Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
font-size-adjust: 0.52;
line-height: 1.4;
}
.sub {
font-size: 0.75rem;
margin: 0 .5rem;
font-size: 0.9rem;
font-weight: 500;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial,
sans-serif;
margin-top: 0.5em;
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-darkpurple);
color: var(--c-accent-1);
}
@supports (background-clip: text) {
.head {
background: linear-gradient(125deg, var(--c-darkpurple), var(--c-purplepink), var(--c-reddish) );
background: linear-gradient(125deg, var(--c-accent-1), var(--c-accent-1-alt), var(--c-accent-2) );
background-clip: text;
color: transparent;
}
@ -91,10 +95,10 @@ const displayBackButton = returnbutton ? "": "display: none"
@media (min-width: 620px) {
.head {
hyphens: none;
font-size: 3rem;
font-size: 3.75rem;
}
.sub {
font-size: 0.75rem;
font-size: 1rem;
}
}

View file

@ -1,11 +1,12 @@
---
import '@fontsource/work-sans/700.css'
import '@fontsource/work-sans/500.css'
import LinkContainer from "../links/linkContainer.astro";
---
<div class="wrap">
<div class="head">Hi, I'm <span class="fancy">Firq</span></div>
<div class="sub">&lt;/ Software dev / Scathach enthusiast / Germany /&gt;</div>
<LinkContainer />
<div class="fade"></div>
</div>
@ -18,13 +19,13 @@ import '@fontsource/work-sans/500.css'
align-items: center;
flex-wrap: wrap;
flex-direction: column;
color: var(--c-lighter);
background-color: var(--c-darkergray);
color: var(--c-primary-text);
background-color: var(--c-primary-background);
}
.fade {
margin-top: 1.5rem;
background: linear-gradient(to bottom, transparent, var(--c-lightgray));
background: linear-gradient(to bottom, transparent, var(--c-secondary-background));
height: 2.5rem;
width: 100%;
}
@ -33,24 +34,29 @@ import '@fontsource/work-sans/500.css'
padding-top: 2rem;
font-size: 3rem;
font-weight: 700;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial,
sans-serif;
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: 500;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial,
sans-serif;
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-darkpurple);
color: var(--c-accent-1);
}
@supports (background-clip: text) {
.fancy {
background: linear-gradient(125deg, var(--c-darkpurple), var(--c-purplepink), var(--c-reddish) );
background: linear-gradient(125deg, var(--c-accent-1), var(--c-accent-1-alt), var(--c-accent-2) );
background-clip: text;
color: transparent;
}

View file

@ -44,7 +44,7 @@ const { id, thumbnail } = Astro.props
.warningtext {
display: flex;
justify-content: center;
background-color: var(--c-darkgray);
background-color: var(--c-primary-background);
width: 640px;
height: 360px;
}

View file

@ -1,7 +1,8 @@
import { z, defineCollection } from 'astro:content'
import { glob } from 'astro/loaders';
const blogCollection = defineCollection({
type: 'content', // v2.5.0 and later
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/data/blog" }),
schema: z.object({
title: z.string(),
tags: z.array(z.string()),

View file

@ -1,5 +1,4 @@
---
layout: ../../layouts/blogPost.astro
title: 'How Astro powers this site'
pubDate: 2023-03-09
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.
It is structured like this:
```
```yaml
---
layout: ../../layouts/blogPost.astro
title: 'How Astro powers this site'

View file

@ -1,14 +1,13 @@
---
layout: ../../layouts/blogPost.astro
title: 'How to: TA Cernunnos'
pubDate: 2023-07-14
description: 'A handful of observations from the cernunnos fight'
author: 'Requiem & Firq'
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"
import thumbnail_firq from "@assets/thumbnails/WrHudtdfivA.jpg"
import thumbnail_requiem from "@assets/thumbnails/O1f-go7uJQM.jpg"
import YoutubeEmbed from "@components/youtubeEmbed.astro"
## Foreword

View file

@ -1,5 +1,4 @@
---
layout: ../../layouts/blogPost.astro
title: 'Migrating to Forgejo'
pubDate: 2023-12-23
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
RUN rm -r /public/assets/data/
ENV PORT 8081
EXPOSE 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
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:
@ -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).
The `Dockerfile` can be found below (really simple again):
<details>
<summary>`Dockerfile` (really simple again)</summary>
```dockerfile
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
```
</details>
With this container, the CI step to actually run the tests became a lot easier:
```yaml

View file

@ -1,5 +1,4 @@
---
layout: ../../layouts/blogPost.astro
title: 'How to: Deploy this site'
pubDate: 2023-03-17
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.
If displayed as separated shell calls, they would look something like this:
<details>
<summary>commands formatted as a shell script</summary>
```shell
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"
```
</details>
With the following directory structure on the remote host, this can easily be explained:
```
```plain
/
├─ public/
│ ├─ 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.
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
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"
```
</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
without causing any weird issues. But by using a custom ssh-key and config, it became really easy to get this to run.

View file

@ -1,5 +1,4 @@
---
layout: ../../layouts/blogPost.astro
title: 'FGO TA - How, what, why'
pubDate: 2023-03-16
description: 'What FGO TA is, what brought me to it and how you can do it yourself!'

View file

@ -1,5 +1,4 @@
---
layout: ../../layouts/blogPost.astro
title: 'Hello World!'
pubDate: 2023-03-08
description: 'First blog post, talking a bit about the site, the technologies behind it and the future ideas'

View file

@ -1,13 +1,12 @@
---
layout: ../../layouts/blogPost.astro
title: 'FGO Mechanics: Instant Death'
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'
tags: ['fgo', 'games']
---
import thumbnail from "../../assets/thumbnails/UwbNp_dB_VU.jpg"
import YoutubeEmbed from "../../components/youtubeEmbed.astro"
import thumbnail from "@assets/thumbnails/UwbNp_dB_VU.jpg"
import YoutubeEmbed from "@components/youtubeEmbed.astro"
> **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"

View 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.

View file

@ -15,6 +15,12 @@
"name": "Summer Skadi",
"origin": "Fate/Grand Order",
"imageFile": "summerskadi.png",
"link": "https://twitter.com/firq_ow/status/1816355501216596073"
"link": "https://youtu.be/qmR4s3DHZXw"
},
{
"name": "Summer Scathach",
"origin": "Fate/Grand Order",
"imageFile": "summerscathach.png",
"link": "https://youtu.be/k0E3kgx3eMA"
}
]

View file

@ -5,14 +5,14 @@
"icon": "home"
},
{
"link": "/servants",
"text": "Servants",
"icon": "servants"
"link": "/fgo",
"text": "FGO",
"icon": "fgo"
},
{
"link": "/ta-collection",
"text": "TA Collection",
"icon": "ta_collection"
"link": "/projects",
"text": "Projects",
"icon": "projects"
},
{
"link": "/blog",

View 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"
}
]

View 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"
}
]

View file

@ -1,18 +1,18 @@
---
import Navbar from '../components/navbar/navbar.astro'
import NavbarEntry from '../components/navbar/navbarEntry.astro'
import navdata from '../../static/data/_navdata.json'
import embed from '../assets/embed.png'
import '@fontsource-variable/work-sans'
import workSans from '@fontsource-variable/work-sans/files/work-sans-latin-wght-normal.woff2?url'
import home from 'iconoir/icons/home.svg'
import servants from 'iconoir/icons/task-list.svg'
import ta_collection from 'iconoir/icons/database.svg'
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 workSans500 from '@fontsource/work-sans/files/work-sans-latin-500-normal.woff2?url';
import workSans600 from '@fontsource/work-sans/files/work-sans-latin-600-normal.woff2?url';
import workSans700 from '@fontsource/work-sans/files/work-sans-latin-700-normal.woff2?url';
import projects from 'iconoir/icons/network-right.svg'
export interface Props {
title: string
@ -26,8 +26,8 @@ interface IconsLookup {
const icons: IconsLookup = {
home: home,
servants: servants,
ta_collection: ta_collection,
fgo: fgo,
projects: projects,
blog: blog,
about: about,
}
@ -73,12 +73,16 @@ const mapped_navdata = navdata.map((item) => ({
content="SmcWcewh7DCANcLeTe3ntU0R-LESbo_bsolICJnmulE"
/>
<!-- Disable DarkReader, as site is already in dark mode -->
<meta name="darkreader-lock" content="this site only has darkmode">
<meta name="darkreader-lock" content="this site only has darkmode" />
<!-- Links -->
<link rel="preload" as="font" type="font/woff2" href={workSans500} crossorigin="anonymous" />
<link rel="preload" as="font" type="font/woff2" href={workSans600} crossorigin="anonymous" />
<link rel="preload" as="font" type="font/woff2" href={workSans700} crossorigin="anonymous" />
<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 href="https://mastodon.neshweb.net/@Firq" rel="me" />
</head>
@ -108,24 +112,44 @@ const mapped_navdata = navdata.map((item) => ({
<slot />
</body>
</html>
<style is:global>
:root {
--hover-scale: 1.05;
--speed: 50%;
--ease: 50%;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--c-darkgray: #1e1e1e;
--c-duskgray: #242424;
--c-gray: #2e2e2e;
--c-lighter: #eee;
--c-lightgray: #3e3e3e;
--c-darkpurple: #b86cff;
--c-purplepink: #c105ff;
--c-darkergray: #1b1b1b;
--c-reddish: #ff0077;
/* Animations */
--a-time-default: 200ms;
--a-time-short: 100ms;
--a-time-slow: 1000ms;
--a-animation-1: ease-in-out;
/* Fonts */
--f-default: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--f-title: 'Work Sans Variable', system-ui, 'Segoe UI', sans-serif;
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 {
background: var(--c-lightgray);
background: var(--c-secondary-background);
margin: 0px;
}

View file

@ -1,13 +1,15 @@
---
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">
<h1>{title}</h1>
<h1 class={hidden}>{title}</h1>
<div>
<slot />
</div>
@ -27,28 +29,22 @@ const { title } = Astro.props
align-self: center;
display: flex;
flex-flow: row wrap;
padding: 1em;
position: relative;
}
div h1 {
h1 {
font-size: 40px;
line-height: 48px;
letter-spacing: -1px;
color: white;
margin-top: 2rem;
margin-bottom: 0px;
margin-left: auto;
margin-right: auto;
padding: 0.25rem 1.5rem;
border-radius: 0.5rem;
color: var(--c-primary-text);
margin: 0 auto 0px;
padding: 0.25rem 1.5rem 0.5rem;
max-width: max-content;
background-color: var(--c-darkgray);
padding-bottom: 0.5rem;
}
@media (min-width: 512px) {
div {
row-gap: 1.5em;
column-gap: 1.5em;
&:after {
content:' ';
display: block;
width: 65%;
height: 3px;
margin: auto;
background-color: var(--c-accent-1);
}
}

View file

@ -1,9 +1,9 @@
---
import SmallTitle from '../components/titles/smallTitle.astro'
import SmallTitle from '@components/titles/smallTitle.astro'
import Layout from './Layout.astro'
const { frontmatter } = Astro.props
const title = `${frontmatter.title} - Firq FGO Site`
const title = `${frontmatter.title} - firq.dev`
const description = frontmatter.description
const options_date: Intl.DateTimeFormatOptions = {
year: 'numeric',
@ -19,100 +19,85 @@ const subtext = `Written by ${frontmatter.author} • Published on ${date}`
<Layout title={title} currentpage="blog" descriptionOverride={description}>
<SmallTitle maintext={frontmatter.title} subtext={subtext} fadeout={true} returnbutton={true} baseurl='blog'/>
<div>
<article>
<slot />
</article>
</div>
<article>
<slot />
</article>
</Layout>
<style>
a {
display: flex;
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;
article {
color: var(--c-primary-text);
margin: 0.5rem 3rem 0.5rem 3rem;
padding: 0.25rem 0.75rem;
max-width: max-content;
background-color: var(--c-darkgray);
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;
font-size: 1.1em;
text-align: justify;
padding-bottom: 5rem;
}
article :global(h2) {
margin-right: 3rem;
font-size: 32px;
word-wrap: normal;
color: white;
padding: 0.25rem 0.75rem;
color: var(--c-primary-text);
max-width: max-content;
background-color: var(--c-darkgray);
padding: 0.25rem 1rem;
border-radius: 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) {
margin-bottom: 0.5rem;
margin-right: 3rem;
font-size: 24px;
word-wrap: normal;
color: white;
padding: 0.25rem 0.75rem;
color: var(--c-primary-text);
max-width: max-content;
background-color: var(--c-darkgray);
padding: 0.25rem 1rem;
border-radius: 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) {
margin-bottom: 0.5rem;
margin-right: 3rem;
font-size: 18px;
word-wrap: normal;
color: white;
padding: 0.25rem 0.75rem;
color: var(--c-primary-text);
max-width: max-content;
background-color: var(--c-darkgray);
padding: 0.25rem 1rem;
border-radius: 0.5rem;
padding-bottom: 0.5rem;
margin-bottom: 0px;
text-align: left;
line-height: 32px;
&: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) {
color: #dcb7ff;
text-decoration: none;
font-style: italic;
}
article {
color: white;
margin: 0.5rem 3rem 0.5rem 3rem;
font-size: 1.1em;
padding-bottom: 5rem;
}
article :global(.astro-code) {
width: auto;
padding: 1rem 1rem 1rem 2rem;
}
article :global(code) {
font-weight: bold;
color: orange;
@ -120,31 +105,16 @@ const subtext = `Written by ${frontmatter.author} • Published on ${date}`
@media (min-width: 1000px) {
article {
margin-left: 10rem;
margin-right: 10rem;
margin-left: 22.5%;
margin-right: 22.5%;
}
h1 {
margin-left: 10rem;
margin-right: 10rem;
margin-left: 20%;
margin-right: 20%;
}
p {
margin-left: 10rem;
margin-right: 10rem;
}
}
@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;
margin-left: 20%;
margin-right: 20%;
}
}
</style>

View file

@ -42,7 +42,7 @@ const line = displayLine ? "flex" : "none"
bottom: -5rem;
border-radius: 0% 50% 50% 50%;
transform: rotate(45deg);
background-color: var(--c-darkpurple);
background-color: var(--c-accent-1);
}
.start {
@ -50,8 +50,8 @@ const line = displayLine ? "flex" : "none"
border-radius: 40%;
border-style: solid;
border-width: 0.25rem;
border-color: var(--c-lightgray);
background-color: var(--c-darkpurple);
border-color: var(--c-secondary-background);
background-color: var(--c-accent-1);
}
.line {
@ -62,7 +62,7 @@ const line = displayLine ? "flex" : "none"
right: 0;
margin-left: auto;
margin-right: auto;
background-color: var(--c-darkpurple);
background-color: var(--c-accent-1);
height: calc(100% + 5rem);
translate: 0% 2rem;
width: 0.25rem;
@ -71,14 +71,14 @@ const line = displayLine ? "flex" : "none"
h1 {
line-height: 48px;
letter-spacing: -1px;
color: white;
color: var(--c-primary-text);
font-size: 1.6rem;
margin-top: 1rem;
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
max-width: max-content;
background-color: var(--c-darkgray);
background-color: var(--c-primary-background);
padding: 0.25rem 1.5rem;
border-radius: 0.5rem;
padding-bottom: 0.5rem;
@ -95,8 +95,8 @@ const line = displayLine ? "flex" : "none"
align-items: stretch;
align-content: center;
justify-content: space-around;
padding: 1em;
color: white;
padding: 1em 0px;
color: var(--c-primary-text);
font-size: 1em;
position: relative;
}
@ -114,11 +114,14 @@ const line = displayLine ? "flex" : "none"
margin-left: 3rem;
font-size: 1.85rem;
}
}
@media (min-width: 1500px) {
.wrapper {
margin-left: 15rem;
margin-right: 15rem;
padding: 1em;
}
}
@media (min-width: 1100px) {
.wrapper {
margin-left: 20%;
margin-right: 20%;
flex-direction: row;
}
section {
@ -132,7 +135,7 @@ const line = displayLine ? "flex" : "none"
margin-left: 1.75rem;
}
.line {
margin-left: 2.6rem;
margin-left: 2.625rem;
height: calc(100% + 6rem);
translate: 0px -2rem;
}

View file

@ -15,18 +15,23 @@ const { title } = Astro.props
<style>
h1 {
color: white;
font-size: 26px;
color: var(--c-primary-text);
font-size: 24px;
font-weight: 700;
line-height: 32px;
letter-spacing: -1px;
margin: 0.5rem 0px 0.5rem 0.5em;
padding: 0.25rem 0.75rem;
font-family: 'Work Sans Variable', sans-serif;
width: max-content;
background-color: var(--c-darkgray);
padding: 0.25rem 1rem;
border-radius: 0.5rem;
padding-bottom: 0.5rem;
margin: 0 auto 0.5rem;
&:after {
content:' ';
display: block;
width: 75%;
height: 2px;
margin: -1px auto auto;
background-color: var(--c-accent-1);
}
}
div {
row-gap: 1em;
column-gap: 1em;
@ -51,6 +56,14 @@ const { title } = Astro.props
margin-left: 3rem;
margin-right: 3rem;
}
h1 {
margin-left: 0;
margin-right: 0;
&:after {
margin: -2px 0 0;
}
}
}
@media (min-width: 1500px) {
.base {

View file

@ -1,8 +1,8 @@
---
import okita from '../assets/okita.png'
import atlas from '../assets/atlas.png'
import foxcat from '../assets/lurker.png'
import padoru from '../assets/padoru.png'
import okita from '@assets/okita.png'
import atlas from '@assets/atlas.png'
import foxcat from '@assets/lurker.png'
import padoru from '@assets/padoru.png'
import { Image } from 'astro:assets'
import packagejson from '../../package.json'
@ -44,9 +44,9 @@ const release = `https://forgejo.neshweb.net/Firq/firq-dev-website/releases/tag/
rel="noopener noreferrer">Mitsunee</a
> for the support when building this site. I could not have done it without
you 🧡 Check out <a
href="https://fgo.mitsunee.com"
href="https://mitsunee.com"
target="_blank"
rel="noopener noreferrer">FGO Timers here</a
rel="noopener noreferrer">her own website here</a
>
<div class="sticky-image-wrapper">
<Image src={foxcat} alt="Best Foxcat" />
@ -64,7 +64,7 @@ const release = `https://forgejo.neshweb.net/Firq/firq-dev-website/releases/tag/
>his site here</a
>
<div class="sticky-image-wrapper">
<Image src={okita} alt="" style="height:60px" />
<Image src={okita} alt="" style="height:67px" />
</div>
</div>
<br />
@ -75,7 +75,7 @@ const release = `https://forgejo.neshweb.net/Firq/firq-dev-website/releases/tag/
rel="noopener noreferrer">AnthonyJ</a
> for providing me with the custom Shishou favicon.
<div class="sticky-image-wrapper">
<Image src={padoru} alt="Hashire sori yo ..." />
<Image src={padoru} alt="Hashire sori yo ..." width={64} height={64} quality={100}/>
</div>
</div>
<br />
@ -88,11 +88,11 @@ const release = `https://forgejo.neshweb.net/Firq/firq-dev-website/releases/tag/
<style>
footer {
color: white;
color: var(--c-primary-text);
bottom: 0;
}
footer > div > a {
color: white;
color: var(--c-primary-text);
}
.sticky-image-wrapper {
position: fixed;
@ -108,7 +108,7 @@ const release = `https://forgejo.neshweb.net/Firq/firq-dev-website/releases/tag/
bottom: -100px;
height: 64px;
width: 64px;
transition: transform 1s ease-in-out;
transition: transform var(--a-time-slow) var(--a-animation-1);
transform: translateX(-50%);
}
</style>

View file

@ -34,18 +34,19 @@ const { title } = Astro.props
.base h1 {
font-size: 40px;
line-height: 48px;
letter-spacing: -1px;
color: white;
margin-top: 2rem;
margin-bottom: 0px;
margin-left: auto;
margin-right: auto;
padding: 0.25rem 1.5rem;
border-radius: 0.5rem;
color: var(--c-primary-text);
margin: 0 auto 0px;
padding: 0.25rem 1.5rem 0.5rem;
max-width: max-content;
background-color: var(--c-darkgray);
padding-bottom: 0.5rem;
&:after {
content:' ';
display: block;
width: 65%;
height: 3px;
margin: auto;
background-color: var(--c-accent-1);
}
}
@media (min-width: 1000px) {

View file

@ -24,34 +24,36 @@ if (abovetext === undefined) {
<style>
div > div {
row-gap: 1.5em;
column-gap: 1.5em;
display: flex;
position: relative;
flex-flow: row wrap;
row-gap: 1em;
column-gap: 1em;
justify-content: center;
align-self: center;
display: flex;
flex-flow: row wrap;
padding: 1em;
}
h1 {
font-size: 40px;
line-height: 48px;
letter-spacing: -1px;
color: white;
margin-top: 2rem;
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
padding: 0.25rem 0.75rem;
color: var(--c-primary-text);
margin: 0 auto 0px;
padding: 0.25rem 1.5rem 0.5rem;
max-width: max-content;
background-color: var(--c-darkgray);
padding: 0.25rem 1.5rem;
border-radius: 0.5rem;
padding-bottom: 0.5rem;
&:after {
content:' ';
display: block;
width: 65%;
height: 3px;
margin: auto;
background-color: var(--c-accent-1);
}
}
h2 {
color: white;
color: var(--c-primary-text);
font-size: 16px;
font-weight: 600;
font-weight: 400;
margin: 5;
line-height: 20px;
text-align: center;

View file

@ -1,16 +1,14 @@
---
import '@fontsource/work-sans/500.css'
import '@fontsource/work-sans/600.css'
import { Image } from 'astro:assets'
import Layout from '../layouts/Layout.astro'
import sadshishou from '../assets/shishousad.webp'
import SmallTitle from '../components/titles/smallTitle.astro'
import Layout from '@layouts/Layout.astro'
import sadshishou from '@assets/shishousad.webp'
import SmallTitle from '@components/titles/smallTitle.astro'
const description = "Error. This shouldn't happen :/"
---
<Layout
title="404 - Firq FGO Site"
title="404 - firq.dev"
currentpage="404"
descriptionOverride={description}
>
@ -33,9 +31,9 @@ const description = "Error. This shouldn't happen :/"
align-items: center;
}
h2 {
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif;
font-family: 'Work Sans Variable', system-ui, 'Segoe UI', sans-serif;
font-weight: 500;
color: white;
color: var(--c-primary-text);
font-size: 1.25rem;
margin-top: 1rem;
max-width: max-content;
@ -46,22 +44,22 @@ const description = "Error. This shouldn't happen :/"
a {
text-align: center;
width: fit-content;
font-weight: 600;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif;
color: white;
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-darkergray);
background-color: var(--c-primary-background);
border-radius: 10px;
border-style: solid;
border-width: 2px;
border-color: var(--c-darkergray);
border-color: var(--c-primary-background);
text-transform: capitalize;
}
a:hover {
border-color: var(--c-darkpurple);
border-color: var(--c-accent-1);
}
img {

View file

@ -1,17 +0,0 @@
---
import Layout from '../layouts/Layout.astro';
---
<Layout title="Firq FGO Site">
<Navbar/>
<Title/>
<Divider/>
<HomeLayout>
<AboutMe/>
<Favourites/>
</HomeLayout>
</Layout>
<style>
</style>

View file

@ -1,14 +1,13 @@
---
import Layout from '../layouts/Layout.astro'
import Layout from '@layouts/Layout.astro'
import ContactSection from '../layouts/contactSection.astro'
import ContactCard from '../components/cards/contactCard.astro'
import contactdata from '../../static/data/_contactdata.json'
import ContactSection from '@layouts/contactSection.astro'
import CustomFooter from '../layouts/customFooter.astro'
import TechnologyCard from '../components/cards/technologyCard.astro'
import technologydata from '../../static/data/_technologydata.json'
import SmallTitle from '../components/titles/smallTitle.astro'
import CustomFooter from '@layouts/customFooter.astro'
import TechnologyCard from '@components/cards/technologyCard.astro'
import technologydata from '@datafiles/technologydata.json'
import SmallTitle from '@components/titles/smallTitle.astro'
import Placeholder from '@components/placeholder.astro'
const description =
"A summary of the technologies used as well as my contact information. You'll also find disclaimers and thank you notes for the people that helped me."
@ -17,20 +16,18 @@ const subtext =
---
<Layout
title="About - Firq FGO Site"
title="About - firq.dev"
currentpage="about"
descriptionOverride={description}
>
<SmallTitle maintext="About" subtext={subtext} fadeout={true} />
<ContactSection title="Technologies used">
<SmallTitle maintext="About this site" subtext={subtext} fadeout={true} />
<ContactSection title="Powered by">
{technologydata.map((item) => <TechnologyCard {...item} />)}
</ContactSection>
<ContactSection title="Contact me">
{contactdata.map((item) => <ContactCard {...item} />)}
</ContactSection>
<ContactSection title="Disclaimers">
<CustomFooter />
</ContactSection>
<Placeholder height="1.5rem"/>
</Layout>
<style></style>

View file

@ -1,17 +1,20 @@
---
import { getCollection } from 'astro:content'
import BlogPost from '@layouts/blogPost.astro'
import { getCollection, render } from 'astro:content'
export async function getStaticPaths() {
const blogEntries = await getCollection('blog')
const blogEntries = await getCollection('blog')
return blogEntries.map((entry) => ({
params: { slug: entry.slug },
props: { entry },
}))
return blogEntries.map((entry) => ({
params: { slug: entry.id },
props: { entry },
}))
}
const { entry } = Astro.props
const { Content } = await entry.render()
const { Content } = await render(entry)
---
<Content />
<BlogPost frontmatter={entry.data}>
<Content />
</BlogPost>

View file

@ -1,12 +1,14 @@
---
import Layout from '../../layouts/Layout.astro'
import BlogCard from '../../components/cards/blogCard.astro'
import BlogSection from '../../layouts/blogSection.astro'
import SmallTitle from '../../components/titles/smallTitle.astro'
import Layout from '@layouts/Layout.astro'
import BlogCard from '@components/cards/blogCard.astro'
import BlogSection from '@layouts/blogSection.astro'
import SmallTitle from '@components/titles/smallTitle.astro'
import { getCollection } from 'astro:content'
const description =
'My own small blog. Topics include FGO, TA, Programming, web technologies and more!'
const subtext =
'My personal blog. Includes rambling about FGO, coding and other stuff!'
const blogEntries = await getCollection('blog')
blogEntries.sort(
(a, b) =>
@ -15,17 +17,17 @@ blogEntries.sort(
---
<Layout
title="Blog - Firq FGO Site"
title="Blog - firq.dev"
currentpage="blog"
descriptionOverride={description}
>
<SmallTitle maintext="Blog Articles" subtext="" fadeout={true} />
<SmallTitle maintext="Blog Articles" subtext={subtext} fadeout={true} />
<BlogSection title="Blog Articles" displayLine={true} titlehidden={true}>
{
blogEntries.map((post) => (
<BlogCard
url="blog"
slug={post.slug}
slug={post.id}
title={post.data.title}
pubdate={post.data.pubDate}
description={post.data.description}

50
src/pages/fgo.astro Normal file
View file

@ -0,0 +1,50 @@
---
import FavouriteCard from '@components/cards/favouriteCard.astro'
import ProjectCard from '@components/cards/projectCard.astro'
import Placeholder from '@components/placeholder.astro'
import SmallTitle from '@components/titles/smallTitle.astro'
import favouritesdata from '@datafiles/favouritesdata.json'
import BaseSection from '@layouts/baseSection.astro'
import Layout from '@layouts/Layout.astro'
const description =
"Information about my Fate/Grand Order account. This includes TA runs, my personal TA offering and other interesting stuff."
const subtext =
"Fate/Grand Order is a game I really love - here are some of my details you might find interesting or useful"
const projects = [
{
name: "TA Offering",
description: "A listing of servants I can offer for your own TA attempts. Feel free to contact me if you need any of them.",
url: "/servants"
},
{
name: "TA Collection",
description: "A collection of my own TA runs, as well as some community ones. See fgo-ta.com or Youtube for more.",
url: "/ta-collection"
}
]
const data = structuredClone(favouritesdata);
data.map((item) => { if (item.name !== "Summer Scathach") {item.origin = "First 120 on NA"} else {item.origin = "After Forever"} })
---
<Layout
title="FGO - firq.dev"
currentpage="fgo"
descriptionOverride={description}
>
<SmallTitle maintext="FGO Infos" subtext={subtext} fadeout={true} />
<BaseSection title="Highlights">
{data.map((item) => <FavouriteCard {...item} />)}
</BaseSection>
<Placeholder height="1.5rem"/>
<BaseSection title="Infos" titleHidden={true}>
{projects.map((item) => <ProjectCard {...item} />)}
</BaseSection>
<Placeholder height="1.5rem"/>
</Layout>
<style></style>

View file

@ -1,23 +1,30 @@
---
import Layout from '../layouts/Layout.astro'
import BaseSection from '../layouts/baseSection.astro'
import FavouriteCard from '../components/cards/favouriteCard.astro'
import favouritesdata from '../../static/data/_favouritesdata.json'
import Hi from '../components/titles/title.astro'
import AboutText from '@components/aboutText.astro'
import FavouriteCard from '@components/cards/favouriteCard.astro'
import Placeholder from '@components/placeholder.astro'
import Hi from '@components/titles/title.astro'
import favouritesdata from '@datafiles/favouritesdata.json'
import Layout from '@layouts/Layout.astro'
import BaseSection from '@layouts/baseSection.astro'
const description =
'The very own page of Firq for providing informating about TA servants, listing past TA achievements and hosting a blog for talking about FGO, Programming and other stuff'
---
<Layout
title="Home - Firq FGO Site"
title="Home - firq.dev"
currentpage="home"
descriptionOverride={description}
>
<Hi />
<BaseSection title="Favourite Things">
<BaseSection title="About me">
<Placeholder height="0px"/>
<AboutText/>
</BaseSection>
<BaseSection title="Favourites">
{favouritesdata.map((item) => <FavouriteCard {...item} />)}
</BaseSection>
<Placeholder height="1.5rem"/>
</Layout>
<style></style>

28
src/pages/projects.astro Normal file
View file

@ -0,0 +1,28 @@
---
import Layout from '@layouts/Layout.astro'
import ProjectCard from '@components/cards/projectCard.astro'
import projects from '@datafiles/projectdata.json'
import SmallTitle from '@components/titles/smallTitle.astro'
import BaseSection from '@layouts/baseSection.astro'
import Placeholder from '@components/placeholder.astro'
const description =
"My personal projects I am currently working on. Some of them are born from necessity, while others are for uni or similar."
const subtext =
"Usually born out of necessity, but some were actually developed as uni projects for my masters degree."
---
<Layout
title="Projects - firq.dev"
currentpage="projects"
descriptionOverride={description}
>
<SmallTitle maintext="Personal Projects" subtext={subtext} fadeout={true} />
<BaseSection title="Projects" titleHidden={true}>
{projects.map((item) => <ProjectCard {...item} />)}
</BaseSection>
<Placeholder height="1.5rem"/>
</Layout>
<style></style>

View file

@ -1,38 +1,31 @@
---
import Layout from '../layouts/Layout.astro'
import ServantSection from '../layouts/servantSection.astro'
import ServantCard from '../components/cards/servantCard.astro'
import servantdata from '../../static/data/_servantdata.json'
import CeCard from '../components/cards/ceCard.astro'
import cedata from '../../static/data/_cedata.json'
import SmallTitle from '../components/titles/smallTitle.astro'
import CeCard from '@components/cards/ceCard.astro'
import ServantCard from '@components/cards/servantCard.astro'
import Placeholder from '@components/placeholder.astro'
import SmallTitle from '@components/titles/smallTitle.astro'
import cedata from '@datafiles/cedata.json'
import servantdata from '@datafiles/servantdata.json'
import Layout from '@layouts/Layout.astro'
import ServantSection from '@layouts/servantSection.astro'
const description =
'A list of all the servants and ces that Firq can offer up on support for TA.'
---
<Layout
title="Servants - Firq FGO Site"
currentpage="servants"
title="Servants - firq.dev"
currentpage="fgo"
descriptionOverride={description}
>
<SmallTitle maintext='TA Offering' subtext='Servants and CEs I can offer for your TAs' fadeout={true}/>
<SmallTitle maintext='TA Offering' subtext='Servants and CEs I can offer for your TAs' fadeout={true} baseurl='fgo' returnbutton={true} buttontext='FGO'/>
<ServantSection title="Servants">
{servantdata.map((item) => <ServantCard {...item} />)}
{servantdata.map((item, index) => <ServantCard {...item} index={index} />)}
</ServantSection>
<div class="placeholder"></div>
<Placeholder height="2.5rem"/>
<ServantSection title="CEs">
{cedata.map((item) => <CeCard {...item} />)}
</ServantSection>
<div class="placeholder"></div>
<Placeholder height="2.5rem"/>
</Layout>
<style>
.placeholder {
visibility: hidden;
width: 100%;
height: 2.5rem;
}
</style>
<style></style>

View file

@ -5,14 +5,15 @@
// > You could have the notable ones like you do now, but at the bottom, there could be a drop-down or "expand" or "more" or
// > some other section like that which you could click and show the rest
import Layout from '../layouts/Layout.astro'
import Layout from '@layouts/Layout.astro'
import TaSection from '../layouts/taSection.astro'
import TaCard from '../components/cards/taCard.astro'
import tadata from '../../static/data/_tadata.json'
import featured_data from '../../static/data/_featureddata.json'
import FgotaHero from '../components/fgotaHero.astro'
import SmallTitle from '../components/titles/smallTitle.astro'
import TaSection from '@layouts/taSection.astro'
import TaCard from '@components/cards/taCard.astro'
import tadata from '@datafiles/tadata.json'
import featured_data from '@datafiles/featureddata.json'
import FgotaHero from '@components/fgotaHero.astro'
import Placeholder from '@components/placeholder.astro'
import SmallTitle from '@components/titles/smallTitle.astro'
const important_data = tadata.filter(function (el) {
return [
@ -34,11 +35,11 @@ const description = 'A collection of TAs previously completed be Firq.'
---
<Layout
title="TA Collection - Firq FGO Site"
currentpage="ta-collection"
title="TA Collection - firq.dev"
currentpage="fgo"
descriptionOverride={description}
>
<SmallTitle maintext='TA Collection' subtext=''/>
<SmallTitle maintext='TA Collection' subtext='' baseurl='fgo' returnbutton={true} buttontext='FGO'/>
<FgotaHero fadeout={true}/>
<TaSection title="Notable TAs" abovetext="My most notable TAs">
{important_data.map((item) => <TaCard {...item} />)}
@ -52,13 +53,7 @@ const description = 'A collection of TAs previously completed be Firq.'
>
{featured_data.map((item) => <TaCard {...item} />)}
</TaSection>
<div class="placeholder"></div>
<Placeholder height="5rem"/>
</Layout>
<style>
.placeholder {
visibility: hidden;
width: 100%;
height: 5rem;
}
</style>
<style></style>

View file

@ -1,32 +0,0 @@
[
{
"site": "Youtube: @Firq_",
"link": "https://www.youtube.com/@Firq_",
"imageFile": "youtube.webp"
},
{
"site": "Twitch: Firq",
"link": "https://www.twitch.tv/firq",
"imageFile": "twitch.webp"
},
{
"site": "Discord: firq",
"link": "https://discord.com/users/186014064835690496",
"imageFile": "discord.webp"
},
{
"site": "Twitter: Firq_ow",
"link": "https://twitter.com/firq_ow",
"imageFile": "twitter.webp"
},
{
"site": "Reddit: u/Firq_ow",
"link": "https://www.reddit.com/user/firq_ow",
"imageFile": "reddit.webp"
},
{
"site": "Support Setup on Rayshift",
"link": "https://rayshift.io/na/firq",
"imageFile": "custom-fgo.webp"
}
]

View file

@ -1,7 +0,0 @@
user-agent:*
Disallow: /assets/data/
User-agent: GPTBot
Disallow: /
Sitemap: https://firq.dev/sitemap-index.xml

View file

@ -1,3 +1,12 @@
{
"extends": "astro/tsconfigs/strict"
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@assets/*": [ "src/assets/*" ],
"@components/*": [ "src/components/*" ],
"@datafiles/*": [ "src/data/datafiles/*" ],
"@layouts/*": [ "src/layouts/*" ]
}
}
}

View file

@ -1,21 +1,52 @@
export default {
import type { UserConfig } from '@unlighthouse/core'
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: 'simulate',
throttling: {
rttMs: 150,
throughputKbps: 1638.4,
requestLatencyMs: 562.5,
downloadThroughputKbps: 1474.56,
uploadThroughputKbps: 675,
cpuSlowdownMultiplier: 4,
},
screenEmulation: {
mobile: true,
width: 412,
height: 823,
deviceScaleFactor: 1.75,
disabled: false,
},
skipAudits: [ 'is-on-https', 'redirects-http', 'uses-http2' ],
},
puppeteerOptions: {
args: [
"--no-sandbox",
"--disable-setuid-sandbox"
],
args: [ '--no-sandbox', '--disable-setuid-sandbox' ],
},
puppeteerClusterOptions: {
maxConcurrency: 1
maxConcurrency: 1
},
ci: {
budget: 50,
buildStatic: true
budget: 50,
buildStatic: true,
},
scanner: {
device: 'mobile',
sitemap: true,
throttle: false,
sitemap: false,
dynamicSampling: false,
samples: 1,
},
outputPath: "unlighthouse-reports",
outputPath: 'unlighthouse-reports',
cache: true,
urls
}
}