Major redesign and rewrite

This commit is contained in:
Firq 2024-07-19 22:52:15 +02:00
parent 9cb6ff6ed7
commit 0dca43eb19
Signed by: Firq
GPG key ID: 3ACC61C8CEC83C20
41 changed files with 3863 additions and 4889 deletions

View file

@ -1,8 +1,11 @@
---
import '@fontsource/work-sans/500.css'
import '@fontsource/work-sans/600.css'
import { Image } from 'astro:assets'
import Layout from '../layouts/Layout.astro'
import BaseSection from '../layouts/baseSection.astro'
import sadshishou from '../assets/shishousad.webp'
import SmallTitle from '../components/titles/smallTitle.astro'
const description = "Error. This shouldn't happen :/"
---
@ -12,7 +15,7 @@ const description = "Error. This shouldn't happen :/"
currentpage="404"
descriptionOverride={description}
>
<BaseSection title="FirqhundredandFirq - Not Found">
<SmallTitle maintext='Error' subtext='FirqhundredandFirq - Not Found' fadeout={true}/>
<div>
<Image src={sadshishou} alt="Sad Shishou" />
<h2>Well ... you were not supposed to end up here.</h2>
@ -20,44 +23,51 @@ const description = "Error. This shouldn't happen :/"
&lt;&lt; Go back home
</a>
</div>
</BaseSection>
</Layout>
<style>
div {
padding: 0px 2rem;
display: flex;
flex-wrap: wrap;
flex-wrap: nowrap;
flex-direction: column;
align-items: center;
}
h2 {
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif;
font-weight: 500;
color: white;
font-size: 2rem;
font-weight: bold;
font-size: 1.25rem;
margin-top: 1rem;
max-width: max-content;
}
a {
display: flex;
align-items: center;
justify-content: center;
width: 75%;
text-align: center;
color: white;
background-color: var(--c-gray);
padding: 0.5rem 0px;
margin-bottom: 2rem;
text-decoration: none;
font-size: 1.5rem;
font-weight: bold;
font-style: italic;
}
&:hover {
color: var(--c-darkpurple);
}
a {
text-align: center;
width: fit-content;
font-weight: 600;
font-family: 'Work Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif;
color: white;
margin: 1rem 0px 0px;
padding: 0.5rem 0.75rem;
text-decoration: none;
background-color: var(--c-darkergray);
border-radius: 10px;
border-style: solid;
border-width: 2px;
border-color: var(--c-darkergray);
text-transform: capitalize;
}
a:hover {
border-color: var(--c-darkpurple);
}
img {
width: 256px;
height: 256px;
width: 200px;
height: auto;
border-radius: 10%;
}
</style>

View file

@ -1,17 +1,19 @@
---
import Layout from '../layouts/Layout.astro'
import AboutSection from '../layouts/aboutSection.astro'
import ContactSection from '../layouts/contactSection.astro'
import ContactCard from '../components/contactCard.astro'
import ContactCard from '../components/cards/contactCard.astro'
import contactdata from '../../static/data/_contactdata.json'
import CustomFooter from '../layouts/customFooter.astro'
import TechnologyCard from '../components/technologyCard.astro'
import TechnologyCard from '../components/cards/technologyCard.astro'
import technologydata from '../../static/data/_technologydata.json'
import SmallTitle from '../components/titles/smallTitle.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."
const subtext =
"This is a small sideproject that I'm creating. First time doing webdev in general, and first project using Typescript."
---
<Layout
@ -19,10 +21,7 @@ const description =
currentpage="about"
descriptionOverride={description}
>
<AboutSection title="About">
This is a small sideproject that I'm creating. First time doing webdev in
general, and first project using Typescript.
</AboutSection>
<SmallTitle maintext="About" subtext={subtext} fadeout={true} />
<ContactSection title="Technologies used">
{technologydata.map((item) => <TechnologyCard {...item} />)}
</ContactSection>

View file

@ -1,35 +0,0 @@
---
import Layout from '../layouts/Layout.astro'
import BlogCard from '../components/blogCard.astro'
import BlogSection from '../layouts/blogSection.astro'
const description =
'My own small blog. Topics include FGO, TA, Programming, web technologies and more!'
const allPosts = await Astro.glob('../pages/blog/*.{md,mdx}')
allPosts.sort(
(a, b) =>
Date.parse(b.frontmatter.pubDate) - Date.parse(a.frontmatter.pubDate)
)
---
<Layout
title="Blog - Firq FGO Site"
currentpage="blog"
descriptionOverride={description}
>
<BlogSection title="Blog Articles">
{
allPosts.map((post) => (
<BlogCard
url={post.url}
title={post.frontmatter.title}
pubdate={post.frontmatter.pubDate}
description={post.frontmatter.description}
author={post.frontmatter.author}
/>
))
}
</BlogSection>
</Layout>
<style></style>

View file

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

View file

@ -1,106 +0,0 @@
---
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'
author: 'Firq'
tags: ['astro', 'coding']
---
## What is Astro
<a href="https://astro.build/" target="_blank" rel="noopener noreferrer">Astro</a> is a new type of all-in-one web framework, generally designed for speed. It's fascinating because it manages to integrate popular frameworks like `react` or `vue` while staying lightweight. It's really nice to use, especially coming from plain HTML + CSS.
For a fast overview of Astro, look no further than <a href="https://www.youtube.com/watch?v=dsTXcSeAZq8" target="_blank" rel="noopener noreferrer">Astro in 100 Seconds</a> by <a href="https://www.youtube.com/@Fireship" target="_blank" rel="noopener noreferrer">Fireship</a>
## Why I like Astro
To be honest, the best way is to just show a bit of code:
With the following lines, I create the homepage of my website (I am omitting any imports)
```astro
<Layout
title="Home - Firq FGO Site"
currentpage="home"
descriptionOverride={description}
>
<Hero />
<BaseSection title="Favourites">
{favouritesdata.map((item) => <FavouriteCard {...item} />)}
</BaseSection>
</Layout>
```
Instead of having a huge amount of HTML, I instead have only a layout and some components to put the page together. What's also amazing is the fact that instead of declaring each of the favourites cards separately, I can just use a JSON file with all needed parameters and map it to the component.
In addition, managing blogposts is really convenient: instead of creating a page for each component, I can just use the following:
```astro
---
const allPosts = await Astro.glob('../pages/blog/*.md')
---
<Layout
title="Blog - Firq FGO Site"
currentpage="blog"
descriptionOverride={description}
>
<BlogSection title="Blog Articles">
{
allPosts.map((post) => (
<BlogCard
url={post.url}
title={post.frontmatter.title}
pubdate={post.frontmatter.pubDate}
description={post.frontmatter.description}
/>
))
}
</BlogSection>
</Layout>
```
This imports all the markdown files from the `pages/blog` directory and then maps them to individual link cards. In the background, astro is formatting the markdown and creating a route under the `/blog` endpoint.
Also, since I want to have my blogposts sorted by date, the following line rearranged the post order before continuing:
```typescript
allPosts.sort(
(a, b) =>
Date.parse(b.frontmatter.pubDate) - Date.parse(a.frontmatter.pubDate)
)
```
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:
```
---
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"
author: "Firq"
tags: ["astro", "coding"]
---
```
This would for example be the `frontmatter` of this very post. The layout defines how the post will be rendered, which is also just an Astro layout.
And for styling the markdown itself, you would use the `:global()` css property that Astro introduces, since you have no structured HTML to refer to when creating the layout.
For example, the code blocks are styled like this:
```css
article :global(.astro-code) {
width: auto;
padding: 1rem 1rem 1rem 2rem;
}
```
But that's enough of me talking about how awesome I find Astro. I'm really looking forward what kind of developments I can achieve with this in the future.
**~ Firq**
_next blog-post will probably be FGO-related_

View file

@ -1,66 +0,0 @@
---
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"
## Foreword
A big thank you to Requiem who came up with most of these facts while TAing the fluffy boy. I am just here to convert this into a more vieweable format, as well as adding some of my own observations to it.
## Curse stacks
On Turn 1, Cernunnos has 3 actions. Each attacking action has a 30% chance to get a new stack of curse. If Cernunnos gains a curse stack with an attack during the first two actions, Cernunnos stops and does not perform the third action.
The important thing is that he can't have more than 10 stacks of curse. Once Cernunnos reaches 10 stacks, 3 curse stacks are released. This means the minimum amount of curses he can have by Turn 4 is 9 stacks.
Ideally, you don't leave him at 10 stacks by the end of turn 3. Otherwise, Cernunnos won't release 3 stacks, as the corresponding skill cannot be cast.
If you successfully manage to dodge the random curses gained by attacks, you can go for 2 brave chains and 2 attacks to get the minimum amount of curses for turn 4. One of his skills let him eat his own curse, it happens rarely but can save your run.
### Curse gains
* At the end of each turn 1 stack of curse is gained
* The fight starts with Cernunnos getting 7 stacks of curse
* Turn 3 five stacks of curses are gained
* Turn 4 seven more curse stacks are gained
## Fight effects
Turn 2 Cernunnos starts with casting irremovable skill and np seal. This does not affect backline party members, so if you manage to cycle supports on the first attack turn you have their full set of skills to use.
## Actions
* Turn 1: Cernunnos has three actions if no curse stack is gained by attacking, otherwise two actions
* Turn 2: Only two actions, meaning Cernunnos can attack only twice.
* Turn 3: He again has only two actions
* Turn 4: Again, only two actions
## Skill Cooldowns
On the first break Cernunnos skill seals your servants. This means on turn 2 your servant won't lose 1 cooldown on that turn.
As an example:
Let's say I used Alteras third skill turn 1. She has to wait 5 turns to reuse it and on turn 3 Vitch gives her 2 times cooldown reduction.
Ideally you would be able to reuse her third skill on turn 4 but because she gets skill sealed she can't do it.
## Resulting runs
Both Requiem and I managed to get unique TAs done with Scathach (Requiem even with more servants). Those runs are embedded here:
### Firq
This run doesn't use the support Castoria provided by the game and instead makes use of Sherlock instead.
<YoutubeEmbed id="WrHudtdfivA" thumbnail={thumbnail_firq.src}/>
### Requiem
A frontline only run with double Castoria
<YoutubeEmbed id="O1f-go7uJQM" thumbnail={thumbnail_requiem.src}/>

View file

@ -1,164 +0,0 @@
---
layout: ../../layouts/blogPost.astro
title: 'Migrating to Forgejo'
pubDate: 2023-12-23
description: 'My short recollection of migrating this site + CI to Forgejo'
author: 'Firq'
tags: ['astro', 'docker', 'forgejo']
---
Before I begin this, I want to give a shoutout to Neshura - without him, I would never have had the opportunity to actually create and host my website. I also want to thank him for his patience when I was stuck multiple times during the migration process, as my experience with Forgejo and its CI was really limited.
## Migrating Git - Gitlab to Forgejo
The migration of the git files themselves went a lot smoother than expected. Main reason for this was Forgejos built-in migration tool suite, which lets you migrate external repositories with ease.
Generally, the workflow was as follows:
1. Open Forgejo and start a new migration
2. Select GitLab from the list
3. Fill the details of the migration: Repository URL, Access token, etc.
4. Hit Migrate
And voila: The repository with all extra features like the wiki is getting migrated automatically.
In total, I did this for 7 repositories without issues. Afterwards, it was just a matter of updating the remote repository url for my local clones:
```shell
git remote set-url origin https://forgejo.neshweb.net/Firq/firq-dev-website.git
```
Since the underlying git repository didn't change, migration was painless on the client side of things.
## Reworking CI
One of the major parts of the migration was to port my working GitLab-CI to Forgejo Actions. As I never worked with this before (or with Github Actions, which is the basis of Forgejo), it was ... interesting to say the least.
I stumbled over multiple issues while transforming my old `.gitlab-ci.yml` file into a new `build_release.yml`, but in the end I got there. My main issue was that I imagined the CI just to work like GitLab - but I was mistaken.
My main points of confusion were:
- Files are not checked out automatically - A manual run of the checkout action is necessary, otherwise the files will just be missing
- The working directory is not preserved between different run commands (made apparent early on)
But after around 2 hours of tinkering, I managed to get everything working (and even improved some steps, such as automatically getting the known_hosts instead of hardcoding it into the secrets). But still, it felt a bit unsatisfying, as I was still relying on a ssh-connection with rsync to deploy the files. Unbeknownst to me, this was about to change drastically ...
## Such Innovation: Moving from dedicated website server to docker containers
After migrating and finishing up, Neshura showed me how he deploys the main website of his server: With a docker container. After seeing how much easier this would be in the long run, I decided to just go for it and switch from building the static files and syncing them to a webserver to just build my own container.
Generally, this turned out to be a lot easier than expected: I added a Dockerfile to my repo and switched the CI to build a container based on that, which then would get published to the Forgejo registry.
The `Dockerfile` itself is rather simple:
```dockerfile
FROM node:lts AS build
WORKDIR /app
COPY . .
RUN npm i
RUN npm run build
FROM forgejo.neshweb.net/ci-docker-images/website-serve:latest AS runtime
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" ]
```
As you can see, I am using a custom container for the runtime stage, which will be explained in the next section.
### 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.
The `Dockerfile` for that one is laughable simple:
```dockerfile
FROM node:lts
RUN npm install --global "@warren-bank/serve"
```
The container is also published to the docker registry that the Forgejo instance provides, which allows referencing it easily during runs.
## Deployments using Dockge
Since my website is now using a docker container instead of the previous `rsync` + `screen` approach, a new deployment solution was needed.
In the end, Neshura proposed to use <a href="https://github.com/louislam/dockge" target="_blank" rel="noopener noreferrer" >Dockge</a>, a new, simple container management tool build by the developer of the beloved uptime-kuma. With that set up, getting the website only was really really easy:
1. Create a new stack
2. Add a container entry
3. Fill the new entry with the url of the website container on the registry
4. Configure the ports
5. Hit start
After that, it was just a matter of pointing nginx to the new IP address and port that Dockge uses. Just like that, the website was online.
But this still wasn't the end of my migration tasks.
## Unlighthouse - Implementing website testing without worries
Before I even planned to migrate to Forgejo, I had long implemented some simple site benchmarking using the `Unlighthouse` package. This required a separate instance of my site to be running for benchmarking, as I wanted to test the site before pushing any changes to the main domain.
The same principle now applies here: I can push changes to my dev branch and build a preview container by pushing a preview tag. Once that's build, I can deploy the preview using Dockge to a staging environment.
With that set up, I can push a new tag with a keyword for unlighthouse to run. The reports will then be uploaded onto the old webserver, as I don't want to build extra containers just for the reports.
Implementing this proved a lot easier than expected, but sadly I got disappointed when trying to circumvent the staging environment, as the container wouldn't work as a service in Forgejo actions.
### Dedicated unlighthouse docker
One last hurdle was, ironically, Gitlab. Specifically, it was their `lighthouse` container I used for testing. The main issue was that this container is run as a non-privileged user. In general, this is not a problem. However, this prevented me from actually cloning my repository using Forgejo actions.
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):
```dockerfile
FROM node:20.10.0-bookworm
LABEL authorname="firq"
WORKDIR /unlighthouse
ENV CHROMIUM_VERSION="120.0.6099.109-1~deb12u1"
ENV NODE_ENV='production'
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 puppeteer
```
With this container, the CI step to actually run the tests became a lot easier:
```yaml
jobs:
unlighthouse:
runs-on: docker
container: forgejo.neshweb.net/ci-docker-images/unlighthouse:latest
steps:
- name: Checkout repository
uses: https://code.forgejo.org/actions/checkout@v3
- name: Run unlighthouse
run: unlighthouse-ci --site "https://preview.firq.dev/"
- name: Prepare artifacts
run: cp serve.json unlighthouse-reports
- name: Upload reports
uses: actions/upload-artifact@v3
with:
name: unlighthouse-reports
path: unlighthouse-reports/
```
Once the tests ran, the artifacts would be uploaded to the website-server for the time being.
## Conclusion
In the end, I must say migrating was a lot more painless than expected. Sure, Forgejo is missing some of the features that Gitlab offers (mainly YAML anchors and manual CI actions, which hopefully will be implemented in the near future). But at the end of the day, it actually feels refreshing to now have a stable and independent CI to deploy this site, without having to construct weird solutions to self-inflicted problems.
I also updated my about page to now reflect the migration, as the old technologies weren't up-to-date anymore.
If you want to check out the repository by yourself, feel free to do so. <a href="https://forgejo.neshweb.net/Firq/firq-dev-website" target="_blank" rel="noopener noreferrer" >It is available on Neshuras Forgejo instance</a>

View file

@ -1,225 +0,0 @@
---
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'
author: 'Firq'
tags: ['astro', 'gitlab', 'linux']
---
After spending the earlier half of the day getting familiar with new commands and concepts like artifacts,
I am pleased to announce that I managed to rewrite my deployment process.
Let's take a look, shall we?
## The previous setup
Since this site is being developed with Astro and served statically, I need to build it before being able to serve it. So running `npm install` and `npm build` is
given, and that's what the _really old_ GitLab Pages setup did. Back then, the `gitlab-ci.yml` looked like this:
```yaml
image: node:lts
pages:
cache:
paths:
- node_modules/
script:
- npm install
- npm run build
artifacts:
paths:
- public
only:
- main
```
_Funnily, the `node:lts` is a change I made to the Astro docs after Version 2.0 released._
This setup just used a `node` docker image for building and publishing the files to the path `public`, where GitLab Pages then serves them.
But after migrating to the custom configuration using `npx serve`, the pipeline needed to be adjusted. The new pipeline moved building from the docker container
to the proxmox instance where the site is hosted.
The new pipeline looked like this:
```yaml
deploy-site:
stage: deploy
rules:
- if: $CI_COMMIT_BRANCH == "main"
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "Connecting to proxmox machine"
- ssh $DEPLOY_USER@$DEPLOY_HOST -o StrictHostKeyChecking=no IdentitiesOnly=yes " .... "
```
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:
```shell
screen -X -S website-firq-npx kill;
rm -r -f public/*;
cd build;
git reset --hard;
git pull;
rm -r -f node_modules;
npm install;
npm run build;
rm -r public/assets/data/;
cp -R public ~;
cp serve.json ~/public;
cd ~;
screen -S website-firq-npx -dm npx serve public/ -p 9000 -c serve.json"
```
With the following directory structure on the remote host, this can easily be explained:
```
/
├─ public/
│ ├─ site content
│ ├─ serve.json
├─ build/
│ ├─ node_modules/
│ ├─ .git
│ ├─ repository content
```
<details>
<summary>Small explanation of the serve.json</summary>
---
The `serve.json` is the config-file of the `npx serve` command that controls the behavior of the files served by it. In my case, I use if for two entries:
```json
{
"directoryListing": ["/!assets/**"],
"headers": [
{
"source": "**/*.@(jpg|jpeg|gif|png|webp)",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache"
}
]
}
]
}
```
This redirects any non-file access requests to <a href="https://firq.dev/404" target="_blank" rel="noopener noreferrer">my custom 404 page</a>, as I don't want people to be able to use the npx UI for navigating my file system. It also controls the cache for the image-files, which I still need to change in the future.
---
</details>
Upon starting the pipeline, the old site gets taken offline and deleted. Afterwards, the repository in `build` is reset and pulled. Then, as previously, `npm install`
and `npm run build` are executed, the finished data is then moved to the public folder before getting served.
But after running with this setup for a while, I noticed its shortcomings when it comes to easily expanding: The command being so long meant the code had really bad visibility and was pretty much obfuscated. So, I wanted to change that up. How I did it follows now.
## The new setup
The first step was splitting up the pipeline into 2 stages instead of one. This meant a new `stages` entry needed to be added to the `yaml`:
```yaml
stages:
- build
- deploy
- notification
```
Note: The `notification` stage is not developed by me, but by the great folks over at the <a href="https://github.com/DiscordHooks/gitlab-ci-discord-webhook" target="_blank" rel="noopener noreferrer">DiscordHooks Project</a>. By using their `gitlab-ci-discord-webhook`, I can send the status of my pipelines to any amount of discord servers. Look at their repo if you want to integrate it into your own pipelines.
### Building first
This meant the upper portion of the `.gitlab-ci,yml` looks like the following:
```yaml
build-site:
image: node:lts
stage: build
cache:
paths:
- node_modules/
only:
- main
script:
- npm install
- npm run build
- rm -r public/assets/data/
- cp serve.json public
artifacts:
paths:
- public
expire_in: 1 day
```
This stage then builds the site, moves the serve config, removes the data folder and puts the output as an artifact into `public`. It is pretty obvious that this works similar to the old GitLab Pages pipeline, but instead of invoking the pages afterwards, the data just gets stored for a later stage.
After completing, the new stage begins.
### rsync heaven
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:
```yaml
deploy-site:
stage: deploy
only:
- main
before_script:
- 'which rsync || ( apk update && apk add rsync )'
- 'which ssh-agent || ( apk update && apk add openssh-client)'
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' >> ~/.ssh/key_firq
- chmod 600 ~/.ssh/key_firq
- echo "Host $DEPLOY_HOST" >> ~/.ssh/config
- echo $'\n\tIdentityFile ~/.ssh/key_firq' >> ~/.ssh/config
- echo $'\n\tStrictHostKeyChecking no\n\tIdentitiesOnly yes\n' >> ~/.ssh/config
- chmod 644 ~/.ssh/config
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- ls public
- ssh $DEPLOY_USER@$DEPLOY_HOST "screen -X -S website-firq-npx kill; rm -r -f public/*;"
- rsync -az --stats public $DEPLOY_USER@$DEPLOY_HOST:~/.
- ssh $DEPLOY_USER@$DEPLOY_HOST "screen -S website-firq-npx -dm npx serve public/ -p 9000 -c serve.json"
```
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.
Funnily enough, this also taught me how to squash commits on main, as I had like 30 commits where I pretty much changed single lines just for debugging. _I'll use a feature branch next time, that's for sure._
```shell
git rebase -i origin/main~30 main
git push origin +main
```
## Finally, a result
After getting it all to work, I was really pleased how well this whole chain works. In the future, I'll probably tie the pipeline to a tag push instead of pushes on main, but this can wait.
Also: I can only recommend that anyone working with GitLab uses <a href="https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow" target="_blank" rel="noopener noreferrer">their workflows extension in VSCode</a> . This makes debugging faster, as you can easily validate the yaml in Code, and you can observe running pipelines and merge requests from the editor itself without switching tabs.
All in all, I am really happy with this improvement, and know that developing this site became a lot easier now
Thanks for reading,
**~ Firq**

View file

@ -1,196 +0,0 @@
---
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!'
author: 'Firq'
tags: ['fgo', 'games']
---
TA is a central point about my experience of FGO. I am actively doing my own TAs since January 2022, focusing mainly on Shishou TA. However,
I sometimes play around with other servants like Melt Lancer, Skadi, Tomoe or Welfares.
## How did I get to TA
Welllll .... blame <a href="https://youtube.com/@requiemta" target="_blank" rel="noopener noreferrer">@Requiem</a>. No, to be honest, I randomly started with TA in January 2023 when I wanted to try out something different for clearing Challenge Quests. I quickly started using Shishou for my TAs, and slowly learning the ins and outs of TA over the course of the CQs in 2022. Other players like Requiem or KOG were a great help in guiding me, as well as the people from Atlas Academy.
## What is TA
TA, short for **T**urn **A**ttack, is a form of completing challenging content in as little turns as possible. This means in most cases to break a bar each turn or defeat all enemies on screen in that turn. It differs from general farming, as TA is mostly associated with challenging content in FGO, while clearing farming nodes in 3 Turns is just considered 3-turing.
### Types of TA
- **Single-Core**: The simplest form of TA, using a single main DPS with supports
- **Multi-Core**: A variation which uses multiple attackers
- **No Duplicates**: Each servant is only allowed once. This includes supports
- **Frontline Only / No Swap**: The amount of servants is limited to the frontline (3 servants max)
- **No CE**: The DPS doesn't use a CE in general
- **No Event CE**: A variation of No CE which only excluded Event Damage CEs
- **No NP**: The DPS doesn't use its NP for clearing
## How to TA
This section is in no way perfect, it can however serve as a guideline for either getting into TA or expanding your general knowledge of the game. Be aware tho that, depending on the targeted node and enemies as well as gimmicks, this advice can sometimes not be applicable (see for example Challenge Quests with a different taunt behavior like the slapstick museum CQ).
### Quests
In general, most quests can be TA'd, which makes it pretty universal in FGO. But generally, there are four main categories of quests that get cleared in TA:
- **Challenge Quests**: The High-Difficulty nodes in most events, they usually are repeatable and grant a Lore as clear reward
- **Exhibition Quests**: Special High-Difficulty quests associated with special Lotto events like Nero-/Gil-/Ishtar-Fest
- **Memorial Quests**: Quests available as a replay of previous High-Difficulty Quests, open either in a Main Interlude or for special events
- **Special Story Quests**: Notable Quests from the main story that can be TA'd
This is by no means a restrictive list, everyone is encouraged to try to TA everything. But most of the time, the run is done in one of these types of quests.
### Servant
So who can TA ... well, as it stands, most servants available can TA, but not everyone can TA everything.
I chose Shishou for TA for multiple reasons:
- **She is my Waifu**
- **Her best support (Skadi) is also my Waifu**
- Great Single Target damage
- She has a special damage mod against 2 common traits (divine and undead)
- She has cool animations and a cute costume <sub>_pyon pyon~_</sub>
However, the stun on her NP as well as her bad gains and hitcounts make it pretty hard at times to get the best results. This doesn't stop me from trying tho 💜💜💜
### Supports
There are a lot of supportive servants that can be used for TA. General supports like Waver, specialized supports like Skadi or unusual supports like
Hans. In general, most servants with the ability to buff others can be used for TA, but some are generally better than others.
Typical SSR supports include Merlin, Skadi, Castoria, Waver, Reines, Van Gogh, Nero (Bride), while typical SR supports are Nightingale (Santa), Elizabeth,
Jane or Wu Zetian. Most of the named supports can be found on the <a href="https://firq.dev/servants" target="_blank" rel="noopener noreferrer">Servants Page</a> of
my site.
Other interesting supports that I sometimes use are:
- **Chiron**: A great general support with a multi-color-buff, critical stars and critical damage buffs
- **Bartholomew**: One of the best quick supports if you are on budget ... multiple different buff types and low HP really go a long way
- **Bunyan**: Since her release, she has been a great support because of buster buff and her low HP.
But depending on the niches that need to be filled during a CQ, other supports are gonna be used in TA (for example Ibaraki for her debuff cleanse and critical
buff).
### CEs
CEs play an important role in supporting or buffing the main TA servant. Some CEs even make the whole Clear possible, as without their effects a lack of damage or special gimmicks would prevent it. In the following paragraph, I will try to highlight some of the most important CEs for TA. For reference, you can
look at the <a href="https://firq.dev/servants" target="_blank" rel="noopener noreferrer">CE Section of my Servants Page</a>.
#### Taunt
Taunt CEs enable one of the most important parts of TA: Cycling supports. Giving a low-HP support a taunt CE ensures that enemies will only attack this servant, making them die faster than by just relying on Lady Luck. Sadly, there are only two options for taunt CEs: Poster Girl and Outrage. Luckily, Outrage can be exchanged for from the RP shop.
#### Starbombs
Getting a burst of stars either at the start of the TA or when a support gets cycled in after some turns is a gamechanger when needing to crit. As gaining stars just from attacks isn't enough most of the time, these CEs enable a predictable amount of stars at specific points in the run. They work even better when combined with servants that have dependencies on stars like Jane.
#### Bond CEs
Getting a servant to Bond 10 usually takes quite a while (unless your name is WhiteFury). But for the supports, their effects usually are worth it. Skadi for example gives an extra 10% Quick Buff and 15% Critical Damage as long as she's on field.
In general, these CEs can easily be used to passively buff the DPS servant a bit further.
#### Other
Some other CEs that are used a lot in TA:
- **Black Grail**: Best NP damage CE in the game, the demerit of 500 damage/turn can usually be ignored as the quest is completed before the amount of damage becomes too much
- **Holy Night Supper**: A general BiS CE for servants, as 50% starting Gauge, 15% NP Damage and 15% Critical Damage go a long way in TA
- **Devilish Bodhisattva**: Nice CE for giving 60% Charge to a support servant as well as 2 levels of overcharge. Works really well with Van Gogh for the extra damage.
- **A Fragment of 2030**: Gaining 12 Stars/Turn is a great way to get a sustained amount of stars, even better when combined with a support like BB (Summer) for the card lock
### Save-Loading / Save-Scumming
Save loading or save scumming is a strategy to ensure that the correct RNG happens during the run. For this, when a skill that has RNG elements is used (such as Shishou S1), the game can be quit before the skill animation completes. Upon loading back into the game, the skill is still unused.
As the RNG seed for skills only changes upon skill usage, using another skill (preferably non-RNG), the seed for the RNG skill can be reset. Repeat this procedure and you can almost guarantee each skill.
This tactic can also be applied to the attack turn itself. The RNG seed can be reset by doing the following:
- Different chain hitcounts
- Different enemy attacked/killed
- Skill used
- Command Spell used
The important thing is that the game is quit before the player reaches the next turn.
The RNG seed applies to critical chance, enemy actions and the damage roll. As the damage you deal with each card varies between 90% and 110%, you can use this strategy to etch out damage high-rolls for certain situations.
### Support-Cycling
As the turns of the quest pass, buffs will run out, charge will be needed, debuffs need to be cleansed ... That's where the topic of support-cycling comes in.
This simply means that a support present on field (that already used all of its skills) gets swapped out or defeated in favor of another support that can apply
new skills.
Cycling supports can be achieved in three ways:
- The servant gets defeated
- The servant gets swapped out (Order Change, Miss Crane NP)
- The servant gets sacrificed (The GONG)
If a servant needs to be defeated, it is advisable to either use a low-rarity support that needs to be cycled (low HP) or an esports version of a SSR. Esports
simply means that the servant in question is only leveled until all of its skills are unlocked. For example, a level 70 Skadi has already access to all of her skills,
but she has 2767 HP less than her level 90 version. This can be the difference between getting her defeated or her surviving.
Even for low-rarity servants, it makes usually sense to keep an esports copy just to make sure that they get defeated, even if the enemy is Odysseus (who struggled to defeat an esports Skadi in three actions with x2 class advantage).
### Command Codes
Command Codes can be, just like CEs, real gamechangers. As an example: In <a href="https://www.youtube.com/watch?v=lz6iBZvoDuw" target="_blank" rel="noopener noreferrer">this video</a>, MHXX has a 80% defense buff for Turn 1. The last quick card in the chain has an ignore defense CC, which enables it to do normal damage and breaking the bar with it. Other great CCs are the Ignore Invincibility one as well as the crit damage buff CCs, but the best CCs are hands down the NP Charge CC (+10% charge just for using the card every 3 turns) and the NP damage CCs (+8 Party or +15% self NP damage).
These CCs can sometimes be the difference between completing a run or it being impossible.
## More helpful information
To get better results from your own TA attempts, I have compiled a list of small tweaks and tips to help with getting more consistent results.
### Damage Calculator
In general, it is recommended to calculate each turn of the TA beforehand. This helps preventing situations where you TA for hours only to find out even your high-roll won't be enough to finish the enemy. Todays damage calculators and bots also can calculate parameters like NP refund, critical stars, full chains and more. This helps with keeping an overview over the whole run and ensuring correct skill orders.
Damage calculation tools that I usually use are:
- <a href="https://discord.gg/TKJmuCR" target="_blank" rel="noopener noreferrer">Archimedes Bot, available in DMs or on the Atlas Academy Discord</a>
- <a href="https://maketakunai.github.io/" target="_blank" rel="noopener noreferrer">Maketakunai NP damage calculator</a>
### Recording
Recording TAs in general is easy: You just need to record your phone screen and audio. Since most phones already have a built-in screen recorder, anyone can use that for getting their TAs recorded. However, if you want to go the extra mile like me, you can use different setups to capture your phone.
#### OBS
For capturing using a PC, I would always recommend <a href="https://obsproject.com/" target="_blank" rel="noopener noreferrer">OBS</a>. It is hands-down the best recording software, completely free of charge. Setting it up is really easy, as is using it. The files get recorded on your PC in your desired quality, ready to pe processed by the editing software of your choice.
Just be sure to use OBS itself and not something like Streamlabs-OBS (just my personal opinion)
#### scrcpy
To get your phone screen (and audio as of version 2.0) to your PC, I would always recommend <a href="https://github.com/Genymobile/scrcpy" target="_blank" rel="noopener noreferrer">scrcpy (screen copy)</a>. Not because I have some commits in the project, but rather because it's an amazing piece of software that does most thing better than paid apps.
To get started, download the latest release from their git repository, unzip it and you're good to go. Just attach your phone via a cable, call scrcpy from the terminal and voilá: You have a mirrored window of your phone.
Such a simple way definitely deserves more recognition, so if you like it, feel free to share.
#### Capture Card
Another method would be to use a USB capture card. These can be found for cheap prices on Amazon, as well as everything else that's needed (HDMI cable, HDMI to USB-C converter). With these, the capture card acts like an external display so that it can be captured by OBS using the `Video Capture Device` option as source.
### Key Mapper
For save-scumming, it can be advisable (especially if you use screen gestures) to use a key mapper app. With this, any key on your phone can be remapped to do a certain action.
Usually, I map `[Volume Up]` --> `[Home]` so that if I need to quit the game for canceling a skill, I just need to press one button instead of doing two swipes.
This also drastically reduced the amount of times where I accidentally clicked and animation-skipped a skill I wanted to save-scum.
## Closing Words
I hope I could give you a glimpse into the world of FGO TA and the methods I usually use to perform them myself.
If any other questions arise or if you find something that either seems wrong to you or that is missing, feel free to reach out.
My contact details can be found <a href="https://firq.dev/about" rel="noopener noreferrer">in the contact section.</a>
Anyway, thanks for the read, I hope you could learn something
~ Firq

View file

@ -1,53 +0,0 @@
---
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'
author: 'Firq'
tags: ['astro', 'hello']
---
Well ... that took some time to get up and running. But nonetheless, here we are, and my site is starting to work!
## What is this whole thing?
My own site! Well, I should explain a bit more ...
Last year, someone asked me if I could provide them with a list of servants and CEs that I own and that are often used in FGO TA. At first, I started writing down
the servants as markdown files (kinda ironic, this is a markdown file as well). But after a while, a good friend, <a href="https://mastodon.neshweb.net/@neshura" target="_blank" rel="noopener noreferrer">Neshura</a> said to me: "Why don't you just create a small site with GitLab Pages? You can host that on my instance". And so, I started writing HTML and CSS by hand, getting the
first version of this site done after 1-2 days. It was challenging, as my experience with CSS was limited, but in the end, it was ready (special thanks again to <a href="https://www.mitsunee.com/" target="_blank" rel="noopener noreferrer">Mitsunee</a> for helping me so much with the CSS).
But I got tired of always writing the same lines of code (because that happens if you have multiple cards with different content), so I kinda stopped updating the site for a while.
But then came <a href="https://astro.build/" target="_blank" rel="noopener noreferrer">Astro</a>. When Mitsunee recommended it to me, I was instantly amazed by how easy this was ... I just needed to migrate the existing site to Astro (which took me a bit) and adapt the `gitlab-ci.yml`, and it was done! It felt amazing, having reusable components and simple creation of those.
Shout outs to one of my favourite lines which creates the servant cards on the Servants page:
```typescript
<BaseSection title="Servants">
{servantdata.map((item) => (
<ServantCard {...item} />
))}
</BaseSection>
```
But still, the whole site felt odd. It was a single page, with everything just cramped in there. So around 2 weeks ago, I started to rewrite the whole thing as a multi-page website. GitLab made it sometimes pretty hard (because having a baseurl of `/fgosite` was forced), but it worked and felt a lot cooler.
Still, I was a bit annoyed that I had this long-ish URL for the site (`firq.pages.neshweb.net/fgosite`), but the GitLab instance didn't offer to set a custom domain (because of how the server where it runs is set up).
So in the end, Neshura and I started a journey on how to best do this, and, in the end, found a solution.
## But ... how does it work?
In general, this whole site is an Astro project that is being built using GitLab Pipelines. For that, the project is copied onto an Alpine-Linux Proxmox instance where it then gets built. This is necessary so that once it is served to `localhost` using `npx serve`, it has it's own IP address and port. Using those in nginx, the site can be assigned the `firq.dev` domain and be made public.
To prevent the `serve`-command from ending once the GitLab runner cuts the SSH connection the Proxmox instance, `screen` is being used to start a headless session where `serve` is then executed. This also makes it easy to reload the site after a finished build, as I can easily kill the running screen session with the specific tag.
## So ... what can be expected here?
I have some more blog topics that I want to write about in the future (FGO TA being the most prominent one, followed by a detailed explanation of this project). I also want to tackle the design in some cases, as I am not completely satisfied how some parts of the site look.
But in general, this whole thing is a hobby-project which aims to enhance my web-dev skills and provide others with information.
Thanks for reading, and until next time
**~ Firq**

View file

@ -0,0 +1,39 @@
---
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 blogEntries = await getCollection('blog')
blogEntries.sort(
(a, b) =>
(b.data.pubDate.valueOf() - a.data.pubDate.valueOf() )
)
---
<Layout
title="Blog - Firq FGO Site"
currentpage="blog"
descriptionOverride={description}
>
<SmallTitle maintext="Blog Articles" subtext="" fadeout={true} />
<BlogSection title="Blog Articles" displayLine={true} titlehidden={true}>
{
blogEntries.map((post) => (
<BlogCard
url="blog"
slug={post.slug}
title={post.data.title}
pubdate={post.data.pubDate}
description={post.data.description}
author={post.data.author}
/>
))
}
</BlogSection>
</Layout>
<style></style>

View file

@ -1,116 +0,0 @@
---
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.'
author: 'Firq'
tags: ['fgo', 'games']
---
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"
and its interaction with Instant Kill, as we could not reach a final verdict if it affects Instant Kill Hit rate or not. In case of this article, I will assume
it doesn't interact with the hitrate, but if you have proof that this is different, I will gladly incorporate this in the article.
## Introduction
Instant Kill & Instant Death: One of the most rejected systems to exist in FGO. Most players know of it, but discard it because of its unreliability and uselessness against stronger enemies. In this article, I want to dive a bit into the workings of Instant Kill and help you understand how you can, in specific scenarios, use it to your advantage.
There are a multitude of servants that have access to instant-kill, but I will only highlight a few here (You can view the full list on <a href="https://gamepress.gg/grandorder/instant-death" target="_blank" rel="noopener noreferrer" >Gamepress</a>)
- **Nitocris (Caster)**: The most prominent figure, as she has easy access to her instant-killing NP and a skill that increases her death hit rate
- **Void Shiki (Saber)**: Another Example of an AOE Instant-Kill servant who also has access to Death Resist debuffs. One quest in her release event even specifically used the Instant Kill mechanic (<a href="https://apps.atlasacademy.io/db/NA/quest/94021308/1" target="_blank" rel="noopener noreferrer" >Quest Link here</a>)
- **King Hassan (Assassin)**: The only servant that can afflict enemies with instant death on normal attacks (Note: David can do this as well, but only if he has his Bond CE equipped)
But since Instant Kill is based on probability, most players are relucatant to use it in their farming compositions. This thems from the different death rates enemies have:
- Bronze: 80%
- Silver: 50%
- Gold: 20%
This list here is just for rarities. However, there are a lot more factors to consider, as some enemies have higher death rate, some lower. Be sure to check each case individually, as rates may vary.
Also, servant enemies usually have a death rate of 0.1%, sometimes a bit more like 0.3% or 0.4%. So going for Instant Kill on servants will, in the majority of cases, not work.
## Calculating Instant Death Rate
The formular for getting the probability of an enemy dying to Instant Kill is as follows:
```
death = hitRate * deathRate * (1 + instantDeathRateUp - instantDeathRateDown - instantDeathResistUp + instantDeathResistDown)
```
The parameters here are the following:
- `hitRate`: The Death Hit rate of the effect or NP used
- `deathRate`: The Enemy Death rate
- `instantDeathRateUp`: Buffs that increases instant kill success rate (Nitocris S1)
- `instantDeathRateDown`: Debuffs that decreases instant kill success rate
- `instantDeathResistUp`: Buffs that increases instant kill resistance (Taira S2)
- `instantDeathResistDown`: Debuffs that decreases instant kill resistance (Void Shiki S1)
## Example
The main reason this came up was because of a farming comp used in <a href="https://apps.atlasacademy.io/db/NA/quest/94061820/1" target="_blank" rel="noopener noreferrer" >Hunting Quest XI - Day 5 - Pride+</a>. The node is structured
into a 2 - 1 - 1 Layout, meaning using a dual-DPS setup would be the way to go.
Looking at the stats for the first wave, it made sense to use Nitocris: Both Ghouls have a 100% Death Rate <a href="https://apps.atlasacademy.io/db/NA/enemy/9940700" target="_blank" rel="noopener noreferrer" >according to Atlas Academy</a>.
I went with using Nitocris, since she has a 100% NP charge and a skill that increases her Instant Death rate by 100%. Not needing any additional charge meant that no other buffs or AOE charges needed to be used on turn 1.
Her kit brings the following stats:
* Her NP has an Insta-Kill Hit Rate of 50% when used at Overcharge 1 (meaning 100% charge)
* Her S1 increases Insta-Kill success rate by 100%
### Actual calculation
When using the formular from above, we have the following values
```
hitRate: 50%
deathRate: 100%
instantDeathRateUp: 100%
instantDeathRateDown: 0%
instantDeathResistUp: 0%
instantDeathResistDown: 0%
```
This means we can fill in the formular like this
```
death = 0.5 * 1 * (1 + 1 - 0 - 0 + 0)
```
We now break everything down step by step
```
death = 0.5 * (1 + 1)
= 0.5 * 2
= 1
= 100%
```
So her death succeeds 100% of the time on these ghouls, which makes her perfect to use in this scenario.
For other skill levels, this looks like this (_Her S1 scales from 50% to 100%_)
```
Skill Level | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
-------------|-----|-------|-----|-------|-----|-------|-----|-------|-----|------
Death % | 75% | 77.5% | 80% | 82.5% | 85% | 87.5% | 90% | 92.5% | 95% | 100%
```
In the end, this resulted in the following comp that worked like a charm.
<YoutubeEmbed id="UwbNp_dB_VU" thumbnail={thumbnail.src}/>
## Conclusion
This small article should have given you an insight into using Instant Death as an advantage in the future. But remember: Stacking Gacha in a Gacha game can backfire.
That's all for now, thanks for reading
~ Firq

View file

@ -1,9 +1,9 @@
---
import Layout from '../layouts/Layout.astro'
import Hero from '../components/hero.astro'
import BaseSection from '../layouts/baseSection.astro'
import FavouriteCard from '../components/favouriteCard.astro'
import FavouriteCard from '../components/cards/favouriteCard.astro'
import favouritesdata from '../../static/data/_favouritesdata.json'
import Hi from '../components/titles/title.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'
@ -14,7 +14,7 @@ const description =
currentpage="home"
descriptionOverride={description}
>
<Hero />
<Hi />
<BaseSection title="Favourites">
{favouritesdata.map((item) => <FavouriteCard {...item} />)}
</BaseSection>

View file

@ -2,11 +2,12 @@
import Layout from '../layouts/Layout.astro'
import BaseSection from '../layouts/baseSection.astro'
import ServantCard from '../components/servantCard.astro'
import ServantCard from '../components/cards/servantCard.astro'
import servantdata from '../../static/data/_servantdata.json'
import CeCard from '../components/ceCard.astro'
import CeCard from '../components/cards/ceCard.astro'
import cedata from '../../static/data/_cedata.json'
import SmallTitle from '../components/titles/smallTitle.astro'
const description =
'A list of all the servants and ces that Firq can offer up on support for TA.'
@ -17,6 +18,7 @@ const description =
currentpage="servants"
descriptionOverride={description}
>
<SmallTitle maintext='TA Offering' subtext='Servants and CEs I can offer for your TAs' fadeout={true}/>
<BaseSection title="Servants">
{servantdata.map((item) => <ServantCard {...item} />)}
</BaseSection>

View file

@ -8,10 +8,11 @@
import Layout from '../layouts/Layout.astro'
import TaSection from '../layouts/taSection.astro'
import TaCard from '../components/taCard.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'
const important_data = tadata.filter(function (el) {
return [
@ -37,7 +38,8 @@ const description = 'A collection of TAs previously completed be Firq.'
currentpage="ta-collection"
descriptionOverride={description}
>
<FgotaHero />
<SmallTitle maintext='TA Collection' subtext=''/>
<FgotaHero fadeout={true}/>
<TaSection title="Notable TAs" abovetext="My most notable TAs">
{important_data.map((item) => <TaCard {...item} />)}
</TaSection>