Migrate to Next.js + TypeScript (#1038)
* next.js + typescript * more testing * script updates * fixes * favicon generation * testing * readme updates * tweaks * tweaks * move tests * image tweaks * ci tweaks * commit next-env.d.ts for ci * migrations * fixes * fixes * ci tweaks * new animations * project preview tweaks * add codeclimate config * dark mode refactor, test tweaks * readme updates * animation tweaks * animate in loaded images * test update * update humans.txt
39
.codeclimate.yml
Normal file
@ -0,0 +1,39 @@
|
||||
# https://docs.codeclimate.com/docs/default-analysis-configuration
|
||||
# https://docs.codeclimate.com/docs/advanced-configuration
|
||||
|
||||
version: '2' # required to adjust maintainability checks
|
||||
checks:
|
||||
complex-logic:
|
||||
config:
|
||||
threshold: 8
|
||||
method-complexity:
|
||||
config:
|
||||
threshold: 8
|
||||
method-lines:
|
||||
config:
|
||||
threshold: 40
|
||||
|
||||
exclude_patterns:
|
||||
- 'config/'
|
||||
- 'db/'
|
||||
- 'dist/'
|
||||
- 'features/'
|
||||
- '**/node_modules/'
|
||||
- 'script/'
|
||||
- '**/spec/'
|
||||
- '**/test/'
|
||||
- '**/tests/'
|
||||
- 'Tests/'
|
||||
- '**/vendor/'
|
||||
- '**/*_test.go'
|
||||
- '**/*.d.ts'
|
||||
- '**/@types/'
|
||||
- '**/interfaces/'
|
||||
- '**/_types.*'
|
||||
- '**/*.stories.*'
|
||||
- '**/*.test.*'
|
||||
- '.storybook/'
|
||||
- 'tests/'
|
||||
- 'coverage/'
|
||||
- '.next/'
|
||||
- '.swc/'
|
@ -1,15 +0,0 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
LICENSE
|
||||
.vscode
|
||||
public
|
||||
.cache
|
||||
package-lock.json
|
||||
README.md
|
||||
coverage
|
@ -1,2 +1,2 @@
|
||||
GATSBY_GITHUB_TOKEN=xxx
|
||||
GATSBY_TYPEKIT_ID=xxx
|
||||
GITHUB_TOKEN=xxx
|
||||
NEXT_PUBLIC_TYPEKIT_ID=xxx
|
31
.eslintrc
@ -1,31 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:testing-library/dom",
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"plugins": ["react", "graphql", "prettier", "react-hooks", "testing-library"],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": { "jsx": true }
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es2020": true,
|
||||
"jest": true
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"testing-library/no-node-access": "off",
|
||||
"testing-library/no-container": "off"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
3
.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
65
.github/workflows/ci.yml
vendored
@ -20,8 +20,8 @@ jobs:
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v1
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
@ -60,43 +60,40 @@ jobs:
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v1
|
||||
- name: Cache node_modules & Next.js build output
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-node-
|
||||
|
||||
- name: Cache Gatsby build output
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: public
|
||||
key: ${{ runner.os }}-public
|
||||
path: |
|
||||
~/.npm
|
||||
${{ github.workspace }}/.next/cache
|
||||
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
|
||||
restore-keys: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
|
||||
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
env:
|
||||
GATSBY_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NEXT_PUBLIC_TYPEKIT_ID: ${{ secrets.NEXT_PUBLIC_TYPEKIT_ID }}
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
name: public
|
||||
path: public
|
||||
# - uses: actions/upload-artifact@v1
|
||||
# if: github.ref == 'refs/heads/main'
|
||||
# with:
|
||||
# name: public
|
||||
# path: public
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
if: success() && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
# deploy:
|
||||
# needs: build
|
||||
# if: success() && github.ref == 'refs/heads/main'
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: public
|
||||
- name: Deploy to S3
|
||||
run: npm run deploy:s3
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/download-artifact@v1
|
||||
# with:
|
||||
# name: public
|
||||
# - name: Deploy to S3
|
||||
# run: npm run deploy:s3
|
||||
# env:
|
||||
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
# AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
|
||||
|
45
.gitignore
vendored
@ -1,17 +1,40 @@
|
||||
# Project dependencies
|
||||
.cache
|
||||
node_modules
|
||||
yarn-error.log
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# Build directory
|
||||
/public
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
plugins/gatsby-plugin-matomo
|
||||
coverage
|
||||
.env
|
||||
static/matomo.js
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
.env
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
size-plugin.json
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
.swc
|
||||
coverage
|
||||
public/matomo.js
|
||||
public/favicon/
|
24
Dockerfile
@ -1,24 +0,0 @@
|
||||
# Dockerfile for local development just installing dependencies.
|
||||
# Use together with `docker-compose up`
|
||||
FROM node:alpine
|
||||
|
||||
RUN mkdir -p /portfolio
|
||||
WORKDIR /portfolio
|
||||
COPY package.json .
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
g++ \
|
||||
make \
|
||||
autoconf \
|
||||
automake \
|
||||
libtool \
|
||||
nasm \
|
||||
libc6-compat \
|
||||
libjpeg-turbo-dev \
|
||||
libpng-dev \
|
||||
git \
|
||||
bash \
|
||||
&& rm -rf /var/cache/apk/* \
|
||||
&& npm install \
|
||||
&& npm cache clean --force \
|
||||
&& apk del .build-deps
|
109
README.md
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://matthiaskretschmann.com"><img src="src/images/github-header.png" /></a>
|
||||
<a href="https://matthiaskretschmann.com"><img src="public/github-header.png" /></a>
|
||||
</p>
|
||||
<h2 align="center">
|
||||
👔 Portfolio thingy, built with <a href="https://www.gatsbyjs.org">Gatsby</a>.
|
||||
👔 Portfolio thingy.
|
||||
</h2>
|
||||
<p align="center">
|
||||
<a href="https://matthiaskretschmann.com">matthiaskretschmann.com</a>
|
||||
@ -13,17 +13,16 @@
|
||||
<a href="https://codeclimate.com/github/kremalicious/portfolio/test_coverage"><img src="https://api.codeclimate.com/v1/badges/8f561ec93e0f8c6b15d9/test_coverage" /></a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
- [🎉 Features](#-features)
|
||||
- [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages)
|
||||
- [🗂 JSON Resume](#-json-resume)
|
||||
- [🖼 Project images](#-project-images)
|
||||
- [🐱 GitHub repositories](#-github-repositories)
|
||||
- [📍 Location](#-location)
|
||||
- [💅 Theme switcher](#-theme-switcher)
|
||||
- [🏆 SEO component](#-seo-component)
|
||||
- [📇 Client-side vCard creation](#-client-side-vcard-creation)
|
||||
- [💫 Page transitions](#-page-transitions)
|
||||
- [📈 Matomo (formerly Piwik) analytics tracking](#-matomo-formerly-piwik-analytics-tracking)
|
||||
- [🖼 Project images](#-project-images)
|
||||
- [💎 Importing SVG assets](#-importing-svg-assets)
|
||||
- [🍬 Typekit component](#-typekit-component)
|
||||
- [✨ Development](#-development)
|
||||
@ -37,28 +36,27 @@
|
||||
|
||||
## 🎉 Features
|
||||
|
||||
The whole [portfolio](https://matthiaskretschmann.com) is a React-based single page app built with [Gatsby v3](https://www.gatsbyjs.org).
|
||||
The whole [portfolio](https://matthiaskretschmann.com) is a React-based single page app built with [Next.js](https://nextjs.org) in Typescript, using only statically generated pages.
|
||||
|
||||
Most metadata is powered by one `resume.json` file based on [🗂 JSON Resume](#-json-resume), and one `projects.yml` file to [define the displayed projects](#-one-data-file-to-rule-all-pages).
|
||||
If you are looking for the former Gatsby-based app, it is archived in the [`gatsby-deprecated`](https://github.com/kremalicious/portfolio/tree/gatsby-deprecated) branch.
|
||||
|
||||
### 💍 One data file to rule all pages
|
||||
|
||||
All displayed project content is powered by one YAML file where all the portfolio's projects are defined. The project description itself is transformed from Markdown written inside the YAML file into HTML on build time.
|
||||
|
||||
Gatsby automatically creates pages from each item in that file utilizing the [`{ProjectsYaml.slug}.jsx`](src/pages/{ProjectsYaml.slug}.jsx) template.
|
||||
Next.js automatically creates pages from each item in that file utilizing the [`[slug].tsx`](src/pages/[slug].tsx) template.
|
||||
|
||||
- [`content/projects.yml`](content/projects.yml)
|
||||
- [`src/pages/{ProjectsYaml.slug}.jsx`](src/pages/{ProjectsYaml.slug}.jsx)
|
||||
- [`_content/projects.yml`](_content/projects.yml)
|
||||
- [`src/pages/[slug].tsx`](src/pages/[slug].tsx)
|
||||
|
||||
### 🗂 JSON Resume
|
||||
### 🖼 Project images
|
||||
|
||||
Most site metadata and social profiles are defined in [`content/resume.json`](content/resume.json) based on the [JSON Resume](https://jsonresume.org) standard and used throughout the site as a custom React hook. Additionally, a resume page is created under `/resume`.
|
||||
All project images live under `public/images` and are automatically attached to each project based on the inclusion of the project's `slug` in their filenames.
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
Next.js with `next/image` generates all required image sizes for delivering responsible, responsive images to visitors, including lazy loading of all images. For this to work, images are analyzed on build time and various image metadata is passed down as props.
|
||||
|
||||
- [`content/resume.json`](content/resume.json)
|
||||
- [`src/pages/resume/index.jsx`](src/pages/resume/index.jsx)
|
||||
- [`src/hooks/use-resume.js`](src/hooks/use-resume.js)
|
||||
- [`src/components/ProjectImage/index.tsx`](src/components/ProjectImage/index.tsx)
|
||||
- [`src/lib/content.ts`](src/lib/content.ts)
|
||||
|
||||
### 🐱 GitHub repositories
|
||||
|
||||
@ -68,20 +66,20 @@ On build time, all my public repositories are fetched from GitHub, then filtered
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`gatsby-node.js`](gatsby-node.js)
|
||||
- [`content/repos.yml`](content/repos.yml)
|
||||
- [`src/components/molecules/Repository.jsx`](src/components/molecules/Repository.jsx)
|
||||
- [`src/lib/github.ts`](src/lib/github.ts)
|
||||
- [`_content/repos.json`](_content/repos.json)
|
||||
- [`src/components/Repository/index.tsx`](src/components/Repository/index.tsx)
|
||||
|
||||
### 📍 Location
|
||||
|
||||
On client-side, my current and, if known, my next physical location on a city level is fetched from my (private) [nomadlist.com](https://nomadlist.com) profile and displayed in the header.
|
||||
|
||||
Fetching is split up into a serverless function, a hook, and display component. Fetching is done with a serverless function as to not expose the whole profile response into the browser.
|
||||
Fetching is split up into an external serverless function, a hook, and display component. Fetching is done with a serverless function as to not expose the whole profile response into the browser.
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`src/hooks/useLocation.js`](src/hooks/useLocation.js)
|
||||
- [`src/components/molecules/Location.jsx`](src/components/molecules/Location.jsx)
|
||||
- [`src/hooks/useLocation.ts`](src/hooks/useLocation.ts)
|
||||
- [`src/components/Location/index.tsx`](src/components/Location/index.tsx)
|
||||
- [kremalicious/location](https://github.com/kremalicious/location)
|
||||
|
||||
### 💅 Theme switcher
|
||||
@ -90,8 +88,8 @@ Includes a theme switcher which allows user to toggle between a light and a dark
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx)
|
||||
- [`src/hooks/useDarkMode.js`](src/hooks/useDarkMode.js)
|
||||
- [`src/components/ThemeSwitch/index.tsx`](src/components/ThemeSwitch/index.tsx)
|
||||
|
||||
### 🏆 SEO component
|
||||
|
||||
@ -99,7 +97,7 @@ Includes a SEO component which automatically switches all required `meta` tags f
|
||||
|
||||
If you want to know how, have a look at the respective component:
|
||||
|
||||
- [`src/components/atoms/SEO.jsx`](src/components/atoms/SEO.jsx)
|
||||
- [`src/components/Meta/index.tsx`](src/components/Meta/index.tsx)
|
||||
|
||||
### 📇 Client-side vCard creation
|
||||
|
||||
@ -107,44 +105,14 @@ The _Add to addressbook_ link in the footer automatically creates a downloadable
|
||||
|
||||
If you want to know how, have a look at the respective component:
|
||||
|
||||
- [`src/components/atoms/Vcard.jsx`](src/components/atoms/Vcard.jsx)
|
||||
|
||||
### 💫 Page transitions
|
||||
|
||||
Includes mechanism for transitioning between route changes with full page transitions defined with [Framer Motion](https://www.framer.com/motion/).
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`src/components/Layout.jsx`](src/components/Layout.jsx)
|
||||
- [`src/helpers/wrapPageElement.jsx`](src/helpers/wrapPageElement.jsx)
|
||||
- [`gatsby-browser.js`](gatsby-browser.js)
|
||||
- [`gatsby-ssr.js`](gatsby-ssr.js)
|
||||
|
||||
### 📈 Matomo (formerly Piwik) analytics tracking
|
||||
|
||||
Site sends usage statistics to my own [Matomo](https://matomo.org) installation. To make this work in Gatsby, I created and open sourced a plugin, [gatsby-plugin-matomo](https://github.com/kremalicious/gatsby-plugin-matomo), which is in use on this site.
|
||||
|
||||
- [gatsby-plugin-matomo](https://github.com/kremalicious/gatsby-plugin-matomo)
|
||||
|
||||
### 🖼 Project images
|
||||
|
||||
All project images live under `content/images` and are automatically attached to each project based on the inclusion of the project's `slug` in their filenames.
|
||||
|
||||
All project images make use of the excellent [gatsby-plugin-image](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/) plugin, working in tandem with [gatsby-plugin-sharp](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-sharp) and [gatsby-transformer-sharp](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-sharp).
|
||||
|
||||
All together, Gatsby automatically generates all required image sizes for delivering responsible, responsive images to visitors, including lazy loading of all images. Also includes the [intersection-observer polyfill](https://github.com/w3c/IntersectionObserver) to make lazy loading work properly in Safari.
|
||||
|
||||
All project images use one single component where one main GraphQL query fragment is defined, which then gets used throughout other GraphQL queries.
|
||||
|
||||
- [`src/components/atoms/ProjectImage.jsx`](src/components/atoms/ProjectImage.jsx)
|
||||
- [`src/components/Vcard/index.tsx`](src/components/Vcard/index.tsx)
|
||||
|
||||
### 💎 Importing SVG assets
|
||||
|
||||
All SVG assets under `src/images/` will be converted to React components with the help of [gatsby-plugin-svgr](https://github.com/zabute/gatsby-plugin-svgr). Makes use of [SVGR](https://github.com/smooth-code/svgr) so SVG assets can be imported like so:
|
||||
All SVG assets will be converted to React components with the help of [@svgr/webpack](https://react-svgr.com). Makes use of [SVGR](https://github.com/smooth-code/svgr) so SVG assets can be imported like so:
|
||||
|
||||
```js
|
||||
import { ReactComponent as Logo } from './components/svg/Logo'
|
||||
|
||||
import Logo from './components/svg/Logo'
|
||||
return <Logo />
|
||||
```
|
||||
|
||||
@ -154,32 +122,26 @@ Includes a component for adding the Typekit snippet.
|
||||
|
||||
If you want to know how, have a look at the respective component:
|
||||
|
||||
- [`src/components/atoms/Typekit.jsx`](src/components/atoms/Typekit.jsx)
|
||||
- [`src/components/Typekit/index.tsx`](src/components/Typekit/index.tsx)
|
||||
|
||||
## ✨ Development
|
||||
|
||||
You can simply use [Docker](https://www.docker.com) & [Docker Compose](https://docs.docker.com/compose/) or install and run dependencies on your local system.
|
||||
|
||||
```bash
|
||||
git clone git@github.com:kremalicious/portfolio.git
|
||||
cd portfolio/
|
||||
|
||||
# GATSBY_GITHUB_TOKEN is required for some parts
|
||||
# GITHUB_TOKEN is required for some parts
|
||||
# See https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token
|
||||
cp .env.sample .env
|
||||
vi .env
|
||||
|
||||
# use Docker
|
||||
docker-compose up
|
||||
|
||||
# or go with local system
|
||||
npm i
|
||||
npm start
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 🔮 Linting
|
||||
|
||||
ESlint, Prettier, and Stylelint are setup for all linting purposes:
|
||||
ESLint, Prettier, and Stylelint are setup for all linting purposes:
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
@ -189,20 +151,19 @@ To automatically format all code files:
|
||||
|
||||
```bash
|
||||
npm run format
|
||||
npm run format:css
|
||||
```
|
||||
|
||||
### 👩🔬 Testing
|
||||
|
||||
Test suite is setup with [Jest](https://jestjs.io) and [react-testing-library](https://github.com/kentcdodds/react-testing-library).
|
||||
|
||||
To run all tests, including all linting tests:
|
||||
To run all tests, including type checking and linting of all files:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
Most test files live beside the respective component. Testing setup, fixtures, and mocks can be found in the `./tests/` folder.
|
||||
Most test files live beside the respective component. Testing setup, fixtures, and mocks can be found in the `tests/` folder.
|
||||
|
||||
### 🎈 Add a new project
|
||||
|
||||
@ -212,9 +173,9 @@ To add a new project, run the following command. This adds a new item to the top
|
||||
npm run new -- "Hello"
|
||||
```
|
||||
|
||||
Then continue modifying the new entry in [`content/projects.yml`](content/projects.yml).
|
||||
Then continue modifying the new entry in [`_content/projects.yml`](_content/projects.yml).
|
||||
|
||||
Finally, add as many images as needed with the file name format and put into `content/images/`:
|
||||
Finally, add as many images as needed with the file name format and put into `public/images/`:
|
||||
|
||||
```text
|
||||
SLUG-01.png
|
||||
@ -241,7 +202,7 @@ Upon live deployment, deploy script also pings search engines. GitHub requires t
|
||||
|
||||
## 🏛 Licenses
|
||||
|
||||
**© Copyright 2019 Matthias Kretschmann**
|
||||
**© Copyright 2022 Matthias Kretschmann**
|
||||
|
||||
All images and projects are plain ol' copyright, most displayed projects are subject to the copyright of their respective owners.
|
||||
|
||||
|
21
_content/meta.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"description": "Portfolio of web & ui designer/developer Matthias Kretschmann.",
|
||||
"img": "twitter-card.png",
|
||||
"url": "https://matthiaskretschmann.com",
|
||||
"availability": {
|
||||
"status": false,
|
||||
"available": "👔 Available for new projects. <a href=\"mailto:m@kretschmann.io\">Let’s talk</a>!",
|
||||
"unavailable": "Not available for new projects."
|
||||
},
|
||||
"gpg": "https://kretschmann.io/pub.gpg",
|
||||
"addressbook": "/matthias-kretschmann.vcf",
|
||||
"bugs": "https://github.com/kremalicious/portfolio/issues/new",
|
||||
"matomoUrl": "https://analytics.kremalicious.com",
|
||||
"matomoSite": "2",
|
||||
"allowedHosts": [
|
||||
"matthiaskretschmann.com",
|
||||
"beta.matthiaskretschmann.com",
|
||||
"localhost",
|
||||
"05.local"
|
||||
]
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
- title: 'Ocean Market'
|
||||
slug: '/oceanprotocol-market/'
|
||||
img: 'images/oceanprotocol-market-01.png'
|
||||
description: >
|
||||
- title: Ocean Market
|
||||
slug: oceanprotocol-market
|
||||
description: |
|
||||
As part of Ocean Protocol v3, I was leading the planning and execution of the Ocean Market product and decentralized web app in 2020. Ocean Market allows simple tokenization, secure sharing, and monetization of data assets within the Ethereum network with the help of data tokens.
|
||||
|
||||
Assets can be exposed to dynamic price discovery by attaching Automated Market Maker (AMM) pools to them. Metadata is stored on-chain, and all actions like downloading or Compute-to-Data are done via exchanging data tokens, creating trustless provenance records in the process.
|
||||
@ -10,16 +9,17 @@
|
||||
links:
|
||||
- title: Ocean Makes Multi-Network Even Easier
|
||||
icon: FileText
|
||||
url: https://kremalicious.com/ocean-makes-multi-network-even-easier
|
||||
url: 'https://kremalicious.com/ocean-makes-multi-network-even-easier'
|
||||
- title: 'Ocean Market: An Open-Source Community Marketplace for Data'
|
||||
icon: FileText
|
||||
url: https://blog.oceanprotocol.com/ocean-market-an-open-source-community-marketplace-for-data-4b99bedacdc3
|
||||
url: |
|
||||
https://blog.oceanprotocol.com/ocean-market-an-open-source-community-marketplace-for-data-4b99bedacdc3
|
||||
- title: market.oceanprotocol.com
|
||||
icon: Compass
|
||||
url: https://market.oceanprotocol.com
|
||||
url: 'https://market.oceanprotocol.com'
|
||||
- title: '@oceanprotocol/market'
|
||||
icon: GitHub
|
||||
url: https://github.com/oceanprotocol/market
|
||||
url: 'https://github.com/oceanprotocol/market'
|
||||
techstack:
|
||||
- HTML
|
||||
- CSS
|
||||
@ -30,24 +30,23 @@
|
||||
- 3Box
|
||||
- Polygon
|
||||
- Binance Smart Chain
|
||||
|
||||
- title: 'Ocean Protocol - IPFS Integration'
|
||||
slug: '/oceanprotocol-ipfs/'
|
||||
img: 'images/oceanprotocol-ipfs-01.png'
|
||||
description: >
|
||||
- title: Ocean Protocol - IPFS Integration
|
||||
slug: oceanprotocol-ipfs
|
||||
description: |
|
||||
In 2019 I was leading the integration of IPFS into the Ocean Protocol stack, from the core to the libraries, up to the UI in multiple touchpoints.
|
||||
|
||||
For making IPFS as decentralized file storage solution as seemless as possible to use within Ocean Protocol, a public IPFS node was created with a simple user-facing UI and API. The integration was completed by allowing users to add their files to IPFS directly in the [Commons](/oceanprotocol-commons/) publish flow.
|
||||
links:
|
||||
- title: Ocean Protocol and IPFS, Sitting In The Merkle Tree
|
||||
- title: 'Ocean Protocol and IPFS, Sitting In The Merkle Tree'
|
||||
icon: FileText
|
||||
url: https://kremalicious.com/ocean-protocol-and-ipfs-sitting-in-the-merkle-tree
|
||||
url: |
|
||||
https://kremalicious.com/ocean-protocol-and-ipfs-sitting-in-the-merkle-tree
|
||||
- title: ipfs.oceanprotocol.com
|
||||
icon: Compass
|
||||
url: https://ipfs.oceanprotocol.com
|
||||
url: 'https://ipfs.oceanprotocol.com'
|
||||
- title: '@oceanprotocol/ipfs'
|
||||
icon: GitHub
|
||||
url: https://github.com/oceanprotocol/ipfs
|
||||
url: 'https://github.com/oceanprotocol/ipfs'
|
||||
techstack:
|
||||
- HTML
|
||||
- CSS
|
||||
@ -58,11 +57,9 @@
|
||||
- Kubernetes
|
||||
- Jest
|
||||
- IPFS
|
||||
|
||||
- title: 'Ocean Protocol - Commons'
|
||||
slug: '/oceanprotocol-commons/'
|
||||
img: 'images/oceanprotocol-commons-01.png'
|
||||
description: >
|
||||
- title: Ocean Protocol - Commons
|
||||
slug: oceanprotocol-commons
|
||||
description: |
|
||||
From 2018–2019 I was leading the design and development of the Commons marketplace, a Web3 data marketplace to explore, download, and publish open data sets registered in the [Ocean Protocol](/oceanprotocol) network.
|
||||
|
||||
As the main front-facing UI of Ocean Protocol v1 & v2, it served as a product and showcase bringing together the complete Ocean Protocol stack in a simple interface. Additionally, the project was created with developers in mind, making Commons the most complete boilerplate for creating dApps on top of Ocean Protocol.
|
||||
@ -71,16 +68,16 @@
|
||||
links:
|
||||
- title: The Commons Marketplace in Pacific Network
|
||||
icon: FileText
|
||||
url: https://kremalicious.com/the-commons-marketplace-in-pacific-network
|
||||
url: 'https://kremalicious.com/the-commons-marketplace-in-pacific-network'
|
||||
- title: The Commons Marketplace
|
||||
icon: FileText
|
||||
url: https://kremalicious.com/the-commons-marketplace
|
||||
url: 'https://kremalicious.com/the-commons-marketplace'
|
||||
- title: commons.oceanprotocol.com
|
||||
icon: Compass
|
||||
url: https://commons.oceanprotocol.com
|
||||
url: 'https://commons.oceanprotocol.com'
|
||||
- title: '@oceanprotocol/commons'
|
||||
icon: GitHub
|
||||
url: https://github.com/oceanprotocol/commons
|
||||
url: 'https://github.com/oceanprotocol/commons'
|
||||
techstack:
|
||||
- HTML
|
||||
- SCSS
|
||||
@ -93,11 +90,9 @@
|
||||
- Cypress
|
||||
- Jest
|
||||
- IPFS
|
||||
|
||||
- title: 'Ocean Protocol v1'
|
||||
slug: '/oceanprotocol-v1/'
|
||||
img: 'images/oceanprotocol-v1-01.png'
|
||||
description: >
|
||||
- title: Ocean Protocol v1
|
||||
slug: oceanprotocol-v1
|
||||
description: |
|
||||
Since 2017 I'm leading the UI design & development of Ocean Protocol, iterating on a components-based UI design system spanning all of Ocean Protocol's web properties. Additionally, I conceptualize, execute and iterate on the creative and visual direction of the Ocean Protocol brand.
|
||||
|
||||
Most web interfaces are single-page JavaScript applications built with React, pulling their data from multiple sources. All design & development is embedded in continuous deployment processes via GitHub, Travis, Kubernetes, and Vercel.
|
||||
@ -105,23 +100,21 @@
|
||||
In 2020 I was leading the refresh of Ocean Protocol's visual identity for the release of v3 and the [Ocean Market](/oceanprotocol-market/).
|
||||
|
||||
Initial website in collaboration with [Balance](https://balance.io/). Key visuals in collaboration with [Wojciech Hupert](https://twitter.com/wojciechhupert).
|
||||
|
||||
links:
|
||||
- title: oceanprotocol.com
|
||||
icon: Compass
|
||||
url: https://oceanprotocol.com
|
||||
url: 'https://oceanprotocol.com'
|
||||
- title: Styleguide
|
||||
url: https://oceanprotocol.com/art
|
||||
url: 'https://oceanprotocol.com/art'
|
||||
- title: docs.oceanprotocol.com
|
||||
icon: Compass
|
||||
url: https://docs.oceanprotocol.com
|
||||
url: 'https://docs.oceanprotocol.com'
|
||||
- title: '@oceanprotocol/art'
|
||||
icon: GitHub
|
||||
url: https://github.com/oceanprotocol/art
|
||||
url: 'https://github.com/oceanprotocol/art'
|
||||
- title: '@oceanprotocol/docs'
|
||||
icon: GitHub
|
||||
url: https://github.com/oceanprotocol/docs
|
||||
|
||||
url: 'https://github.com/oceanprotocol/docs'
|
||||
techstack:
|
||||
- Sketch
|
||||
- Affinity Designer
|
||||
@ -139,17 +132,14 @@
|
||||
- Docker
|
||||
- Kubernetes
|
||||
- IPFS
|
||||
|
||||
- title: IPDB
|
||||
slug: /ipdb/
|
||||
img: 'images/ipdb-01.png'
|
||||
description: >
|
||||
slug: ipdb
|
||||
description: |
|
||||
From 2015–2017 I was leading the UI design & development of all IPDB web properties and additionally iterated on the creative and visual direction of the IPDB brand.
|
||||
|
||||
The main website is a static site built with Jekyll and a custom Gulp-based build pipeline in front of it. All design & development is embedded in a continuous deployment process via GitHub & Travis.
|
||||
|
||||
Branding and key visuals in collaboration with [Wojciech Hupert](https://twitter.com/wojciechhupert).
|
||||
|
||||
techstack:
|
||||
- Sketch
|
||||
- Jekyll
|
||||
@ -161,17 +151,14 @@
|
||||
- AWS S3
|
||||
- Cloudflare
|
||||
- 3Scale
|
||||
|
||||
links:
|
||||
- title: GitHub
|
||||
url: https://github.com/ipdb/website
|
||||
|
||||
icon: GitHub
|
||||
url: 'https://github.com/ipdb/website'
|
||||
- title: Berlin Innovation Ventures
|
||||
slug: /biv/
|
||||
img: images/biv-01.png
|
||||
description: >
|
||||
slug: biv
|
||||
description: |
|
||||
I designed & developed the website and a basic branding for the Berlin-based VC firm Berlin Innovation Ventures. The main website is a static site built with Jekyll and a custom Gulp-based build pipeline in front of it.
|
||||
|
||||
techstack:
|
||||
- Sketch
|
||||
- Jekyll
|
||||
@ -179,22 +166,21 @@
|
||||
- HTML
|
||||
- SCSS
|
||||
- JavaScript
|
||||
|
||||
links:
|
||||
- title: Link
|
||||
url: http://berlininnovation.vc
|
||||
|
||||
- title: '9984 >> Summit 2017'
|
||||
slug: /9984/
|
||||
img: images/9984-01.png
|
||||
icon: Compass
|
||||
url: 'http://berlininnovation.vc'
|
||||
- title: 9984 >> Summit 2017
|
||||
slug: '9984'
|
||||
img: /images/9984-01.png
|
||||
links:
|
||||
- title: Link
|
||||
url: https://2017.9984.io
|
||||
url: 'https://2017.9984.io'
|
||||
- title: Styleguide
|
||||
url: https://2017.9984.io/styleguide/
|
||||
url: 'https://2017.9984.io/styleguide'
|
||||
- title: GitHub
|
||||
url: https://github.com/9984/2017.9984.io
|
||||
description: >
|
||||
url: 'https://github.com/9984/2017.9984.io'
|
||||
description: |
|
||||
In 2017 I was leading the UI design & development for the 9984 >> Summit, the first joint summit of BigchainDB & IPDB. Additionally, I conceptualized, executed and iterated on the creative and visual direction of the 9984 brand.
|
||||
|
||||
The main website is a static site built with Jekyll and a custom Gulp-based build pipeline in front of it. All design & development is embedded in a continuous deployment process via GitHub & Travis.
|
||||
@ -210,17 +196,14 @@
|
||||
- Travis
|
||||
- AWS S3
|
||||
- Cloudflare
|
||||
|
||||
- title: BigchainDB
|
||||
slug: /bigchaindb/
|
||||
img: images/bigchaindb-01.png
|
||||
description: >
|
||||
slug: bigchaindb
|
||||
description: |
|
||||
From 2016–2019 I was leading the UI design & development of all BigchainDB web properties. I created the initial BigchainDB brand and further conceptualized, executed and iterated on the creative and visual direction of BigchainDB. This included creating and iterating on a components-based UI design system for all of BigchainDB's web properties.
|
||||
|
||||
The main website is a static site built with Jekyll and a custom Gulp-based build pipeline in front of it, pulling data from various external sources and microservices. All design & development is embedded in a continuous deployment process via GitHub & Travis.
|
||||
|
||||
Branding & key visuals in collaboration with [Wojciech Hupert](https://twitter.com/wojciechhupert).
|
||||
|
||||
techstack:
|
||||
- BigchainDB
|
||||
- Sketch
|
||||
@ -234,27 +217,23 @@
|
||||
- Travis
|
||||
- AWS S3
|
||||
- Cloudflare
|
||||
|
||||
links:
|
||||
- title: Link
|
||||
url: https://www.bigchaindb.com
|
||||
url: 'https://www.bigchaindb.com'
|
||||
- title: Styleguide
|
||||
url: https://www.bigchaindb.com/styleguide/
|
||||
url: 'https://www.bigchaindb.com/styleguide'
|
||||
- title: GitHub
|
||||
url: https://github.com/bigchaindb/site
|
||||
url: 'https://github.com/bigchaindb/site'
|
||||
- title: Dribbble
|
||||
url: https://dribbble.com/shots/2522184-BigchainDB-site
|
||||
|
||||
url: 'https://dribbble.com/shots/2522184-BigchainDB-site'
|
||||
- title: ascribe
|
||||
slug: /ascribe/
|
||||
img: images/ascribe-01.png
|
||||
description: >
|
||||
slug: ascribe
|
||||
description: |
|
||||
With ascribe, users were able to tokenize digital art and real life assets onto the Bitcoin blockchain. It was one of the very first decentralized apps using a decentralized ledger as its backend, all before Ethereum even existed. The principles around digital art as entries in a blockchain, given out in editions, would later inspire the ERC-721 NFT standard on the Ethereum blockchain. Finally, the learnings around a decentralized approach for storing structured data led to the creation of [BigchainDB](/bigchaindb).
|
||||
|
||||
From 2015-2017 I worked on the UI development for multiple web touch points, including the main web app built with one of the first versions of React. The service and web app was [discontinued in 2017](https://www.ascribe.io/).
|
||||
|
||||
Branding & key visuals in collaboration with [Wojciech Hupert](https://twitter.com/wojciechhupert).
|
||||
|
||||
techstack:
|
||||
- ascribe
|
||||
- Sketch
|
||||
@ -269,17 +248,14 @@
|
||||
- Cloudflare
|
||||
- React
|
||||
- Bitcoin
|
||||
|
||||
links:
|
||||
- title: Link
|
||||
url: https://www.ascribe.io
|
||||
url: 'https://www.ascribe.io'
|
||||
- title: GitHub
|
||||
url: https://github.com/ascribe
|
||||
|
||||
url: 'https://github.com/ascribe'
|
||||
- title: ChartMogul
|
||||
slug: /chartmogul/
|
||||
img: images/chartmogul-01.png
|
||||
description: >
|
||||
slug: chartmogul
|
||||
description: |
|
||||
From 2015–2017 I was co-designing and leading the UI design & development of various ChartMogul web properties. This included the creation of a components-based UI design system and implementing it across all web touch points.
|
||||
|
||||
The main website with its landing pages is a static site built with Jekyll and a custom Gulp-based build pipeline in front of it, while the blog is running on WordPress with its own custom theme. All embedded in an automated development & deployment workflow via GitHub and Travis.
|
||||
@ -287,7 +263,6 @@
|
||||
Besides designing and implementing new features, I maintained the front-end of the ChartMogul application and implemented the UI design system by refactoring most of its front-end codebase.
|
||||
|
||||
All branding, design & key visuals directed by Michelle Myung.
|
||||
|
||||
techstack:
|
||||
- Sketch
|
||||
- Affinity Designer
|
||||
@ -304,23 +279,19 @@
|
||||
- Cloudflare
|
||||
- Ruby on Rails
|
||||
- Backbone.js
|
||||
|
||||
links:
|
||||
- title: Link
|
||||
url: https://chartmogul.com/
|
||||
url: 'https://chartmogul.com'
|
||||
- title: Styleguide
|
||||
url: https://chartmogul.com/styleguide/
|
||||
url: 'https://chartmogul.com/styleguide'
|
||||
- title: Dribbble
|
||||
url: https://dribbble.com/kremalicious/projects/311439-ChartMogul
|
||||
|
||||
url: 'https://dribbble.com/kremalicious/projects/311439-ChartMogul'
|
||||
- title: ShareTheMeal
|
||||
slug: /sharethemeal/
|
||||
img: images/sharethemeal-01.png
|
||||
description: >
|
||||
slug: sharethemeal
|
||||
description: |
|
||||
ShareTheMeal is an app from the United Nations World Food Programme (WFP) that enables people to "share their meals" with children in need. In 2015 I was consulting, co-designing and leading the front-end development of the ShareTheMeal website and various parts of the ShareTheMeal apps for iOS & Android.
|
||||
|
||||
The main website is a static site built with Jekyll and a custom Gulp-based build pipeline in front of it, embedded in a continuous deployment process via GitHub & Travis.
|
||||
|
||||
techstack:
|
||||
- Sketch
|
||||
- Illustrator
|
||||
@ -333,21 +304,17 @@
|
||||
- AWS S3
|
||||
- Cloudflare
|
||||
- Node.js
|
||||
|
||||
links:
|
||||
- title: Link
|
||||
url: https://sharethemeal.org/
|
||||
|
||||
url: 'https://sharethemeal.org'
|
||||
- title: ezeep
|
||||
slug: /ezeep/
|
||||
img: images/ezeep-01.png
|
||||
description: >
|
||||
From 2012–2015 I worked at ezeep, where I helped creating an unprecedented, market-leading & award-winning user experience based on the principles of emotional design way ahead of all competitors. This included conceptualizing executing, and iterating on the creative & visual direction of the ezeep brand.
|
||||
slug: ezeep
|
||||
description: |
|
||||
From 2012–2015 I helped create an unprecedented, market-leading & award-winning user experience based on the principles of emotional design way ahead of all competitors. This included conceptualizing executing, and iterating on the creative & visual direction of the ezeep brand.
|
||||
|
||||
I was leading the UI design & development of all ezeep touch points and - as a product designer - defined the ezeep product based on user and market research in an iterative process. On top of that, I designed and helped building all app experiences of ezeep on Windows, macOS, iOS, and Android.
|
||||
|
||||
ezeep was acquired by [Cortado AG](https://www.cortado.com) in 2015 and became part of their [ThinPrint Cloud Services](https://www.thinprintcloud.com) suite of products.
|
||||
|
||||
techstack:
|
||||
- Photoshop
|
||||
- Illustrator
|
||||
@ -364,55 +331,43 @@
|
||||
- Node.js
|
||||
- Backbone.js
|
||||
- Electron
|
||||
|
||||
links:
|
||||
- title: Info
|
||||
url: https://kremalicious.com/enterprise-software-sucks/
|
||||
url: 'https://kremalicious.com/enterprise-software-sucks'
|
||||
- title: Dribbble
|
||||
url: https://dribbble.com/kremalicious/projects/84318-ezeep
|
||||
|
||||
url: 'https://dribbble.com/kremalicious/projects/84318-ezeep'
|
||||
- title: Mr. Reader
|
||||
slug: /mrreader/
|
||||
img: images/mrreader-01.png
|
||||
description: >
|
||||
slug: mrreader
|
||||
description: |
|
||||
While working with indy iOS developer Curious Times in 2012, I designed the app icon, a custom theme, and various promotion materials for Mr. Reader, a powerful and highly loved RSS feed reader for iPad.
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
- title: iPixelPad
|
||||
slug: /ipixelpad/
|
||||
img: images/ipixelpad-01.png
|
||||
description: >
|
||||
slug: ipixelpad
|
||||
description: |
|
||||
So, what to do when everyone seem to release iPad icons but fail to include some crisp small size icons? Pushing the pixels for yourself of course. So here’s my take on the smaller sizes of an Apple iPad icon, called iPixelPad.
|
||||
|
||||
Released as a goodie on [kremalicious.com](https://kremalicious.com/ipixelpad/).
|
||||
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
links:
|
||||
- title: Info & Download
|
||||
url: https://kremalicious.com/ipixelpad/
|
||||
|
||||
url: 'https://kremalicious.com/ipixelpad'
|
||||
- title: Out Of Whale Oil
|
||||
slug: /outofwhaleoil/
|
||||
img: images/outofwhaleoil-01.jpg
|
||||
description: >
|
||||
slug: outofwhaleoil
|
||||
description: |
|
||||
Tribute wallpaper pack inspired by the Futurama movie _Into The Wild Green Yonder_. Released as a goodie on [kremalicious.com](https://kremalicious.com/out-of-whale-oil/).
|
||||
links:
|
||||
- title: Info & Download
|
||||
url: https://kremalicious.com/out-of-whale-oil/
|
||||
url: 'https://kremalicious.com/out-of-whale-oil'
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
- title: 'Martin-Luther-Universität Halle-Wittenberg'
|
||||
slug: /unihalle/
|
||||
img: images/unihalle-01.png
|
||||
description: >
|
||||
- title: Martin-Luther-Universität Halle-Wittenberg
|
||||
slug: unihalle
|
||||
description: |
|
||||
From 2009–2012 I worked at the IT services department of [Martin-Luther-Universität Halle-Wittenberg](http://www.uni-halle.de) where I conceptualized, designed & implemented numerous in-house and public facing interfaces for thousands of students and staff.
|
||||
|
||||
Additionally, I conceptualized, designed, created, and maintained the blog network & community for all students & staff.
|
||||
|
||||
techstack:
|
||||
- Photoshop
|
||||
- Illustrator
|
||||
@ -424,46 +379,34 @@
|
||||
- WordPress
|
||||
- Ilias
|
||||
- Stud.IP
|
||||
|
||||
links:
|
||||
- title: Link
|
||||
url: http://blogs.urz-uni-halle.de
|
||||
url: 'http://blogs.urz-uni-halle.de'
|
||||
- title: Dribbble
|
||||
url: https://dribbble.com/kremalicious/projects/690029-MLU
|
||||
|
||||
url: 'https://dribbble.com/kremalicious/projects/690029-MLU'
|
||||
- title: Coffee Cup
|
||||
slug: /coffeecup/
|
||||
img: images/coffeecup-01.png
|
||||
description: >
|
||||
Desktop icons showing the fuel of most designers. Released as a goodie
|
||||
on [kremalicious.com](https://kremalicious.com/coffee-cup-icon/).
|
||||
|
||||
slug: coffeecup
|
||||
description: |
|
||||
Desktop icons showing the fuel of most designers. Released as a goodie on [kremalicious.com](https://kremalicious.com/coffee-cup-icon/).
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
links:
|
||||
- title: Info & Download
|
||||
url: https://kremalicious.com/coffee-cup-icon/
|
||||
|
||||
url: 'https://kremalicious.com/coffee-cup-icon'
|
||||
- title: Project Purple
|
||||
slug: /projectpurple/
|
||||
img: images/projectpurple-01.png
|
||||
description: >
|
||||
slug: projectpurple
|
||||
description: |
|
||||
It had been revealed the original iPhone was developed in a locked down building under the name Project Purple and because of the secrecy involved, the team decorated the building with Fight Club references. Perfect story to create a wallpaper out of it.
|
||||
|
||||
Released as a goodie on [kremalicious.com](https://kremalicious.com/projectpurple/).
|
||||
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
links:
|
||||
- title: Info & Download
|
||||
url: https://kremalicious.com/projectpurple/
|
||||
|
||||
url: 'https://kremalicious.com/projectpurple'
|
||||
- title: Allinnia Creative Group
|
||||
slug: /allinnia/
|
||||
img: images/allinnia-01.png
|
||||
description: >
|
||||
slug: allinnia
|
||||
description: |
|
||||
In 2009 I created the branding, website, and various key visuals for professional music production studio Allinnia Creative Group, reflecting their own musical compositions.
|
||||
|
||||
The website was built from scratch as a simple PHP application with a store and music listening functionality.
|
||||
@ -473,38 +416,30 @@
|
||||
- CSS
|
||||
- JavaScript
|
||||
- PHP
|
||||
|
||||
- title: Aperture Loupe
|
||||
slug: /apertureloupe/
|
||||
img: images/apertureloupe-01.png
|
||||
description: >
|
||||
slug: apertureloupe
|
||||
description: |
|
||||
When Apple released their professional photography app _Aperture_ in 2008, the loupe tool in there was something really novel and just fun to play with. Inspired by that, I created this macOS desktop icon.
|
||||
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
- title: Adiumeetie
|
||||
slug: /adiumeetie/
|
||||
img: images/adiumeetie-01.png
|
||||
description: >
|
||||
slug: adiumeetie
|
||||
description: |
|
||||
A macOS replacement desktop icon created in 2009 for the popular Mac IM client Adium following the style of atebit’s excellent Tweetie for Mac icon. Released as a goodie on [kremalicious.com](https://kremalicious.com/adiumeetie/).
|
||||
|
||||
techstack:
|
||||
- Photoshop
|
||||
|
||||
links:
|
||||
- title: Download
|
||||
url: https://kremalicious.com/adiumeetie/
|
||||
|
||||
- title: "Niépce's Camera Obscura"
|
||||
slug: /niepces-camera-obscura/
|
||||
img: images/niepces-camera-obscura-01.jpg
|
||||
description: >
|
||||
url: 'https://kremalicious.com/adiumeetie'
|
||||
- title: Niépce's Camera Obscura
|
||||
slug: niepces-camera-obscura
|
||||
description: |
|
||||
In 2008 I used the camera obscura as it was used by Nicéphore Niépce in 1826 to create an Aperture and iPhoto replacement icon.
|
||||
|
||||
Nicéphore Niépce made it first possible to preserve an image taken with a camera obscura in 1826 or 1827 by using a special mixture of bitumen on a glass or metal plate, naming it Heliography. This first preserved image 'View from the Window at Le Gras' is the one you can see in the iPhoto icon.
|
||||
links:
|
||||
- title: Info
|
||||
url: https://kremalicious.com/niepces-camera-obscura-and-the-history-of-the-first-photograph/
|
||||
url: |
|
||||
https://kremalicious.com/niepces-camera-obscura-and-the-history-of-the-first-photograph
|
||||
techstack:
|
||||
- Photoshop
|
12
_content/repos.json
Normal file
@ -0,0 +1,12 @@
|
||||
[
|
||||
"kremalicious/portfolio",
|
||||
"kremalicious/blog",
|
||||
"kremalicious/blowfish",
|
||||
"kremalicious/gatsby-plugin-matomo",
|
||||
"kremalicious/gatsby-redirect-from",
|
||||
"kremalicious/hyper-mac-pro",
|
||||
"kremalicious/appstorebadges",
|
||||
"kremalicious/kbdfun",
|
||||
"kremalicious/ipfs",
|
||||
"oceanprotocol/market"
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# most personal metadata can be found in ./resume.json
|
||||
|
||||
- description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
|
||||
img: ../src/images/twitter-card.png
|
||||
|
||||
availability:
|
||||
status: false
|
||||
available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Let’s talk</a>!'
|
||||
unavailable: Not available for new projects.
|
||||
|
||||
# Footer actions
|
||||
gpg: https://kretschmann.io/pub.gpg
|
||||
addressbook: /matthias-kretschmann.vcf
|
||||
bugs: https://github.com/kremalicious/portfolio/issues/new
|
||||
|
||||
# Analytics tools
|
||||
matomoUrl: https://analytics.kremalicious.com
|
||||
matomoSite: 2
|
||||
|
||||
allowedHosts:
|
||||
- matthiaskretschmann.com
|
||||
- beta.matthiaskretschmann.com
|
||||
- localhost
|
||||
- 05.local
|
@ -1,10 +0,0 @@
|
||||
- kremalicious/portfolio
|
||||
- kremalicious/blog
|
||||
- kremalicious/blowfish
|
||||
- kremalicious/gatsby-plugin-matomo
|
||||
- kremalicious/gatsby-redirect-from
|
||||
- kremalicious/hyper-mac-pro
|
||||
- kremalicious/appstorebadges
|
||||
- kremalicious/kbdfun
|
||||
- kremalicious/ipfs
|
||||
- oceanprotocol/market
|
@ -1,10 +0,0 @@
|
||||
version: '3'
|
||||
services:
|
||||
dev:
|
||||
build: .
|
||||
command: npm start
|
||||
volumes:
|
||||
- .:/portfolio
|
||||
- /portfolio/node_modules
|
||||
ports:
|
||||
- '8000:8000'
|
@ -1,36 +0,0 @@
|
||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||
|
||||
// Global styles
|
||||
import './src/styles/global.css'
|
||||
import './src/styles/_toast.css'
|
||||
|
||||
// IntersectionObserver polyfill for gatsby-image (Safari, IE)
|
||||
if (typeof window !== 'undefined' && !window.IntersectionObserver) {
|
||||
import('intersection-observer')
|
||||
}
|
||||
|
||||
// Layout with Page Transitions
|
||||
export const wrapPageElement = wrapPageElementWithLayout
|
||||
|
||||
export const shouldUpdateScroll = ({
|
||||
routerProps: { location },
|
||||
getSavedScrollPosition
|
||||
}) => {
|
||||
if (location.action === 'PUSH') {
|
||||
window.setTimeout(() => window.scrollTo(0, 0), 200)
|
||||
} else {
|
||||
const savedPosition = getSavedScrollPosition(location)
|
||||
window.setTimeout(() => window.scrollTo(...(savedPosition || [0, 0])), 200)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Display a message when a service worker updates
|
||||
// https://www.gatsbyjs.org/docs/add-offline-support-with-a-service-worker/#displaying-a-message-when-a-service-worker-updates
|
||||
export const onServiceWorkerUpdateReady = () => {
|
||||
const div = document.createElement('div')
|
||||
div.id = 'toast'
|
||||
div.classList.add('alert', 'alert-info')
|
||||
div.innerHTML = `<button onClick="window.location.reload()">Updates are available. <span>Click to Reload</span>.</button>`
|
||||
document.body.append(div)
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const yaml = require('js-yaml')
|
||||
const meta = yaml.load(fs.readFileSync('./content/meta.yml', 'utf8'))
|
||||
const resume = require('./content/resume.json')
|
||||
const { matomoSite, matomoUrl } = meta[0]
|
||||
const { name, website } = resume.basics
|
||||
|
||||
require('dotenv').config()
|
||||
|
||||
if (
|
||||
!process.env.GATSBY_GITHUB_TOKEN ||
|
||||
process.env.GATSBY_GITHUB_TOKEN === 'xxx'
|
||||
) {
|
||||
throw Error(`
|
||||
⚠️ A GitHub token as GATSBY_GITHUB_TOKEN is required to build some parts of the blog.
|
||||
⚠️ Check the README https://github.com/kremalicious/portfolio#-development.
|
||||
`)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
siteUrl: `${website}`
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
resolve: 'gatsby-source-filesystem',
|
||||
options: {
|
||||
name: 'content',
|
||||
path: path.join(__dirname, 'content')
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-source-filesystem',
|
||||
options: {
|
||||
name: 'images',
|
||||
path: path.join(__dirname, 'src', 'images')
|
||||
}
|
||||
},
|
||||
'gatsby-transformer-yaml',
|
||||
'gatsby-transformer-json',
|
||||
'gatsby-plugin-image',
|
||||
'gatsby-plugin-sharp',
|
||||
'gatsby-transformer-sharp',
|
||||
{
|
||||
resolve: 'gatsby-plugin-svgr',
|
||||
options: {
|
||||
icon: true
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-matomo',
|
||||
options: {
|
||||
siteId: `${matomoSite}`,
|
||||
siteUrl: `${website}`,
|
||||
matomoUrl: `${matomoUrl}`,
|
||||
localScript: '/matomo.js',
|
||||
trackLoad: false
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-manifest',
|
||||
options: {
|
||||
name: name.toLowerCase(),
|
||||
short_name: 'mk',
|
||||
start_url: '/',
|
||||
background_color: '#e7eef4',
|
||||
theme_color: '#e7eef4',
|
||||
icon: 'src/images/favicon.png',
|
||||
display: 'standalone',
|
||||
cache_busting_mode: 'name',
|
||||
theme_color_in_head: false // dynamically set in ThemeSwitch
|
||||
}
|
||||
},
|
||||
'gatsby-plugin-react-helmet',
|
||||
'gatsby-plugin-sitemap',
|
||||
'gatsby-plugin-offline'
|
||||
]
|
||||
}
|
113
gatsby-node.js
@ -1,113 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const remark = require('remark')
|
||||
const parse = require('remark-parse')
|
||||
const html = require('remark-html')
|
||||
const fs = require('fs')
|
||||
const yaml = require('js-yaml')
|
||||
const reposYaml = yaml.load(fs.readFileSync('./content/repos.yml', 'utf8'))
|
||||
const { performance } = require('perf_hooks')
|
||||
const chalk = require('chalk')
|
||||
const { execSync } = require('child_process')
|
||||
const { getGithubRepos } = require('./scripts/github')
|
||||
|
||||
function truncate(n, useWordBoundary) {
|
||||
if (this.length <= n) {
|
||||
return this
|
||||
}
|
||||
const subString = this.substr(0, n - 1)
|
||||
return (
|
||||
(useWordBoundary
|
||||
? subString.substr(0, subString.lastIndexOf(' '))
|
||||
: subString) + '...'
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
// Fetch matomo.js
|
||||
//
|
||||
execSync(`node ./scripts/fetch-matomo-js > static/matomo.js`, {
|
||||
stdio: 'inherit'
|
||||
})
|
||||
|
||||
//
|
||||
// Get GitHub repos once and store for later build stages
|
||||
//
|
||||
let repos
|
||||
|
||||
exports.onPreBootstrap = async () => {
|
||||
const t0 = performance.now()
|
||||
|
||||
try {
|
||||
repos = await getGithubRepos(reposYaml)
|
||||
const t1 = performance.now()
|
||||
const ms = t1 - t0
|
||||
const s = ((ms / 1000) % 60).toFixed(3)
|
||||
console.log(
|
||||
chalk.green('success ') + `getGithubRepos: ${repos.length} repos - ${s} s`
|
||||
)
|
||||
} catch (error) {
|
||||
throw Error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Add pageContext
|
||||
//
|
||||
exports.onCreatePage = async ({ page, actions }) => {
|
||||
const { createPage, deletePage } = actions
|
||||
|
||||
// Regex for auto-attaching project images to pages based on slug.
|
||||
// Image file names follow the pattern slug-01.png.
|
||||
// Regex inspiration from https://stackoverflow.com/a/7124976
|
||||
const imageRegex = `/${page.path.replace(/\//g, '')}+?(?=-\\d)/`
|
||||
|
||||
deletePage(page)
|
||||
createPage({
|
||||
...page,
|
||||
context: {
|
||||
...page.context,
|
||||
imageRegex,
|
||||
// Add repos only to front page's context
|
||||
...(page.path === '/' && { repos })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
exports.onCreateNode = ({ node, actions }) => {
|
||||
const { createNodeField } = actions
|
||||
|
||||
// Projects YAML nodes
|
||||
if (node.internal.type === 'ProjectsYaml') {
|
||||
// Add transformed Markdown descriptions
|
||||
const description = node.description
|
||||
const descriptionWithLineBreaks = description.split('\n').join('\n\n')
|
||||
|
||||
let descriptionHtml
|
||||
|
||||
remark()
|
||||
.use(parse, { gfm: true, commonmark: true, pedantic: true })
|
||||
.use(html)
|
||||
.process(descriptionWithLineBreaks, (err, file) => {
|
||||
if (err) throw Error('Could not transform project description')
|
||||
|
||||
descriptionHtml = file.contents
|
||||
return descriptionHtml
|
||||
})
|
||||
|
||||
createNodeField({
|
||||
node,
|
||||
name: 'descriptionHtml',
|
||||
value: descriptionHtml
|
||||
})
|
||||
|
||||
// Create excerpt from description
|
||||
const excerpt = truncate.apply(description, [320, true])
|
||||
|
||||
createNodeField({
|
||||
node,
|
||||
name: 'excerpt',
|
||||
value: excerpt
|
||||
})
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||
|
||||
// Layout with Page Transitions
|
||||
export const wrapPageElement = wrapPageElementWithLayout
|
5
next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
32
next.config.js
Normal file
@ -0,0 +1,32 @@
|
||||
// @ts-check
|
||||
|
||||
const next = (phase, { defaultConfig }) => {
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
const nextConfig = {
|
||||
webpack: (config, options) => {
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.svg$/,
|
||||
issuer: /\.(tsx|ts)$/,
|
||||
use: [{ loader: '@svgr/webpack', options: { icon: true } }]
|
||||
},
|
||||
{
|
||||
test: /\.gif$/,
|
||||
// yay for webpack 5
|
||||
// https://webpack.js.org/guides/asset-management/#loading-images
|
||||
type: 'asset/resource'
|
||||
}
|
||||
)
|
||||
|
||||
return typeof defaultConfig.webpack === 'function'
|
||||
? defaultConfig.webpack(config, options)
|
||||
: config
|
||||
}
|
||||
}
|
||||
|
||||
return nextConfig
|
||||
}
|
||||
|
||||
export default next
|
34483
package-lock.json
generated
83
package.json
@ -1,83 +1,72 @@
|
||||
{
|
||||
"name": "@kremalicious/portfolio",
|
||||
"description": "Portfolio thingy, built with Gatsby",
|
||||
"description": "Portfolio thingy",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://matthiaskretschmann.com",
|
||||
"repository": "github:kremalicious/portfolio",
|
||||
"license": "MIT",
|
||||
"author": "Matthias Kretschmann <m@kretschmann.io>",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "gatsby develop --host 0.0.0.0",
|
||||
"build": "gatsby build",
|
||||
"ssr": "npm run build && serve -s public/",
|
||||
"lint:js": "eslint ./gatsby-*.js && eslint ./src/**/*.{js,jsx}",
|
||||
"lint:css": "stylelint ./src/**/*.{css,scss}",
|
||||
"start": "npm run pregenerate && next",
|
||||
"build": "npm run pregenerate && next build",
|
||||
"preview": "npm run build && next start",
|
||||
"export": "npm run pregenerate && next export",
|
||||
"typecheck": "tsc",
|
||||
"lint:js": "next lint",
|
||||
"lint:css": "stylelint ./src/**/*.css",
|
||||
"lint": "npm run lint:js && npm run lint:css",
|
||||
"format": "prettier --write 'src/**/*.{js,jsx,css,scss}'",
|
||||
"test": "NODE_ENV=test npm run lint && jest --coverage -c tests/jest.config.js",
|
||||
"test:watch": "NODE_ENV=test npm run lint && jest --coverage --watch -c tests/jest.config.js",
|
||||
"format": "prettier --write 'src/**/*.{ts,tsx,css}'",
|
||||
"jest": "jest --coverage -c tests/jest.config.ts",
|
||||
"test": "NODE_ENV=test npm run lint && npm run typecheck && npm run jest",
|
||||
"deploy:s3": "./scripts/deploy-s3.sh",
|
||||
"new": "babel-node ./scripts/new.js"
|
||||
"new": "ts-node-esm ./scripts/new.ts",
|
||||
"favicon": "ts-node-esm ./scripts/favicon.ts",
|
||||
"pregenerate": "npm run favicon"
|
||||
},
|
||||
"dependencies": {
|
||||
"@giphy/js-fetch-api": "^4.4.0",
|
||||
"@kremalicious/react-feather": "^2.1.0",
|
||||
"@socialgouv/matomo-next": "^1.4.0",
|
||||
"@yaireo/relative-time": "^1.0.2",
|
||||
"axios": "^1.1.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^7.6.4",
|
||||
"gatsby": "^4.24.0",
|
||||
"gatsby-plugin-image": "^2.24.0",
|
||||
"gatsby-plugin-manifest": "^4.24.0",
|
||||
"gatsby-plugin-matomo": "^0.13.0",
|
||||
"gatsby-plugin-offline": "^5.24.0",
|
||||
"gatsby-plugin-react-helmet": "^5.24.0",
|
||||
"gatsby-plugin-sharp": "^4.24.0",
|
||||
"gatsby-plugin-sitemap": "^5.24.0",
|
||||
"gatsby-plugin-svgr": "^3.0.0-beta.0",
|
||||
"gatsby-source-filesystem": "^4.24.0",
|
||||
"gatsby-transformer-json": "^4.24.0",
|
||||
"gatsby-transformer-sharp": "^4.24.0",
|
||||
"gatsby-transformer-yaml": "^4.24.0",
|
||||
"intersection-observer": "^0.12.2",
|
||||
"next": "13.0.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"remark": "13.0.0",
|
||||
"remark-breaks": "2.0.2",
|
||||
"remark-html": "13.0.2",
|
||||
"remark-parse": "9.0.0",
|
||||
"remark-react": "8.0.0",
|
||||
"remark": "^14.0.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-html": "^15.0.1",
|
||||
"vcf": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/node": "^7.20.2",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@welldone-software/why-did-you-render": "^7.0.1",
|
||||
"babel-preset-gatsby": "^2.24.0",
|
||||
"chalk": "4.1.2",
|
||||
"@types/jest": "^29.2.2",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/sharp": "^0.31.0",
|
||||
"chalk": "^5.1.2",
|
||||
"eslint": "^8.27.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-graphql": "^4.0.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.31.10",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^5.9.1",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.2.2",
|
||||
"eslint-config-next": "^13.0.3",
|
||||
"favicons": "^7.0.2",
|
||||
"jest": "^29.3.1",
|
||||
"jest-canvas-mock": "^2.4.0",
|
||||
"jest-environment-jsdom": "^29.2.2",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"ora": "^6.1.2",
|
||||
"prepend": "^1.0.2",
|
||||
"prettier": "^2.7.1",
|
||||
"sharp": "^0.31.2",
|
||||
"slugify": "^1.6.5",
|
||||
"stylelint": "^14.14.1",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-prettier": "^2.0.0"
|
||||
"stylelint-config-prettier": "^9.0.4",
|
||||
"stylelint-prettier": "^2.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 0.2%",
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@ -7,11 +7,11 @@
|
||||
Site : https://matthiaskretschmann.com
|
||||
Twitter : @kremalicious
|
||||
GitHub : @kremalicious
|
||||
Location : Berlin, Germany
|
||||
Location : Lisboa, Portugal
|
||||
|
||||
/* SITE */
|
||||
Typography : Brandon Grotesque, FF Tisa Sans Web Pro
|
||||
Software : Gatsby.js, React, VS Code, Sketch, macOS
|
||||
Software : Next.js, React, VS Code, macOS
|
||||
|
||||
|
||||
<!--
|
Before Width: | Height: | Size: 878 KiB After Width: | Height: | Size: 878 KiB |
Before Width: | Height: | Size: 668 KiB After Width: | Height: | Size: 668 KiB |
Before Width: | Height: | Size: 784 KiB After Width: | Height: | Size: 784 KiB |
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 283 KiB |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
Before Width: | Height: | Size: 437 KiB After Width: | Height: | Size: 437 KiB |
Before Width: | Height: | Size: 625 KiB After Width: | Height: | Size: 625 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 578 KiB After Width: | Height: | Size: 578 KiB |
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
Before Width: | Height: | Size: 901 KiB After Width: | Height: | Size: 901 KiB |
Before Width: | Height: | Size: 504 KiB After Width: | Height: | Size: 504 KiB |
Before Width: | Height: | Size: 530 KiB After Width: | Height: | Size: 530 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 319 KiB |
Before Width: | Height: | Size: 797 KiB After Width: | Height: | Size: 797 KiB |
Before Width: | Height: | Size: 515 KiB After Width: | Height: | Size: 515 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 592 KiB After Width: | Height: | Size: 592 KiB |
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 291 KiB |
Before Width: | Height: | Size: 534 KiB After Width: | Height: | Size: 534 KiB |
Before Width: | Height: | Size: 415 KiB After Width: | Height: | Size: 415 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 425 KiB After Width: | Height: | Size: 425 KiB |
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 414 KiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 726 KiB After Width: | Height: | Size: 726 KiB |
Before Width: | Height: | Size: 714 KiB After Width: | Height: | Size: 714 KiB |
Before Width: | Height: | Size: 445 KiB After Width: | Height: | Size: 445 KiB |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 229 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 796 KiB After Width: | Height: | Size: 796 KiB |
Before Width: | Height: | Size: 305 KiB After Width: | Height: | Size: 305 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 532 KiB |
Before Width: | Height: | Size: 530 KiB After Width: | Height: | Size: 530 KiB |
Before Width: | Height: | Size: 455 KiB After Width: | Height: | Size: 455 KiB |
Before Width: | Height: | Size: 446 KiB After Width: | Height: | Size: 446 KiB |
Before Width: | Height: | Size: 620 KiB After Width: | Height: | Size: 620 KiB |
Before Width: | Height: | Size: 538 KiB After Width: | Height: | Size: 538 KiB |
Before Width: | Height: | Size: 381 KiB After Width: | Height: | Size: 381 KiB |
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 KiB |
Before Width: | Height: | Size: 882 KiB After Width: | Height: | Size: 882 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 629 KiB After Width: | Height: | Size: 629 KiB |
Before Width: | Height: | Size: 622 KiB After Width: | Height: | Size: 622 KiB |
Before Width: | Height: | Size: 751 KiB After Width: | Height: | Size: 751 KiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 742 KiB After Width: | Height: | Size: 742 KiB |
Before Width: | Height: | Size: 428 KiB After Width: | Height: | Size: 428 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@ -1,3 +0,0 @@
|
||||
{
|
||||
"presets": ["@babel/env"]
|
||||
}
|
90
scripts/favicon.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { favicons, FaviconImage } from 'favicons'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
const imagesDirectory = path.resolve(path.join(process.cwd(), 'src', 'images'))
|
||||
const source = `${imagesDirectory}/favicon.png`
|
||||
const output = path.resolve(path.join(process.cwd(), 'public', 'favicon'))
|
||||
|
||||
const configuration = {
|
||||
path: '/favicon', // Path for overriding default icons path. `string`
|
||||
appName: 'matthias kretschmann',
|
||||
appShortName: 'mk',
|
||||
appDescription: null,
|
||||
developerName: null,
|
||||
developerURL: null,
|
||||
dir: 'auto',
|
||||
lang: 'en-US',
|
||||
appleStatusBarStyle: 'black-translucent', // Style for Apple status bar: "black-translucent", "default", "black". `string`
|
||||
display: 'minimal-ui', // Preferred display mode: "fullscreen", "standalone", "minimal-ui" or "browser". `string`
|
||||
orientation: 'any', // Default orientation: "any", "natural", "portrait" or "landscape". `string`
|
||||
scope: '/', // set of URLs that the browser considers within your app
|
||||
start_url: '/?homescreen=1',
|
||||
background_color: '#e7eef4',
|
||||
theme_color: '#e7eef4',
|
||||
preferRelatedApplications: false,
|
||||
relatedApplications: undefined,
|
||||
version: '1.0',
|
||||
pixel_art: false, // Keeps pixels "sharp" when scaling up, for pixel art. Only supported in offline mode.
|
||||
loadManifestWithCredentials: false,
|
||||
manifestMaskable: `${imagesDirectory}/logo.svg`, // Maskable source image(s) for manifest.json. "true" to use default source. More information at https://web.dev/maskable-icon/. `boolean`, `string`, `buffer` or array of `string`
|
||||
icons: {
|
||||
android: true,
|
||||
appleIcon: true,
|
||||
appleStartup: false,
|
||||
favicons: true,
|
||||
windows: true,
|
||||
yandex: false
|
||||
}
|
||||
}
|
||||
|
||||
async function buildFavicons() {
|
||||
try {
|
||||
const response = await favicons(source, configuration)
|
||||
const allFilesToWrite = response.images.concat(response.files as any)
|
||||
|
||||
fs.rmSync(output, { recursive: true, force: true })
|
||||
|
||||
allFilesToWrite.forEach((file) => {
|
||||
const { name, contents } = file
|
||||
const destination = `${output}/${name}`
|
||||
|
||||
try {
|
||||
fs.readFileSync(destination, 'utf8')
|
||||
} catch (error) {
|
||||
// if there is no file, get data and write a fresh file
|
||||
if (error.code === 'ENOENT') {
|
||||
try {
|
||||
fs.mkdirSync(output, { recursive: true })
|
||||
fs.writeFileSync(destination, contents)
|
||||
} catch (error) {
|
||||
throw new Error("Couldn't write favicon file")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const destinationHtml = `${output}/_meta.html`
|
||||
|
||||
try {
|
||||
fs.readFileSync(destinationHtml, 'utf8')
|
||||
} catch (error) {
|
||||
// if there is no file, get data and write a fresh file
|
||||
if (error.code === 'ENOENT') {
|
||||
try {
|
||||
fs.mkdirSync(output, { recursive: true })
|
||||
fs.writeFileSync(
|
||||
destinationHtml,
|
||||
response.html.reverse().toString().replaceAll(',', '')
|
||||
)
|
||||
} catch (error) {
|
||||
throw new Error("Couldn't write favicon file")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
buildFavicons()
|
@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
const url =
|
||||
'https://raw.githubusercontent.com/matomo-org/matomo/4.x-dev/matomo.js'
|
||||
|
||||
axios(url).then((response) => {
|
||||
process.stdout.write(`${response.data}`)
|
||||
})
|
@ -1,42 +0,0 @@
|
||||
const axios = require('axios')
|
||||
|
||||
//
|
||||
// Get GitHub repos
|
||||
//
|
||||
const gitHubConfig = {
|
||||
headers: {
|
||||
'User-Agent': 'kremalicious/portfolio',
|
||||
Authorization: `token ${process.env.GATSBY_GITHUB_TOKEN}`
|
||||
}
|
||||
}
|
||||
|
||||
async function getGithubRepos(data) {
|
||||
let repos = []
|
||||
let holder = {}
|
||||
|
||||
for (let item of data) {
|
||||
const user = item.split('/')[0]
|
||||
const repoName = item.split('/')[1]
|
||||
const repo = await axios.get(
|
||||
`https://api.github.com/repos/${user}/${repoName}`,
|
||||
gitHubConfig
|
||||
)
|
||||
|
||||
holder.name = repo.data.name
|
||||
holder.full_name = repo.data.full_name
|
||||
holder.description = repo.data.description
|
||||
holder.html_url = repo.data.html_url
|
||||
holder.homepage = repo.data.homepage
|
||||
holder.stargazers_count = repo.data.stargazers_count
|
||||
holder.pushed_at = repo.data.pushed_at
|
||||
repos.push(holder)
|
||||
holder = {}
|
||||
}
|
||||
|
||||
// sort by pushed to, newest first
|
||||
repos = repos.sort((a, b) => b.pushed_at.localeCompare(a.pushed_at))
|
||||
|
||||
return repos
|
||||
}
|
||||
|
||||
module.exports = { getGithubRepos }
|
@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env ts-node
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import prepend from 'prepend'
|
||||
import slugify from 'slugify'
|
||||
import ora from 'ora'
|
||||
|
||||
const templatePath = path.join(__dirname, 'new.yml')
|
||||
const templatePath = path.join(process.cwd(), 'scripts', 'new.yml')
|
||||
const template = fs.readFileSync(templatePath).toString()
|
||||
|
||||
const spinner = ora('Adding new project').start()
|
||||
@ -17,14 +19,14 @@ const title = process.argv[2]
|
||||
spinner.text = `Adding '${title}'.`
|
||||
|
||||
const titleSlug = slugify(title, { lower: true })
|
||||
const projects = path.join(__dirname, '..', 'content', 'projects.yml')
|
||||
const projects = path.join(process.cwd(), '_content', 'projects.yml')
|
||||
const newContents = template
|
||||
.split('TITLE')
|
||||
.join(title)
|
||||
.split('SLUG')
|
||||
.join(titleSlug)
|
||||
|
||||
prepend(projects, newContents, error => {
|
||||
prepend(projects, newContents, (error) => {
|
||||
if (error) spinner.fail(error)
|
||||
spinner.succeed(`Added '${title}' to top of projects.yml file.`)
|
||||
})
|
@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
SRC='./src/images'
|
||||
OUT='./src/components/svg'
|
||||
|
||||
printf "Creating SVG components...\\n\\n"
|
||||
|
||||
# Usage: svgr [-d out-dir] [src-dir]
|
||||
./node_modules/@svgr/cli/bin/svgr --icon -d $OUT $SRC
|
||||
|
||||
printf "\\n🎉 Successfully created SVG components\\n\\n"
|