Home

Awesome

<p align="center"> <img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/logo/full/werewolves-logo.png?raw=true" width="400" alt="logo"/> </p>

TypeScript Nuxt TailwindCSS

โš™๏ธ Build Workflow ๐Ÿš€ Deploy To Production Workflow

GitHub release semantic-release: conventional commits GitHub license DependenciesKnown Vulnerabilities

Tests count Covered Statements Scenarios Mutation testing badge

Technical Debt Duplicated Lines (%) Code Smells

๐Ÿ“‹ Table of Contents

  1. ๐Ÿบ What is this app ?
  2. โœจ Production and development links
  3. ๐Ÿƒ Available roles
  4. ๐Ÿ“ธ Screenshots
  5. ๐Ÿ”จ Installation
  6. ๐Ÿš€ Build
  7. ๐Ÿณ Docker
  8. ๐Ÿ’ฏ Tests
  9. ๐ŸŒฟ Env variables
  10. โ˜‘๏ธ Code analysis and consistency
  11. ๐Ÿ“ˆ Releases & Changelog
  12. ๐Ÿ™ GitHub Actions
  13. โœจ Misc commands
  14. ยฉ๏ธ License
  15. โค๏ธ Contributors

<a name="what-is-this-app">๐Ÿบ What is this app ?</a>

Werewolves Assistant Web is a Nuxt Web App using the Werewolves Assistant API. It helps you, the game master, to manage your games of Werewolves Of Millers Hollow.

[!NOTE] This is the next version of the deprecated Werewolves Assistant Web. It is maintained with the Werewolves Assistant API.

๐Ÿค” Want to know more about this awesome project ? <a href="https://werewolves-assistant.com/about" target="_blank">Check out the dedicated about page</a>.

<a name="production-and-development-links">โœจ Production and development links</a>

๐ŸŒ Production

The production version of this Web App is available at werewolves-assistant.com.

It uses the Production Werewolves Assistant API.

The production server is updated automatically with the latest version of the Web App when a new release is created. (When a new tag is pushed on the main branch)

๐Ÿ› ๏ธ Development

The development version of this Web App is available at preprod.werewolves-assistant.com.

It uses the Preprod Werewolves Assistant API.

The development server is updated automatically when a commit is pushed on the develop branch.

<a name="available-roles">๐Ÿƒ Available roles</a>

<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/werewolf/werewolf-small.jpeg?raw=true" width="40"/><br/>Werewolf<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/big-bad-wolf/big-bad-wolf-small.jpeg?raw=true" width="40"/><br/>Big Bad Wolf<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/accursed-wolf-father/accursed-wolf-father-small.jpeg?raw=true" width="40"/><br/>Accursed Wolf-Father<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/white-werewolf/white-werewolf-small.jpeg?raw=true" width="40"/><br/>White Werewolf
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/villager/villager-small.jpeg?raw=true" width="40"/><br/>Villager<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/villager/villager-small.jpeg?raw=true" width="40"/><br/> Villager-Villager<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/seer/seer-small.jpeg?raw=true" width="40"/><br/>Seer<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/cupid/cupid-small.jpeg?raw=true" width="40"/><br/>Cupid
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/witch/witch-small.jpeg?raw=true" width="40"/><br/>Witch<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/hunter/hunter-small.jpeg?raw=true" width="40"/><br/>Hunter<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/little-girl/little-girl-small.jpeg?raw=true" width="40"/><br/>Little Girl<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/defender/defender-small.jpeg?raw=true" width="40"/><br/>Defender
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/elder/elder-small.jpeg?raw=true" width="40"/><br/>Elder<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/scapegoat/scapegoat-small.jpeg?raw=true" width="40"/><br/>Scapegoat<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/idiot/idiot-small.jpeg?raw=true" width="40"/><br/>Idiot<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/two-sisters/two-sisters-small.jpeg?raw=true" width="40"/><br/>Two Sisters
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/three-brothers/three-brothers-small.jpeg?raw=true" width="40"/><br/>Three Brothers<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/fox/fox-small.jpeg?raw=true" width="40"/><br/>Fox<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/bear-tamer/bear-tamer-small.jpeg?raw=true" width="40"/><br/>Bear Tamer<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/stuttering-judge/stuttering-judge-small.jpeg?raw=true" width="40"/><br/>Stuttering Judge
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/rusty-sword-knight/rusty-sword-knight-small.jpeg?raw=true" width="40"/><br/>Rusty Sword Knight<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/wild-child/wild-child-small.jpeg?raw=true" width="40"/><br/>Wild Child<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/wolf-hound/wolf-hound-small.jpeg?raw=true" width="40"/><br/>Wolf-Hound<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/thief/thief-small.jpeg?raw=true" width="40"/><br/>Thief
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/angel/angel-small.jpeg?raw=true" width="40"/><br/>Angel<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/pied-piper/pied-piper-small.jpeg?raw=true" width="40"/><br/>Pied Piper<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/scandalmonger/scandalmonger-small.jpeg?raw=true" width="40"/><br/>Scandalmonger<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/prejudiced-manipulator/prejudiced-manipulator-small.jpeg?raw=true" width="40"/><br/>Prejudiced Manipulator
<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/actor/actor-small.jpeg?raw=true" width="40"/><br/>Actor<img src="https://github.com/antoinezanardi/werewolves-assistant-api-next/blob/main/public/assets/images/roles/devoted-servant/devoted-servant-small.jpeg?raw=true" width="40"/><br/>Devoted Servant

<a name="screenshots">๐Ÿ“ธ Screenshots</a>

<details> <summary>๐Ÿ  Home Page</summary>

Home page

</details> <details> <summary>โ“ About Page</summary>

About page

</details> <details> <summary>๐Ÿคผ Game Lobby Page</summary>
<details> <summary>๐Ÿคผ Game Lobby Page without players</summary>

Game Lobby Page without players

</details> <details> <summary>๐Ÿคผ Game Lobby Page with 40 players</summary>

Game Lobby Page with 40 players

</details> <details> <summary>๐Ÿƒ Game Lobby Role Picker without picked role</summary>

Game Lobby Role Picker without picked role

</details> <details> <summary>๐Ÿƒ Game Lobby Role Picker with picked role</summary>

Game Lobby Role Picker with picked role

</details> <details> <summary>โš™๏ธ Game Lobby Options Hub</summary>

Game Lobby Options Hub

</details> <details> <summary>๐Ÿƒ๐Ÿƒ๐Ÿƒ๏ธ Game Lobby Additional Cards Manager without cards</summary>

Game Lobby Additional Cards Manager without cards

</details> <details> <summary>๐Ÿƒ๐Ÿƒ๐Ÿƒ๏ธ Game Lobby Additional Cards Manager with 5 cards</summary>

Game Lobby Additional Cards Manager with 5 cards

</details>
</details> <details> <summary>๐ŸŽฒ Game Page</summary>
<details> <summary>๐Ÿ•น๏ธ Game Playgrounds</summary>
<details> <summary>๐Ÿบ Accursed Wolf-Father infects Playground</summary>

Accursed Wolf-Father infects Playground

</details> <details> <summary>๐ŸŽญ Actor chooses card Playground</summary>

Actor chooses card Playground

</details> <details> <summary>๐Ÿบ ๐Ÿ‘น Big Bad Wolf eats Playground</summary>

Big Bad Wolf eats Playground

</details> <details> <summary>๐Ÿ˜ตโ€๐Ÿ’ซ Charmed people meet each other Playground</summary>

Charmed people meet each other Playground

</details> <details> <summary>๐Ÿ‘ผ Cupid charms Playground</summary>

Cupid charms Playground

</details> <details> <summary>๐Ÿ›ก๏ธ Defender protects Playground</summary>

Defender protects Playground

</details> </details> <details> <summary>๐ŸฆŠ Fox sniffs Playground</summary>

Fox sniffs Playground

</details> <details> <summary>๐Ÿ”ซ Hunter shoots Playground</summary>

Hunter shoots Playground

</details> <details> <summary>๐Ÿ’ž Lovers meet each other Playground</summary>

Lovers meet each other Playground

</details> <details> <summary>๐Ÿชˆ Pied Piper charms Playground</summary>

Pied Piper charms Playground

</details> <details> <summary>๐Ÿฆโ€โฌ› Scandalmonger marks Playground</summary>

Scandalmonger marks Playground

</details> <details> <summary>๐Ÿ Scapegoat bans voting Playground</summary>

Scapegoat bans voting Playground

</details> <details> <summary>๐Ÿ”ฎ Seer looks Playground</summary>

Seer looks Playground

</details> <details> <summary>๐ŸŽ–๏ธ Sheriff delegates Playground</summary>

Sheriff delegates Playground

</details> <details> <summary>๐ŸŽ–๏ธ Sheriff settles votes Playground</summary>

Sheriff settles votes Playground

</details> <details> <summary>โš–๏ธ๏ธ Stuttering Judge Requests Another Vote Playground</summary>

Stuttering Judge Requests Another Vote Playground

</details> <details> <summary>๐ŸŽ–๏ธ Survivors elect the Sheriff Playground</summary>

Survivors elect the Sheriff Playground

</details> <details> <summary>๐Ÿ‘บ Thief chooses card Playground</summary>

Thief chooses card Playground

</details> <details> <summary>๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ Three Brothers meet each other Playground</summary>

Three Brothers meet each other Playground

</details> <details> <summary>๐Ÿ‘ฏโ€ Two Sisters meet each other Playground</summary>

Two Sisters meet each other Playground

</details> <details> <summary>๐Ÿบ Werewolves eat Playground</summary>

Werewolves eat Playground

</details> <details> <summary>๐Ÿฆด๐Ÿบ White Werewolf eats Playground</summary>

White Werewolf eats Playground

</details> <details> <summary>๐Ÿ’ Wild Child chooses model Playground</summary>

Wild Child chooses model Playground

</details> <details> <summary>๐Ÿช„ Witch uses life potion Playground</summary>

Witch uses life potion Playground

</details> <details> <summary>๐Ÿช„ Witch uses death potion Playground</summary>

Witch uses death potion Playground

</details> <details> <summary>๐Ÿช„ Witch out of potions Playground</summary>

Witch out of potions Playground

</details> <details> <summary>๐Ÿ• Wolf-Hound chooses side Playground</summary>

Wolf-Hound chooses side Playground

</details> <details> <summary>๐Ÿคท Game Playground without targets</summary>

Game Playground without targets

</details>
</details> <details> <summary>๐ŸŒŸ Game Events</summary>
<details> <summary>๐ŸŽฌ Game Starts Event</summary>

Game Starts Event

</details> <details> <summary>๐ŸŒ™ Game Phase Event</summary>

Game Phase Event

</details>
</details> <details> <summary>โ“ Game not found</summary>

Game not found

</details> <details> <summary>โŒ Game canceled</summary>

Game canceled

</details>
</details> <details> <summary>๐Ÿ† Game Victory Page</summary>
<details> <summary>๐Ÿชฝ Game won by Angel</summary>

Game won by Angel

</details> <details> <summary>๐Ÿ’ž Game won by Lovers</summary>

Game won by Lovers

</details> <details> <summary>๐Ÿ’ž๐Ÿ‘ผ Game won by Lovers and Cupid</summary>

Game won by Lovers and Cupid

</details> <details> <summary>๐Ÿง‘๐Ÿปโ€๐ŸŒพ Game won by Villagers</summary>

Game won by Villagers

</details> <details> <summary>๐Ÿบ Game won by Werewolves</summary>

Game won by Werewolves

</details> <details> <summary>โ˜ ๏ธ Game won by nobody</summary>

Game won by nobody

</details> <details> <summary>๐Ÿชˆ Game won by Pied Piper</summary>

Game won by Pied Piper

</details> <details> <summary>๐Ÿบ ๐Ÿฆด Game won by White Werewolf</summary>

Game won by White Werewolf

</details>
</details> <details> <summary>โ“ Page not found</summary>

Page not found

</details>

<a name="installation">๐Ÿ”จ Installation</a>

To install this project, you will need to have on your machine :

Node PNPM

We recommend to use the node version specified in the .nvmrc file. At least, you'll need to have version 23 installed as mentioned in package.json file.

[!TIP] If you don't have pnpm installed, you can still use npm for all commands below, but we recommend to use pnpm for faster and more reliable installations.

Then, run the following commands :

# Install dependencies and Husky hooks
pnpm install

# Run the app in dev mode
pnpm run dev

The above command will start the app in development mode and watch for changes on local.

<a name="build">๐Ÿš€ Build</a>

In order to build the app for production, run the following command :

# Build the app
pnpm run build

<a name="docker">๐Ÿณ Docker</a>

This app is Docker ready !

The Dockerfile is available at the root of the project. It uses a multi-stage build to optimize the image size.

You can run the whole project (including MongoDB and the Werewolves Assistant API) with the following command :

# Run the whole project with `develop` tags for the API and the Web app
pnpm run docker:preproduction-example:start

# Run the whole project with `latest` tags for the API and the Web app
pnpm run docker:production-example:start

[!TIP] The docker-compose.yml file used for the preproduction example stated above is available here. The docker-compose.yml file used for the production example stated above is available here.

๐Ÿท๏ธ Docker Tags

Because of the Nuxt pre-rendering feature, environment variables are injected at build time on some pages. Therefore, multiple tags are created when pushing to develop and main branches.

โ›ต๏ธ When pushing to develop branch

๐Ÿš€ When pushing to main branch

When pushing to main branch, a new release is created with the version number from the package.json file. Then, all above tags for prod are also created with the version number instead of latest.

๐Ÿ”– Which one to choose ?

For your tests, you must choose the local prefixed tags when you want to test the app with the API running locally.

Not prefixed tags like preprod-fr or prod-en are exclusively used for the preproduction and production environments.

<a name="tests">๐Ÿ’ฏ Tests</a>

๐Ÿงช Unit tests

Vitest

Tests count

Covered Statements

Covered Branches

Covered Functions

Covered Lines

โš—๏ธ E2E / Acceptance tests

Playwright

Cucumber

Scenarios

E2E tests are written in Gherkin language around Playwright functions and are available in the features directory.

[!NOTE] ๐Ÿž๏ธ Screenshots for visual regression tests for each platform (linux / darwin) are available in the screenshots directory.

๐Ÿ‘ฝ Mutant testing

Stryker

Mutation testing badge

โ–ถ๏ธ Commands

[!IMPORTANT] Before testing, you must follow the installation steps.

Then, run one of the following commands :

# Assure you started Werewolves Assistant sandbox API docker containers before running tests
pnpm run docker:sandbox-api:start

# Run unit tests with coverage
pnpm run test:unit:cov

# Run unit tests only on staged files (run on pre-commit)
pnpm run test:unit:staged

# Before running e2e tests, you must prepare them
pnpm run test:cucumber:prepare

# Run all e2e acceptance tests and generate a HTML report
pnpm run test:cucumber

# Run shard-X tagged e2e acceptance tests and generate a HTML report (Every feature file is tagged with a shard number)
pnpm run test:cucumber:shard-1
pnpm run test:cucumber:shard-2
pnpm run test:cucumber:shard-3
pnpm run test:cucumber:shard-4
pnpm run test:cucumber:shard-5
pnpm run test:cucumber:shard-6
pnpm run test:cucumber:shard-7
pnpm run test:cucumber:shard-8

# Run e2e acceptance tests without screenshots comparison and generate a HTML report
pnpm run test:cucumber:skip-screenshots-comparison

# Generate a HTML report from the last e2e tests run (implied by the previous command)
pnpm run test:cucumber:html-report

# Run all mutant tests with coverage
pnpm run test:stryker

# Run mutant tests with coverage from scratch (without using the incremental file)
pnpm run test:stryker:force

# Run mutant tests only on a specific shard of the project
pnpm run test:stryker:components
pnpm run test:stryker:composables
pnpm run test:stryker:layouts
pnpm run test:stryker:pages
pnpm run test:stryker:stores
pnpm run test:stryker:utils

# Run all of the above shards commands one after the other
pnpm run test:stryker:all-shards

<a name="env-variables">๐ŸŒฟ Env variables</a>

Environnement files are available in the env directory.

[!TIP] You can create a .env file in this directory to override the default values when starting the API locally with pnpm run dev command.

Environment variables are :

NameDescriptionRequiredDefault valueLimitations
NUXT_PUBLIC_WEREWOLVES_ASSISTANT_API_BASE_URLURL of the Werewolves Assistant APIโœ…โŒMust be a valid URL
NUXT_WEREWOLVES_ASSISTANT_API_KEYAPI key for the Werewolves Assistant APIโœ…โŒMust be a valid key
NUXT_PUBLIC_DEFAULT_LOCALELocale used when starting the appโŒenMust be either en or fr
NUXT_PUBLIC_CONTACT_EMAILContact email for the appโœ…โŒMust be a valid email address
SKIP_SCREENSHOTS_COMPARISON_TESTSIn E2E tests, skip all screenshots comparisons, not used in productionโŒfalseMust be either true or false
NUXT_SITE_URLUsed for SEO, base URL for the siteโœ…โŒMust be a valid URL
NUXT_SITE_NAMEUsed for SEO, name of the site (Used in titles)โœ…โŒMust be a valid string
NUXT_SITE_ENVUsed for SEO and indexing, environment of the siteโœ…โŒMust be a either production or development
NUXT_SITE_DESCRIPTIONUsed for SEO and indexing, description of the siteโœ…โŒMust be a valid string

<a name="code-analysis-and-consistency">โ˜‘๏ธ Code analysis and consistency</a>

๐Ÿ” Code linting & formatting

ESLint

In order to keep the code clean, consistent and free of bad TS practices, more than 300 ESLint rules are activated !

[!NOTE] Complete list of all enabled rules is available in the eslintrc.config.js file.

โ–ถ๏ธ Commands

[!IMPORTANT] Before linting, you must follow the installation steps.

Then, run one of the following commands :

# Lint 
pnpm run lint

# Lint and fix
pnpm run lint:fix

# Lint and fix only on staged files (runs on pre-commit)
pnpm run lint:staged:fix

# Inspect ESLint config with @eslint/config-inspector
pnpm run lint:inspect-config

๐Ÿฅ‡ Project quality scanner

Multiple tools are set up to maintain the best code quality and to prevent vulnerabilities :

CodeQL

[!NOTE] You can check the CodeQL analysis report here.

SonarCloud

[!NOTE] SonarCloud summary is available here.

Coverage Duplicated Lines (%) Quality Gate Status

Technical Debt Vulnerabilities Code Smells

Reliability Rating Security Rating Bugs

<a name="versions">๐Ÿ“ˆ Releases & Changelog</a>

Releases on main branch are generated and published automatically by :

Semantic Release

It uses the conventional commit strategy.

Each change when a new release comes up is listed in the <a href="https://github.com/antoinezanardi/werewolves-assistant-web-next/blob/master/CHANGELOG.md" target="_blank">CHANGELOG.md file</a>.

[!TIP] Also, you can keep up with changes by watching releases via the Watch GitHub button at the top of this page.

<a name="github-actions">๐Ÿ™ GitHub Actions</a>

This project uses GitHub Actions to automate some boring tasks.

[!NOTE] You can find all the workflows in the .github/workflows directory.

๐ŸŽข Workflows

NameDescription & StatusTriggered on
โš™๏ธ BuildVarious checks for app health, code quality and tests coverage<br/><br/>โš™๏ธ Build Workflowpush on develop and all pull requests to develop
๐Ÿ”ƒ Lint PR Name Into Develop WorkflowChecks if pull request name respects conventionnal-commit rules<br/><br/>๐Ÿ”ƒ Lint PR Name Into Develop Workflowpull-request created or updated
๐Ÿ”ƒ๏ธ Upsert PR Release WorkflowCreates or updates pull request to main depending on commits on develop since last release<br/><br/>๐Ÿ”ƒ๏ธ Upsert PR Release Workflowpush on develop
๐Ÿท๏ธ Release Creation WorkflowCreates a new release using semantic-release with tag and updated changelog<br/><br/>๐Ÿท๏ธ Release Creation Workflowpush on main
๐Ÿš€ Deploy To Production WorkflowDeploys app with last tag version to Docker Hub and GCP<br/><br/>๐Ÿš€ Deploy To Production Workflowtag-creation
๐Ÿ‘ฝ Run Stryker Mutant Tests and Push Incremental Reports WorkflowRuns Stryker mutant tests and pushes incremental reports<br/><br/>๐Ÿ‘ฝ Run Stryker Mutant Tests and Push Incremental Reports Workflowworkflow_dispatch

<a name="misc-commands">โœจ Misc commands</a>

๐ŸŒณ Animated tree visualisation of the project's evolution with Gource

# Please ensure that `gource` is installed on your system.
pnpm run gource

๐Ÿ”€ Create git branch with a conventional name

pnpm run script:create-branch

โคด๏ธ Create pull request against the develop branch from current branch

pnpm run script:create-pull-request

๐Ÿ’  Check that all acceptance features have an associated shard

pnpm run script:check-acceptance-features-shard

๐Ÿ“ธ Download all screenshots from the acceptance tests from a GitHub action

pnpm run script:download-acceptance-screenshots

๐Ÿ“ฃ To all IntelliJ IDEs users (IntelliJ, Webstorm, PHPStorm, etc.)

All the above commands are available in the .run directory at the root of the project.

[!TIP] You can add them as run configurations in your IDE.

<a name="license">ยฉ๏ธ License</a>

This project is licensed under the MIT License.

<a name="contributors">โค๏ธ Contributors</a>

[!WARNING] If you want to contribute to this project, please read the contribution guide.

Thank you to all the contributors:

<table> <tbody> <tr> <td align="center" valign="top"> <a href="https://github.com/danielroe"> <img src="https://github.com/danielroe.png?s=75" width="75" height="75"><br/> Daniel Roe </a> </td> <td align="center" valign="top"> <a href="https://github.com/manniL"> <img src="https://github.com/manniL.png?s=75" width="75" height="75"><br/> Alexander Lichter </a> </td> </tr> </tbody> </table>