Migrate to simple Phoenix application.

main
Joshua Potter 2024-05-18 11:53:56 -06:00
parent 88457c1f03
commit 0411360113
1009 changed files with 10602 additions and 1674 deletions

View File

@ -1,2 +0,0 @@
---
BUNDLE_FORCE_RUBY_PLATFORM: "true"

5
.formatter.exs Normal file
View File

@ -0,0 +1,5 @@
[
import_deps: [:phoenix],
plugins: [Phoenix.LiveView.HTMLFormatter],
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"]
]

29
.githooks/pre-commit Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -e
STAGED=$(
git --no-pager diff --name-only --no-color --cached --diff-filter=d |
# Remove quotations used to surrounding filenames with special characters.
sed -e "s/^\"//" -e "s/\"$//g"
)
MIX_TARGETS=()
WEB_TARGETS=()
while IFS= read -r FILENAME
do
if [[ "$FILENAME" =~ .*\.exs?$ ]]; then
MIX_TARGETS+=("${FILENAME}")
elif [[ "$FILENAME" =~ .*\.jsx?$ ]] || [[ "$FILENAME" =~ .*\.tsx?$ ]]; then
WEB_TARGETS+=("${FILENAME}")
fi
done <<< "$STAGED"
if (( ${#MIX_TARGETS[@]} )); then
mix format "${MIX_TARGETS[@]}"
git add "${MIX_TARGETS[@]}"
fi
if (( ${#WEB_TARGETS[@]} )); then
prettier --write "${WEB_TARGETS[@]}"
git add "${WEB_TARGETS[@]}"
fi

40
.gitignore vendored
View File

@ -1,8 +1,42 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Temporary files, for example, from tests.
/tmp/
# Ignore package tarball (built via "mix hex.build").
portfolio-*.tar
# Ignore assets that are produced by build tools.
/priv/static/assets/
# Ignore digested assets cache.
/priv/static/cache_manifest.json
# In case you use Node.js/npm, you want to ignore these.
npm-debug.log
/assets/node_modules/
# Directory used by `direnv` to hold `use flake`-generated profiles.
/.direnv/
# A symlink produced by default when running `nix build`.
/result
# The jekyll-produced static bundle.
_site/

View File

@ -1,25 +0,0 @@
---
permalink: /404.html
layout: default
---
<style type="text/css" media="screen">
.container {
margin: 10px auto;
max-width: 600px;
text-align: center;
}
h1 {
margin: 30px 0;
font-size: 4em;
line-height: 1;
letter-spacing: -1px;
}
</style>
<div class="container">
<h1>404</h1>
<p><strong>Page not found :(</strong></p>
<p>The requested page could not be found.</p>
</div>

14
Gemfile
View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
source "https://rubygems.org"
gem "jekyll", "~> 4.3.2"
gem "lagrange", "~> 4.0"
# If you have any plugins, put them here!
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.12"
gem "jekyll-seo-tag", "~> 2.8"
gem "jekyll-sitemap", "~> 1.4"
gem "jekyll-paginate-v2", "~> 3.0"
end

View File

@ -1,92 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0)
concurrent-ruby (1.2.2)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.16.3)
forwardable-extended (2.6.0)
google-protobuf (3.25.1)
http_parser.rb (0.8.0)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
jekyll (4.3.2)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (>= 2.0, < 4.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.3, >= 2.3.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (>= 0.3.6, < 0.5)
pathutil (~> 0.9)
rouge (>= 3.0, < 5.0)
safe_yaml (~> 1.0)
terminal-table (>= 1.8, < 4.0)
webrick (~> 1.7)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-paginate (1.1.0)
jekyll-paginate-v2 (3.0.0)
jekyll (>= 3.0, < 5.0)
jekyll-sass-converter (3.0.0)
sass-embedded (~> 1.54)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
lagrange (4.0.0)
jekyll (~> 4.2)
jekyll-feed (~> 0.6)
jekyll-paginate (~> 1.1)
jekyll-seo-tag (~> 2.6)
jekyll-sitemap (~> 1.3)
liquid (4.0.4)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.4)
rake (13.1.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.6)
rouge (4.2.0)
safe_yaml (1.0.5)
sass-embedded (1.69.5)
google-protobuf (~> 3.23)
rake (>= 13.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.5.0)
webrick (1.8.1)
PLATFORMS
ruby
DEPENDENCIES
jekyll (~> 4.3.2)
jekyll-feed (~> 0.12)
jekyll-paginate-v2 (~> 3.0)
jekyll-seo-tag (~> 2.8)
jekyll-sitemap (~> 1.4)
lagrange (~> 4.0)
BUNDLED WITH
2.4.22

102
README.md
View File

@ -1,29 +1,95 @@
# Jekyll Flake Template
# Portfolio
This is a template for bootstrapping a [Jekyll](https://jekyllrb.com/)-based
project (version 4.3.2) with the [lagrange](https://github.com/LeNPaul/Lagrange)
theme (version 4.0). [direnv](https://direnv.net/) can be used to launch a dev
shell upon entering this directory (refer to `.envrc`). Otherwise run via:
This is my main personal website. It uses Elixir (version 1.15.7, Erlang/OTP
25) and the [Phoenix](https://www.phoenixframework.org/) (version 1.7.10)
framework. [direnv](https://direnv.net/) can be used to launch a dev shell upon
entering this directory (refer to `.envrc`). Otherwise run via:
```bash
$ nix develop
```
Start the server by running:
```
$ jekyll serve [--watch]
## Quickstart
Run the Phoenix setup command and then start the local server:
```bash
$ mix deps.get
$ mix assets.setup
$ mix phx.server
```
## Building
## Blog
Dependencies are managed using [bundix](https://github.com/nix-community/bundix).
If you make any changes to the `Gemfile`, run the following:
Blog posts exist under the nested `blog_html/` directory. Markdown files are
converted to HTML using pandoc (version 3.1.9). For example,
```bash
$ bundix -l
$ cd lib/portfolio_web/controllers/blog_html
$ pandoc \
> --to=html \
> --toc \
> --template=template.html \
> --output tagless_final_parsing.html.heex \
> tagless_final_parsing.md
```
This will update the `Gemfile.lock` and `gemset.nix` files. Afterward you can
run:
Styling is maintained within the `blog.css` file. This file is generated by
copying the CSS made within a standalone invocation of `pandoc`. For example,
```bash
$ nix build
$ pandoc --standalone --output output.html --highlight-style=zenburn
```
Note that we need the `.bundle/config` file to workaround issues bundix has with
pre-built, platform-specific gems. Refer to
[PR #68](https://github.com/nix-community/bundix/pull/68) for more details.
## Dependencies
### Backend
Mix dependencies are packaged using [mix2nix](https://github.com/ydlr/mix2nix).
After updating your `mix.lock` file, make sure to re-run the following:
```bash
$ mix2nix > deps.nix
```
As of now, `mix2nix` cannot handle git dependencies found inside the `mix.lock`
file. If you have git dependencies, add them manually or use
[FODs](https://nixos.org/manual/nixpkgs/stable/#packaging-beam-applications).
### Frontend
Frontend dependencies (i.e. assets found in the `/assets` folder) are packaged
using [node2nix](https://github.com/svanderburg/node2nix). You can generate the
relevant nix files for import using the following sequence of commands:
```bash
$ cd assets
$ rm -r node_modules # If this directory exists.
$ node2nix -l
```
In the above, we must remove `node_modules` (if it exists). Otherwise the
node packages will be included in the Nix build, influencing the outcome of
`node2nix`. The above generates three files:
* `node-packages.nix`
* Captures the packages that can be deployed (including all its required
dependencies)
* `node-env.nix`
* Contains build logic
* `default.nix`
* A composition expression allowing users to deploy the package. For an
example of this deployment, refer to `flake.nix`
NOTE: Do not update the lock version used in `assets`. `node2nix` currently only
supports lock versions 1 and 2.
## Language Server
The [elixir-ls](https://github.com/elixir-lsp/elixir-ls) LSP (version 0.17.10)
and [typescript-language-server](https://github.com/typescript-language-server/typescript-language-server)
(version 4.1.2) is included in this flake.
## Formatting
Formatting depends on [prettier](https://prettier.io/) (version 3.1.0) and the
`mix format` task. A `pre-commit` hook is included in `.githooks` that can be
used to format all `*.exs?`, `*.jsx?`, and `*.tsx?` files prior to commit.
Install via:
```bash
$ git config --local core.hooksPath .githooks/
```
If running [direnv](https://direnv.net/), this hook is installed automatically
when entering the directory.

View File

@ -1,66 +0,0 @@
# Welcome to Jekyll!
#
# This config file is meant for settings that affect your whole blog, values
# which you are expected to set up once and rarely edit after that. If you find
# yourself editing this file very often, consider using Jekyll's data files
# feature for the data you need to update frequently.
#
# For technical reasons, this file is *NOT* reloaded automatically when you use
# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
#
# If you need help with YAML syntax, here are some quick references for you:
# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml
# https://learnxinyminutes.com/docs/yaml/
#
# Site settings
# These are used to personalize your new site. If you look in the HTML files,
# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
# You can create any custom variable you would like, and they will be accessible
# in the templates via {{ site.myvariable }}.
title: Joshua Potter
description: My portfolio
author: Joshua Potter
github_username: jrpotter
theme: lagrange
plugins:
- jekyll-feed
- jekyll-paginate-v2
- jekyll-seo-tag
- jekyll-sitemap
pagination:
enabled: true
per_page: 8
permalink: '/page/:num/'
limit: 0
sort_field: 'date'
sort_reverse: true
markdown: kramdown
highlighter: route
permalink: /:title
hide_post_share: true
hide_related_posts: true
# Exclude from processing.
# The following items will not be processed, by default.
# Any item listed under the `exclude:` key here will be automatically added to
# the internal "default list".
#
# Excluded items can be processed by explicitly listing the directories or
# their entries' file path in the `include:` list.
#
# exclude:
# - .sass-cache/
# - .jekyll-cache/
# - gemfiles/
# - Gemfile
# - Gemfile.lock
# - node_modules/
# - vendor/bundle/
# - vendor/cache/
# - vendor/gems/
# - vendor/ruby/

View File

@ -1,18 +0,0 @@
menu:
- name: "Home"
- name: "Projects"
url: "projects.html"
- name: "Archive"
url: "archive.html"
social:
- icon: "github"
link: "https://github.com/jrpotter"
- icon: "rss-square"
link: "https://blog.jrpotter.com"
- icon: "book"
link: "https://bookshelf.jrpotter.com"
- icon: "linkedin"
link: "https://www.linkedin.com/in/jrpotter2112/"
- icon: "hdd-o"
link: "https://www.zotero.org/jrpotter2112/library"

View File

@ -1,4 +0,0 @@
<footer class="footer">
{% include social-icons.html %}
<div class="footer-description"><a href="{{ site.github.url }}/">© 2023 {{ site.title }}</a></div>
</footer>

View File

@ -1,29 +0,0 @@
<head>
<title>Portfolio • Joshua Potter</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<link rel="stylesheet" href="{{ site.github.url }}/assets/css/main.css">
<link rel="stylesheet" href="{{ site.github.url }}/assets/css/syntax.css">
<!-- Use Atom -->
{% feed_meta %}
<!-- Use RSS-2.0 -->
<!--<link href="{{ site.github.url }}/rss-feed.xml" type="application/rss+xml" rel="alternate" title="{{ site.title }} | {{ site.description }}"/>
//-->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700%7CPT+Sans:400">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Quattrocento+Sans">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [['$', '$'], ['\\(', '\\)']]
}
});
</script>
{% if jekyll.environment == "production" %}
<!-- Analytics -->
{% endif %}
<!-- Use Jekyll SEO plugin -->
{% seo %}
</head>

View File

@ -1,32 +0,0 @@
{% if paginator.total_pages > 1 %}
<nav aria-label="Page Navigation">
<ul class="pagination">
<!-- left arrow -->
{% if paginator.previous_page %}
{% assign prev_url = paginator.previous_page_path | prepend: site.baseurl %}
{% else %}
{% assign prev_url = '' %}
{% endif %}
<li class="page-item {% unless paginator.previous_page %}disabled{% endunless %}">
<a class="page-link" href="{{ prev_url }}" aria-label="previous-page">
<i class="fa fa-angle-left"></i>
</a>
</li>
<!-- right arrow -->
{% if paginator.next_page_path %}
{% assign next_url = paginator.next_page_path | prepend: site.baseurl %}
{% else %}
{% assign next_url = '#' %}
{% endif %}
<li class="page-item {% unless paginator.next_page_path %}disabled{% endunless %}">
<a class="page-link" href="{{ next_url }}" aria-label="next-page">
<i class="fa fa-angle-right"></i>
</a>
</li>
</ul>
</nav>
<!-- .pagination -->
{% endif %}

View File

@ -1,28 +0,0 @@
---
layout: default
---
<ul class="posts-container">
{% for post in site.posts %}
{% unless post.next %}
<h3>{{ post.date | date: '%Y' }}</h3>
{% else %}
{% capture year %}{{ post.date | date: '%Y' }}{% endcapture %}
{% capture nyear %}{{ post.next.date | date: '%Y' }}{% endcapture %}
{% if year != nyear %}
<hr />
<h3>{{ post.date | date: '%Y' }}</h3>
{% endif %}
{% endunless %}
<li itemscope>
<div class="post-tags">
<a href="{{ site.github.url }}{{ post.url }}">{{ post.title }}</a>
{% for tag in post.tags %}
<div class="{{ tag }}">{{ tag }}</div>
{% endfor %}
</div>
<p>{{ post.content | strip_html }}</p>
<p class="post-date"><span><i class="fa fa-calendar" aria-hidden="true"></i> {{ post.date | date: "%B %-d" }}</span></p>
</li>
{% endfor %}
</ul>

View File

@ -1,45 +0,0 @@
---
layout: default
---
{% if paginator.page_path contains "index" %}
<p class="home-description">
Most applications listed below are served from <a href="https://nixos.org/">NixOS</a>
machines hosted on <a href="https://www.digitalocean.com/">Digital Ocean</a>.
Configuration files for each of my machines can be found <a href="https://git.jrpotter.com/r/nixos-configuration">here</a>.
If interested in starting a similar hosting solution, consider getting a $200
credit using my <a href="https://m.do.co/c/c65b89434c1b">referral link</a>.
</p>
{% endif %}
{% for post in paginator.posts %}
<div class="posts-container">
<h1>
<a href="{{ post.href }}">{{ post.title }}</a>
</h1>
{% if post.image %}
<div class="thumbnail-container">
<a href="{{ post.href }}">
<img src="{{ site.github.url }}/assets/img/{{ post.image }}">
</a>
</div>
{% endif %}
<p>
<div class="post-tags">
{% for tag in post.tags %}
<div class="{{ tag }}">{{ tag }}</div>
{% endfor %}
</div>
<p>{{ post.content }}</p>
<span class="post-date">
{% if post.href %}
<a href="{{ post.href }}">{{ post.href }}</a> -
{% endif %}
<i class="fa fa-calendar" aria-hidden="true"></i>
{{ post.date | date_to_string }}
</span>
</p>
</div>
{% endfor %}
{% include post-paginator.html %}

View File

@ -1,33 +0,0 @@
---
layout: default
---
<h1>
{% if page.href %}
<a href="{{ page.href }}">{{ page.title }}</a>
{% else %}
{{ page.title }}
{% endif %}
</h1>
{% if page.image %}
<div class="page-image-container">
<a href="{{ page.href }}">
<img src="{{ site.github.url }}/assets/img/{{ page.image }}">
</a>
</div>
{% endif %}
<article>
{{ content }}
</article>
{% if site.hide_post_date != true %}
{% include post-date.html %}
{% endif %}
{% if site.hide_post_share != true %}
{% include social-sharing.html %}
{% endif %}
{% if site.hide_related_posts != true %}
{% include related-posts.html %}
{% endif %}
{% if site.data.settings.disqus.comments %}
{% include disqus.html %}
{% endif %}

View File

@ -1,13 +0,0 @@
---
layout: post
title: Mini Java
categories: project
tags: [compiler, java]
href: "https://git.jrpotter.com/r/mini-java"
image: java.png
---
A Java implemention of a subset of Java. Generates code that targets mJAM, an
abstract machine included in the source that supports running `miniJava`. In
particular, this implementation supports various primitives, array types, and,
to a certain degree, classes.

View File

@ -1,13 +0,0 @@
---
layout: post
title: Fifth
categories: project
tags: [cellular-automata, python]
href: "https://git.jrpotter.com/r/fifth"
image: conway-gol.jpg
---
A library for parsing various rulesets for cellular automata machines (CAMs).
The parsed CAM is displayed using [matplotlib](https://matplotlib.org). For
instance, this library parses ruleset `B3/S23` and then produces a running
visualization of Conway's Game of Life.

View File

@ -1,14 +0,0 @@
---
layout: post
title: Pong
categories: project
tags: [fpga, mips-assembly, verilog]
href: "https://git.jrpotter.com/r/pong"
image: pong.jpg
---
An implementation of the classic pong video game, written from scratch on an
Artix FPGA using System Verilog. This works on a custom ALU intended to process
an arbitrary MIPS program with modified memory configuration: `.text 0x0000`
and `.data 0x2000`. A memory mapped IO scheme is used to draw to the monitor and
interact with the keyboard.

View File

@ -1,13 +0,0 @@
---
layout: post
title: Highlight Plugin
categories: project
tags: [vim]
href: "https://git.jrpotter.com/r/vim-highlight"
image: vim.png
---
A small Vim plugin that maintains a custom registry for manipulating highlights.
This registry allows highlighting different keywords without overriding previous
searches. Includes a small snippet for including the active highlight from
within the statusline.

View File

@ -1,12 +0,0 @@
---
layout: post
title: Join Plugin
categories: project
tags: [vim]
href: "https://git.jrpotter.com/r/vim-join"
image: vim.png
---
A small Vim plugin that joins a number of lines together and then breaks them
again with respect to the `textwidth` parameter. This enables re-flowing a set
of lines similar in manner to `fold` or `fmt`.

View File

@ -1,11 +0,0 @@
---
layout: post
title: Bill Gates and Honey
categories: other
tags: [podcast]
href: "https://open.spotify.com/episode/2CaNBdpgZofhXDGCs2QsPW"
image: would-you-blabber.png
---
In our first episode of Would You Blabber, we discuss honey, truck drivers, and
hurting those more fortunate than us.

View File

@ -1,10 +0,0 @@
---
layout: post
title: Bubbles and Funny Bones
categories: other
tags: [podcast]
href: "https://open.spotify.com/episode/0MeLBj9jw3lPXBxRKevODo"
image: would-you-blabber.png
---
In this episode of Would You Blabber, we discuss bubble boys and precognition.

View File

@ -1,10 +0,0 @@
---
layout: post
title: Bananas, Buildings, and Batman
categories: other
tags: [podcast]
href: "https://open.spotify.com/episode/3XrpanmTQiZhXjYqLP3dBd"
image: would-you-blabber.png
---
Bananas, buildings, and Batman!

View File

@ -1,11 +0,0 @@
---
layout: post
title: Huh, Huh, and Huh ft. Kenny Yi
categories: other
tags: [podcast]
href: "https://open.spotify.com/episode/44oNp0ctwyk590298xWHwA"
image: would-you-blabber.png
---
In this episode of Would You Blabber, we explore the world of glogging, huh-ing,
and old soft-serve ice cream.

View File

@ -1,13 +0,0 @@
---
layout: post
title: Postlude
categories: project
tags: [haskell]
href: "https://git.jrpotter.com/r/postlude"
image: haskell.png
---
An example of a custom-rolled [Prelude](https://hackage.haskell.org/package/base-4.19.0.0/docs/Prelude.html).
Serves as a fairly comprehensive list of imports I found relevant across the
various Haskell projects I worked on as well as a demonstration on how
forwarding imports with Haskell works.

View File

@ -1,14 +0,0 @@
---
layout: post
title: Looped
categories: home
tags: [python, swift, kotlin, vue]
image: looped.png
---
VP of engineering at Looped, the "Ultimate Virtual Venue". Featured on
[Forbes](https://www.forbes.com/sites/ericfuller/2021/01/06/loopedthe-app-helping-fans-mingle-and-meet-artists-personally-during-live-streamed-events)
and [TechCrunch](https://techcrunch.com/2021/03/02/looped-raises-7-7m-to-expand-its-interactive-live-event-platform).
Led development on the Kotlin-based [Android app](https://play.google.com/store/apps/details?id=com.vipvr.android)
(50K+ downloads, 4.5 star review), the Swift-based iOS app (100K+ downloads,
4.8 star review), the Vue-based web app, and the Django-based backend.

View File

@ -1,13 +0,0 @@
---
layout: post
title: Blog
categories: home
tags: [jekyll, nix, ruby]
href: "https://blog.jrpotter.com"
image: jekyll.png
---
I occasionally write about (usually) technical concepts in my blog. Originally
powered by Github Pages, I've since moved the [Jekyll](https://jekyllrb.com/)-based
project to a self-hosting solution. Theming is provided by
[Chirpy](https://github.com/cotes2020/jekyll-theme-chirpy).

View File

@ -1,14 +0,0 @@
---
layout: post
title: Homesync
categories: project
tags: [git, nix, rust]
href: "https://git.jrpotter.com/r/homesync"
image: git-branches.png
---
An experimental Rust-based project for automatically syncing files across your
desktop to a git repository. Allows upstream and downstream syncing with a
single command, without any need to copy files manually to and from a git
repository. Separately, a daemon can be spawned that watches files for changes
and pushes/pulls them as they happen.

View File

@ -1,11 +0,0 @@
---
layout: post
title: Anki Synonyms
categories: project
tags: [anki, python]
href: "https://git.jrpotter.com/r/anki-synonyms"
image: anki.png
---
An [Anki](https://apps.ankiweb.net/) plugin for specifying synonyms within
flashcard question and answer prompts.

View File

@ -1,13 +0,0 @@
---
layout: post
title: Bookshelf
categories: home
tags: [latex, lean, nix]
href: "https://bookshelf.jrpotter.com"
image: lean.svg
---
A collection of books I am actively studying. Usually mathematics or
computer-science based, I aim to prove concepts as I encounter them using the
[Lean](https://lean-lang.org/) interactive theorem prover. All proofs are also
available in [LaTeX](https://www.latex-project.org/).

View File

@ -1,13 +0,0 @@
---
layout: post
title: We're the Crew
categories: other
tags: [music]
href: "https://gusvieweg.bandcamp.com/track/were-the-crew"
image: smash-rap.jpg
---
A Smash Bros. Ultimate rap made in collaboration with my friend
[Gus](https://www.gusvieweg.com/) and fiancée Brittany.

View File

@ -1,14 +0,0 @@
---
layout: post
title: Bootstrap
categories: project
tags: [c, nix]
href: "https://git.jrpotter.com/r/bootstrap"
image: shoelaces.jpg
---
A C-based CLI for initializing projects in a flexible but deterministic way.
Originally motivated to serve as a better alternative to [Nix flake templates](https://github.com/NixOS/templates),
`bootstrap` allows you to provide different parameters to custom initialization
scripts akin to `npm init`, `django-admin startproject`, etc.

View File

@ -1,14 +0,0 @@
---
layout: post
title: BoardWise
categories: home
tags: [elixir, react, nix]
href: "https://boardwise.gg"
image: boardwise.svg
---
A [Phoenix](https://www.phoenixframework.org/)- and
[React](https://react.dev/)-based project that provides an interface for finding
chess coaches. This serves as an alternative to those found on
[Lichess](https://lichess.org/coach) and [Chess.com](https://www.chess.com/coaches).
Based on the [Tailwind Studio](https://tailwindui.com/templates/studio) theme.

View File

@ -1,12 +0,0 @@
---
layout: post
title: NixOS Configuration
categories: project
tags: [lua, nix]
href: "https://git.jrpotter.com/r/nixos-configuration"
image: nix.png
---
The [nix](https://nixos.org) configuration files used to declaratively describe
my local and remote machines. The site you are on now is declared within this
project!

View File

@ -1,13 +0,0 @@
---
layout: post
title: Bookshelf Doc Generator
categories: project
tags: [latex, lean]
href: "https://git.jrpotter.com/r/bookshelf-doc"
image: lean.svg
---
A fork of [doc-gen4](https://github.com/leanprover/doc-gen4) tightly coupled to
my [bookshelf](https://git.jrpotter.com/r/bookshelf) project. This augments the
`:docs` facet to convert LaTeX files into PDFs and then list them in the
generated navbar.

View File

@ -1,12 +0,0 @@
---
layout: post
title: Forgejo
categories: home
tags: [git, nix]
href: "https://git.jrpotter.com"
image: forgejo.svg
---
A self-hosted [forgejo](https://forgejo.org/) instance. For the most part, my
[GitHub](https://github.com) repositories are a mirror of those found here.
There do exist a few repos though that live exclusively on either site.

View File

@ -1,12 +0,0 @@
---
layout: post
title: Sketchbook Shuffle
categories: other
tags: [art]
href: "https://www.twobeeindustries.com/playing-card-instructions"
image: joker-card.png
---
My contribution to a collective art project in which different members of the
community choose a playing card to design. I designed the red joker. Once all
cards are submitted, a deck will be printed for purchase.

View File

@ -1,13 +0,0 @@
---
layout: post
title: Notebook
categories: home
tags: [nix, quartz]
href: "https://notebook.jrpotter.com"
image: quartz.png
---
A static site generated with [Quartz](https://quartz.jzhao.xyz/). Contains a
collection of my transcribed notes, primarily Markdown managed using
[Obsidian](https://obsidian.md/). Hidden are a collection of
[Anki](https://apps.ankiweb.net/) flashcards.

View File

@ -1,15 +0,0 @@
---
layout: post
title: Hide and Seek
categories: home
tags: [elixir, phoenix, react]
href: "https://hideandseek.live"
image: hide-and-seek.png
---
Realtime hide-and-seek application for the town of Fort Collins, built using
[Phoenix](https://www.phoenixframework.org/) and [React](https://react.dev/).
Group up with friends, designate a hider, and allow the rest of the group to
find the hider in a city-wide search. Use various clues to narrow down the
search space at the cost of potentially giving the hider means of thwarting the
search.

View File

@ -1,6 +0,0 @@
@import "base",
"code",
"default",
"home",
"post",
"social-icons"

View File

@ -1,33 +0,0 @@
.home-description, .posts-container {
padding-bottom: 25px;
}
.thumbnail-container {
text-align: center;
img {
max-width: 100%;
max-height: 220px;
}
}
ul.pagination {
list-style: none;
display: flex;
gap: 2rem;
padding: 0;
margin-bottom: 0;
justify-content: center;
li.disabled {
opacity: 0.5;
.page-link i {
cursor: not-allowed;
}
}
.page-link i {
font-size: 1.75rem;
}
}

View File

@ -1,48 +0,0 @@
.page-image-container {
text-align: center;
img {
width: auto;
height: 200px;
}
}
.post-tags {
display: flex;
gap: 8px;
div {
background: #e0e0e0;
border-radius: 999px;
padding: 2px 8px;
font-size: 16px;
}
}
.post-date {
display: block;
margin-top: 3px;
margin-bottom: 1rem;
color: $light-gray-color;
font-family: $sans-serif-font-family;
font-size: 0.8rem;
}
@media (max-width: $elements-responsive-width) {
.posts h1 {
font-size: 1.5rem;
}
}
.related {
padding-bottom: 2rem;
}
.related-posts {
padding-left: 0px;
list-style: none;
}
.related-posts a {
text-decoration: none;
}

View File

@ -1,70 +0,0 @@
/*
Social media icons
*/
.social-icons a, .sharing-icons a {
padding-right: 10px;
}
@mixin social-media-icon($color, $transition){
-webkit-transition: $transition;
-o-transition: $transition;
-ms-transition: $transition;
-moz-transition: $transition;
transition: $transition;
&:hover{
color: $color;
}
}
.sharing-icons {
.fa-envelope {
padding: 5px;
@include social-media-icon($envelope-color, $icon-transition-time);
}
.fa-twitter {
padding: 5px;
@include social-media-icon($twitter-color, $icon-transition-time);
}
.fa-instagram {
padding: 5px;
@include social-media-icon($instagram-color, $icon-transition-time);
}
.fa-github {
padding: 5px;
@include social-media-icon($github-color, $icon-transition-time);
}
.fa-linkedin {
padding: 5px;
@include social-media-icon($linkedin-color, $icon-transition-time);
}
.fa-facebook {
padding: 5px;
@include social-media-icon($facebook-color, $icon-transition-time);
}
.fa-pinterest {
padding: 5px;
@include social-media-icon($pinterest-color, $icon-transition-time);
}
.fa-medium {
padding: 5px;
@include social-media-icon($medium-color, $icon-transition-time);
}
.fa-codepen {
padding: 5px;
@include social-media-icon($codepen-color, $icon-transition-time);
}
.fa-rss-square {
padding: 5px;
@include social-media-icon($rss-color, $icon-transition-time);
}
}

131
assets/css/app.css Normal file
View File

@ -0,0 +1,131 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
/* Links */
a {
display: inline-block;
text-decoration: underline;
width: fit-content;
}
/* Table of contents */
#TOC li {
padding-top: 0.5rem;
}
#TOC .h3 {
margin-left: 1rem;
}
#TOC .h4 {
margin-left: 2rem;
}
/* Headers */
h1, h2, h3, h4 {
padding: 0.5rem 0;
width: fit-content;
}
h1 {
border-bottom: 1px dashed white;
font-size: 1.25rem;
line-height: 1.75rem;
}
h2, h3, h4 {
border-bottom: 1px dotted white;
}
h2 {
font-size: 1.125rem;
line-height: 1.75rem;
}
h4 {
font-size: 0.875rem;
line-height: 1.25rem;
}
/* Figures */
figure {
text-align: center;
}
figure img {
margin: 0 auto;
max-width: 768px;
}
/* Footnote */
#footnotes {
margin-top: 2rem;
}
#footnotes ol {
padding-top: 1rem;
padding-left: 1rem;
list-style-type: decimal;
}
/* Syntax highlighting */
code {
font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
font-size: 85%;
margin: 0;
}
pre {
border-left: 1px solid gray;
padding-left: 1rem;
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
code span.al { color: #ffcfaf; } /* Alert */
code span.an { color: #7f9f7f; font-weight: bold; } /* Annotation */
code span.at { } /* Attribute */
code span.bn { color: #dca3a3; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #f0dfaf; } /* ControlFlow */
code span.ch { color: #dca3a3; } /* Char */
code span.cn { color: #dca3a3; font-weight: bold; } /* Constant */
code span.co { color: #7f9f7f; } /* Comment */
code span.cv { color: #7f9f7f; font-weight: bold; } /* CommentVar */
code span.do { color: #7f9f7f; } /* Documentation */
code span.dt { color: #dfdfbf; } /* DataType */
code span.dv { color: #dcdccc; } /* DecVal */
code span.er { color: #c3bf9f; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #c0bed1; } /* Float */
code span.fu { color: #efef8f; } /* Function */
code span.im { } /* Import */
code span.in { color: #7f9f7f; font-weight: bold; } /* Information */
code span.kw { color: #f0dfaf; } /* Keyword */
code span.op { color: #f0efd0; } /* Operator */
code span.ot { color: #efef8f; } /* Other */
code span.pp { color: #ffcfaf; font-weight: bold; } /* Preprocessor */
code span.sc { color: #dca3a3; } /* SpecialChar */
code span.ss { color: #cc9393; } /* SpecialString */
code span.st { color: #cc9393; } /* String */
code span.va { } /* Variable */
code span.vs { color: #cc9393; } /* VerbatimString */
code span.wa { color: #7f9f7f; font-weight: bold; } /* Warning */

17
assets/default.nix Normal file
View File

@ -0,0 +1,17 @@
# This file has been generated by node2nix 1.11.1. Do not edit!
{pkgs ? import <nixpkgs> {
inherit system;
}, system ? builtins.currentSystem, nodejs ? pkgs."nodejs_14"}:
let
nodeEnv = import ./node-env.nix {
inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript;
inherit pkgs nodejs;
libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
};
in
import ./node-packages.nix {
inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit;
inherit nodeEnv;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
<path d="M 12.5 0 H 20 Q 25 0, 25 5 V 12.5 H 12.5 Z" fill="rgb(10 10 10)"></path>
<path d="M 0 20 Q 0 25, 5 25 H 12.5 V 12.5 H 0 Z" fill="rgb(10 10 10)"></path>
</svg>

Before

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

View File

@ -1,40 +0,0 @@
<svg viewBox="0 0 212 212" xmlns="http://www.w3.org/2000/svg">
<metadata
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
>
<rdf:RDF>
<cc:Work rdf:about="https://codeberg.org/forgejo/governance/src/branch/main/branding#logo">
<dc:title>Forgejo logo</dc:title>
<cc:creator rdf:resource="https://caesarschinas.com/"><cc:attributionName>Caesar Schinas</cc:attributionName></cc:creator>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
</rdf:RDF>
</metadata>
<style type="text/css">
circle {
fill: none;
stroke: #000;
stroke-width: 15;
}
path {
fill: none;
stroke: #000;
stroke-width: 25;
}
.orange {
stroke:#ff6600;
}
.red {
stroke:#d40000;
}
</style>
<g transform="translate(6,6)">
<path d="M58 168 v-98 a50 50 0 0 1 50-50 h20" class="orange" />
<path d="M58 168 v-30 a50 50 0 0 1 50-50 h20" class="red" />
<circle cx="142" cy="20" r="18" class="orange" />
<circle cx="142" cy="88" r="18" class="red" />
<circle cx="58" cy="180" r="18" class="red" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 KiB

View File

@ -1,314 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="300"
height="150"
viewBox="0 0 240 120"
version="1.1"
id="svg2"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
sodipodi:docname="lean_logo.svg">
<metadata
id="metadata113">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1418"
id="namedview111"
showgrid="false"
inkscape:zoom="4"
inkscape:cx="223.20569"
inkscape:cy="58.300704"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="surface1"
inkscape:document-rotation="0"
showguides="true"
inkscape:guide-bbox="true">
<sodipodi:guide
position="70.622896,28.982703"
orientation="0,-1"
id="guide193" />
<sodipodi:guide
position="55.91963,99.309873"
orientation="0,-1"
id="guide205" />
<sodipodi:guide
position="63.7,64.3"
orientation="0,-1"
id="guide207" />
<sodipodi:guide
position="57.515625,28.982703"
orientation="0,-1"
id="guide209" />
<sodipodi:guide
position="57.403125,27.453125"
orientation="0,-1"
id="guide211" />
<sodipodi:guide
position="9.834375,27.071875"
orientation="1,0"
id="guide213" />
<sodipodi:guide
position="11.771875,28.396875"
orientation="1,0"
id="guide215" />
</sodipodi:namedview>
<defs
id="defs4">
<g
id="g6">
<symbol
overflow="visible"
id="glyph0-0"
style="overflow:visible">
<path
style="stroke:none"
d="m -5.140625,0 -180.499995,0 0,-262.4375 180.499995,0 z M -178.125,-250.15625 l 0,237.09375 79.171875,-118.75 z m 5.14063,243.03125 154.781245,0 -77.1875,-117.5625 z m 77.593745,-131.8125 77.1875,-116.375 -154.781245,0 z m 3.5625,7.125 79.15625,118.75 0,-237.09375 z"
id="path9"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph0-1"
style="overflow:visible">
<path
style="stroke:none"
d="m -24.9375,-135.375 -143.29687,0 0,-6.32812 143.29687,0 0,-129.04688 -146.45312,0 0,-5.9375 152.781245,0 0,276.6875 -153.578125,0 0,-5.9375 147.25,0 z"
id="path12"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph1-0"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.140625,0 180.499995,0 0,-262.4375 -180.499995,0 z M 178.125,-250.15625 l 0,237.09375 -79.171875,-118.75 z m -5.14063,243.03125 -154.781245,0 77.1875,-117.5625 z m -77.593745,-131.8125 -77.1875,-116.375 154.781245,0 z m -3.5625,7.125 -79.15625,118.75 0,-237.09375 z"
id="path15"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph1-1"
style="overflow:visible">
<path
style="stroke:none"
d="m 24.9375,0 0,-264.40625 L 207.8125,0 l 6.32812,0 0,-277.07812 -5.9375,0 0,265.999995 -184.062495,-265.999995 -5.53125,-0.39063 0,277.46875 z"
id="path18"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph1-2"
style="overflow:visible">
<path
style="stroke:none"
d="m 24.9375,-276.6875 -6.328125,0 0,276.6875 C 70.453125,0 121.92188,0 173.76562,0 l 0,-6.328125 -148.82812,0 z"
id="path21"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-0"
style="overflow:visible">
<path
style="stroke:none"
d="m 1.21875,0 42.5,0 0,-61.796875 -42.5,0 z m 40.734375,-58.921875 0,55.84375 -18.640625,-27.96875 z m -1.21875,57.25 -36.453125,0 18.1875,-27.6875 z M 22.46875,-32.71875 4.28125,-60.125 l 36.453125,0 z m -0.84375,1.671875 -18.640625,27.96875 0,-55.84375 z"
id="path24"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-1"
style="overflow:visible">
<path
style="stroke:none"
d="m 24.796875,0.28125 0,-64.046875 22.5625,0 0,-1.484375 -46.515625,0 0,1.484375 22.546875,0 0,64.046875 z"
id="path27"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-2"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.78125,-65.15625 -1.40625,0 0,65.15625 1.40625,0 0,-32.34375 40.921875,0 0,32.34375 1.484375,0 0,-65.15625 -1.484375,0 0,31.40625 -40.921875,0 z"
id="path30"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-3"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.875,-31.875 33.75,0 0,-1.5 -33.75,0 0,-30.390625 34.484375,0 0,-1.390625 -35.984375,0 0,65.15625 36.171875,0 0,-1.390625 -34.671875,0 z"
id="path33"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-4"
style="overflow:visible">
<path
style="stroke:none"
d="m 67.765625,-32.625 c 0,8.859375 -3.53125,16.78125 -9.21875,22.5625 -5.78125,5.875 -13.796875,9.5 -22.5625,9.5 -8.765625,0 -16.6875,-3.625 -22.46875,-9.5 C 7.828125,-15.84375 4.1875,-23.765625 4.1875,-32.625 c 0,-8.859375 3.640625,-16.78125 9.328125,-22.5625 5.78125,-5.875 13.703125,-9.5 22.46875,-9.5 8.765625,0 16.78125,3.625 22.5625,9.5 5.6875,5.78125 9.21875,13.703125 9.21875,22.5625 z m -65.0625,0 c 0,9.234375 3.828125,17.609375 9.78125,23.671875 5.96875,6.15625 14.359375,9.890625 23.5,9.890625 9.234375,0 17.515625,-3.734375 23.578125,-9.890625 5.96875,-6.0625 9.796875,-14.4375 9.796875,-23.671875 0,-9.234375 -3.828125,-17.625 -9.796875,-23.671875 C 53.5,-62.453125 45.21875,-66.1875 35.984375,-66.1875 c -9.140625,0 -17.53125,3.734375 -23.5,9.890625 C 6.53125,-50.25 2.703125,-41.859375 2.703125,-32.625 z"
id="path36"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-5"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.78125,-63.765625 21.15625,0 c 24.234375,0 24.234375,35.234375 0,35.234375 l -12.671875,0 0,1.40625 L 44.28125,0 46.421875,0 16.40625,-27.125 l 10.53125,0 c 26.109375,0 26.109375,-38.125 0,-38.125 l -22.5625,0 0,65.25 1.40625,0 z"
id="path39"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-6"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.78125,-63.109375 26.5625,39.53125 1.21875,0 26.5625,-39.53125 0,63.109375 1.40625,0 0,-65.25 -1.5,0 -27.125,39.984375 L 5.875,-65.25 l -1.5,0 0,65.25 1.40625,0 z"
id="path42"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-7"
style="overflow:visible">
<path
style="stroke:none"
d=""
id="path45"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-8"
style="overflow:visible">
<path
style="stroke:none"
d="m 4.375,-64.5 c 0,21.53125 0,42.96875 0,64.5 l 1.5,0 0,-24.796875 21.0625,0 c 26.109375,0 26.109375,-40.453125 0,-40.453125 l -22.5625,0 z m 1.5,38.3125 0,-37.578125 21.0625,0 c 24.234375,0 24.234375,37.578125 0,37.578125 z"
id="path48"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-9"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.78125,-63.765625 21.15625,0 c 24.234375,0 24.234375,35.234375 0,35.234375 l -12.671875,0 0,1.40625 L 44.28125,0 46.421875,0 16.40625,-27.125 l 10.53125,0 c 26.109375,0 26.109375,-38.125 0,-38.125 l -22.5625,0 0,65.25 1.40625,0 z"
id="path51"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-10"
style="overflow:visible">
<path
style="stroke:none"
d="m 67.03125,-32.625 c 0,8.859375 -3.546875,16.78125 -9.234375,22.5625 -5.78125,5.875 -13.796875,9.5 -22.5625,9.5 -8.765625,0 -16.6875,-3.625 -22.46875,-9.5 -5.6875,-5.78125 -9.3125,-13.703125 -9.3125,-22.5625 0,-8.859375 3.625,-16.78125 9.3125,-22.5625 5.78125,-5.875 13.703125,-9.5 22.46875,-9.5 8.765625,0 16.78125,3.625 22.5625,9.5 5.6875,5.78125 9.234375,13.703125 9.234375,22.5625 z m -65.078125,0 c 0,9.234375 3.828125,17.609375 9.796875,23.671875 5.96875,6.15625 14.34375,9.890625 23.484375,9.890625 9.234375,0 17.53125,-3.734375 23.59375,-9.890625 5.953125,-6.0625 9.78125,-14.4375 9.78125,-23.671875 0,-9.234375 -3.828125,-17.625 -9.78125,-23.671875 -6.0625,-6.15625 -14.359375,-9.890625 -23.59375,-9.890625 -9.140625,0 -17.515625,3.734375 -23.484375,9.890625 C 5.78125,-50.25 1.953125,-41.859375 1.953125,-32.625 z"
id="path54"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-11"
style="overflow:visible">
<path
style="stroke:none"
d="m 27.5,-0.84375 -25.359375,-64.3125 -1.578125,0 26.1875,66.1875 1.59375,0 26.1875,-66.1875 -1.578125,0 z"
id="path57"
inkscape:connector-curvature="0" />
</symbol>
<symbol
overflow="visible"
id="glyph2-12"
style="overflow:visible">
<path
style="stroke:none"
d="m 5.875,-31.875 33.75,0 0,-1.5 -33.75,0 0,-30.390625 34.484375,0 0,-1.390625 -35.984375,0 0,65.15625 36.171875,0 0,-1.390625 -34.671875,0 z"
id="path60"
inkscape:connector-curvature="0" />
</symbol>
</g>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3094">
<rect
style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3096"
width="754.1781"
height="300.61642"
x="13.184932"
y="8.90411" />
</clipPath>
</defs>
<g
id="surface1"
clip-path="url(#clipPath3094)"
transform="matrix(0.30563256,0,0,0.25971391,2.2768141,14.142563)">
<g
style="fill:#000000;fill-opacity:1"
id="g71" />
<g
id="g1226">
<path
style="fill:#000000;fill-opacity:1;stroke:none"
d="M 323.2793,166.51926 H 179.98243 v -6.32812 H 323.2793 V 31.14426 H 176.82618 v -5.9375 h 152.78124 v 276.6875 h -304.879816 V 25.210709 h 6.339311 v 270.787071 h 292.212385 z"
id="path188"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc" />
<path
style="fill:#000000;fill-opacity:1;stroke:none"
d="M 554.58618,301.89426 V 37.48801 l 182.875,264.40625 h 6.32812 V 25.303323 h -5.9375 V 290.81613 L 553.78931,25.303323 h -5.53125 V 301.89426 Z"
id="path196"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
d="m 493.91087,342.7146 35.08932,83.78546 35.25165,-83.75238"
transform="matrix(3.192199,0,0,3.192199,-1250.002,-1067.559)"
id="path75"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
d="m 511.6195,385.54515 34.93019,0.0"
transform="matrix(3.192199,0,0,3.192199,-1250.002,-1067.559)"
id="path77"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

44
assets/js/app.js Normal file
View File

@ -0,0 +1,44 @@
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
// to get started and then uncomment the line below.
// import "./user_socket.js"
// You can include dependencies in two ways.
//
// The simplest option is to put them in assets/vendor and
// import them using relative paths:
//
// import "../vendor/some-package.js"
//
// Alternatively, you can `npm install some-package --prefix assets` and import
// them using a path starting with the package name:
//
// import "some-package"
//
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import { Socket } from "phoenix"
import { LiveSocket } from "phoenix_live_view"
import topbar from "../vendor/topbar"
let csrfToken = document
.querySelector("meta[name='csrf-token']")
.getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
})
// Show progress bar on live navigation and form submits
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" })
window.addEventListener("phx:page-loading-start", (_info) => topbar.show(300))
window.addEventListener("phx:page-loading-stop", (_info) => topbar.hide())
// connect if there are any LiveViews on the page
liveSocket.connect()
// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket

689
assets/node-env.nix Normal file
View File

@ -0,0 +1,689 @@
# This file originates from node2nix
{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}:
let
# Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master
utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux;
python = if nodejs ? python then nodejs.python else python2;
# Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
tarWrapper = runCommand "tarWrapper" {} ''
mkdir -p $out/bin
cat > $out/bin/tar <<EOF
#! ${stdenv.shell} -e
$(type -p tar) "\$@" --warning=no-unknown-keyword --delay-directory-restore
EOF
chmod +x $out/bin/tar
'';
# Function that generates a TGZ file from a NPM project
buildNodeSourceDist =
{ name, version, src, ... }:
stdenv.mkDerivation {
name = "node-tarball-${name}-${version}";
inherit src;
buildInputs = [ nodejs ];
buildPhase = ''
export HOME=$TMPDIR
tgzFile=$(npm pack | tail -n 1) # Hooks to the pack command will add output (https://docs.npmjs.com/misc/scripts)
'';
installPhase = ''
mkdir -p $out/tarballs
mv $tgzFile $out/tarballs
mkdir -p $out/nix-support
echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
'';
};
# Common shell logic
installPackage = writeShellScript "install-package" ''
installPackage() {
local packageName=$1 src=$2
local strippedName
local DIR=$PWD
cd $TMPDIR
unpackFile $src
# Make the base dir in which the target dependency resides first
mkdir -p "$(dirname "$DIR/$packageName")"
if [ -f "$src" ]
then
# Figure out what directory has been unpacked
packageDir="$(find . -maxdepth 1 -type d | tail -1)"
# Restore write permissions to make building work
find "$packageDir" -type d -exec chmod u+x {} \;
chmod -R u+w "$packageDir"
# Move the extracted tarball into the output folder
mv "$packageDir" "$DIR/$packageName"
elif [ -d "$src" ]
then
# Get a stripped name (without hash) of the source directory.
# On old nixpkgs it's already set internally.
if [ -z "$strippedName" ]
then
strippedName="$(stripHash $src)"
fi
# Restore write permissions to make building work
chmod -R u+w "$strippedName"
# Move the extracted directory into the output folder
mv "$strippedName" "$DIR/$packageName"
fi
# Change to the package directory to install dependencies
cd "$DIR/$packageName"
}
'';
# Bundle the dependencies of the package
#
# Only include dependencies if they don't exist. They may also be bundled in the package.
includeDependencies = {dependencies}:
lib.optionalString (dependencies != []) (
''
mkdir -p node_modules
cd node_modules
''
+ (lib.concatMapStrings (dependency:
''
if [ ! -e "${dependency.packageName}" ]; then
${composePackage dependency}
fi
''
) dependencies)
+ ''
cd ..
''
);
# Recursively composes the dependencies of a package
composePackage = { name, packageName, src, dependencies ? [], ... }@args:
builtins.addErrorContext "while evaluating node package '${packageName}'" ''
installPackage "${packageName}" "${src}"
${includeDependencies { inherit dependencies; }}
cd ..
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
'';
pinpointDependencies = {dependencies, production}:
let
pinpointDependenciesFromPackageJSON = writeTextFile {
name = "pinpointDependencies.js";
text = ''
var fs = require('fs');
var path = require('path');
function resolveDependencyVersion(location, name) {
if(location == process.env['NIX_STORE']) {
return null;
} else {
var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json");
if(fs.existsSync(dependencyPackageJSON)) {
var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON));
if(dependencyPackageObj.name == name) {
return dependencyPackageObj.version;
}
} else {
return resolveDependencyVersion(path.resolve(location, ".."), name);
}
}
}
function replaceDependencies(dependencies) {
if(typeof dependencies == "object" && dependencies !== null) {
for(var dependency in dependencies) {
var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency);
if(resolvedVersion === null) {
process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n");
} else {
dependencies[dependency] = resolvedVersion;
}
}
}
}
/* Read the package.json configuration */
var packageObj = JSON.parse(fs.readFileSync('./package.json'));
/* Pinpoint all dependencies */
replaceDependencies(packageObj.dependencies);
if(process.argv[2] == "development") {
replaceDependencies(packageObj.devDependencies);
}
else {
packageObj.devDependencies = {};
}
replaceDependencies(packageObj.optionalDependencies);
replaceDependencies(packageObj.peerDependencies);
/* Write the fixed package.json file */
fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2));
'';
};
in
''
node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"}
${lib.optionalString (dependencies != [])
''
if [ -d node_modules ]
then
cd node_modules
${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies}
cd ..
fi
''}
'';
# Recursively traverses all dependencies of a package and pinpoints all
# dependencies in the package.json file to the versions that are actually
# being used.
pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args:
''
if [ -d "${packageName}" ]
then
cd "${packageName}"
${pinpointDependencies { inherit dependencies production; }}
cd ..
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
fi
'';
# Extract the Node.js source code which is used to compile packages with
# native bindings
nodeSources = runCommand "node-sources" {} ''
tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
mv node-* $out
'';
# Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty)
addIntegrityFieldsScript = writeTextFile {
name = "addintegrityfields.js";
text = ''
var fs = require('fs');
var path = require('path');
function augmentDependencies(baseDir, dependencies) {
for(var dependencyName in dependencies) {
var dependency = dependencies[dependencyName];
// Open package.json and augment metadata fields
var packageJSONDir = path.join(baseDir, "node_modules", dependencyName);
var packageJSONPath = path.join(packageJSONDir, "package.json");
if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored
console.log("Adding metadata fields to: "+packageJSONPath);
var packageObj = JSON.parse(fs.readFileSync(packageJSONPath));
if(dependency.integrity) {
packageObj["_integrity"] = dependency.integrity;
} else {
packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads.
}
if(dependency.resolved) {
packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided
} else {
packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories.
}
if(dependency.from !== undefined) { // Adopt from property if one has been provided
packageObj["_from"] = dependency.from;
}
fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2));
}
// Augment transitive dependencies
if(dependency.dependencies !== undefined) {
augmentDependencies(packageJSONDir, dependency.dependencies);
}
}
}
if(fs.existsSync("./package-lock.json")) {
var packageLock = JSON.parse(fs.readFileSync("./package-lock.json"));
if(![1, 2].includes(packageLock.lockfileVersion)) {
process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n");
process.exit(1);
}
if(packageLock.dependencies !== undefined) {
augmentDependencies(".", packageLock.dependencies);
}
}
'';
};
# Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes
reconstructPackageLock = writeTextFile {
name = "reconstructpackagelock.js";
text = ''
var fs = require('fs');
var path = require('path');
var packageObj = JSON.parse(fs.readFileSync("package.json"));
var lockObj = {
name: packageObj.name,
version: packageObj.version,
lockfileVersion: 2,
requires: true,
packages: {
"": {
name: packageObj.name,
version: packageObj.version,
license: packageObj.license,
bin: packageObj.bin,
dependencies: packageObj.dependencies,
engines: packageObj.engines,
optionalDependencies: packageObj.optionalDependencies
}
},
dependencies: {}
};
function augmentPackageJSON(filePath, packages, dependencies) {
var packageJSON = path.join(filePath, "package.json");
if(fs.existsSync(packageJSON)) {
var packageObj = JSON.parse(fs.readFileSync(packageJSON));
packages[filePath] = {
version: packageObj.version,
integrity: "sha1-000000000000000000000000000=",
dependencies: packageObj.dependencies,
engines: packageObj.engines,
optionalDependencies: packageObj.optionalDependencies
};
dependencies[packageObj.name] = {
version: packageObj.version,
integrity: "sha1-000000000000000000000000000=",
dependencies: {}
};
processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies);
}
}
function processDependencies(dir, packages, dependencies) {
if(fs.existsSync(dir)) {
var files = fs.readdirSync(dir);
files.forEach(function(entry) {
var filePath = path.join(dir, entry);
var stats = fs.statSync(filePath);
if(stats.isDirectory()) {
if(entry.substr(0, 1) == "@") {
// When we encounter a namespace folder, augment all packages belonging to the scope
var pkgFiles = fs.readdirSync(filePath);
pkgFiles.forEach(function(entry) {
if(stats.isDirectory()) {
var pkgFilePath = path.join(filePath, entry);
augmentPackageJSON(pkgFilePath, packages, dependencies);
}
});
} else {
augmentPackageJSON(filePath, packages, dependencies);
}
}
});
}
}
processDependencies("node_modules", lockObj.packages, lockObj.dependencies);
fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2));
'';
};
# Script that links bins defined in package.json to the node_modules bin directory
# NPM does not do this for top-level packages itself anymore as of v7
linkBinsScript = writeTextFile {
name = "linkbins.js";
text = ''
var fs = require('fs');
var path = require('path');
var packageObj = JSON.parse(fs.readFileSync("package.json"));
var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep);
if(packageObj.bin !== undefined) {
fs.mkdirSync(path.join(nodeModules, ".bin"))
if(typeof packageObj.bin == "object") {
Object.keys(packageObj.bin).forEach(function(exe) {
if(fs.existsSync(packageObj.bin[exe])) {
console.log("linking bin '" + exe + "'");
fs.symlinkSync(
path.join("..", packageObj.name, packageObj.bin[exe]),
path.join(nodeModules, ".bin", exe)
);
}
else {
console.log("skipping non-existent bin '" + exe + "'");
}
})
}
else {
if(fs.existsSync(packageObj.bin)) {
console.log("linking bin '" + packageObj.bin + "'");
fs.symlinkSync(
path.join("..", packageObj.name, packageObj.bin),
path.join(nodeModules, ".bin", packageObj.name.split("/").pop())
);
}
else {
console.log("skipping non-existent bin '" + packageObj.bin + "'");
}
}
}
else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) {
fs.mkdirSync(path.join(nodeModules, ".bin"))
fs.readdirSync(packageObj.directories.bin).forEach(function(exe) {
if(fs.existsSync(path.join(packageObj.directories.bin, exe))) {
console.log("linking bin '" + exe + "'");
fs.symlinkSync(
path.join("..", packageObj.name, packageObj.directories.bin, exe),
path.join(nodeModules, ".bin", exe)
);
}
else {
console.log("skipping non-existent bin '" + exe + "'");
}
})
}
'';
};
prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}:
let
forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com";
in
''
# Pinpoint the versions of all dependencies to the ones that are actually being used
echo "pinpointing versions of dependencies..."
source $pinpointDependenciesScriptPath
# Patch the shebangs of the bundled modules to prevent them from
# calling executables outside the Nix store as much as possible
patchShebangs .
# Deploy the Node.js package by running npm install. Since the
# dependencies have been provided already by ourselves, it should not
# attempt to install them again, which is good, because we want to make
# it Nix's responsibility. If it needs to install any dependencies
# anyway (e.g. because the dependency parameters are
# incomplete/incorrect), it fails.
#
# The other responsibilities of NPM are kept -- version checks, build
# steps, postprocessing etc.
export HOME=$TMPDIR
cd "${packageName}"
runHook preRebuild
${lib.optionalString bypassCache ''
${lib.optionalString reconstructLock ''
if [ -f package-lock.json ]
then
echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!"
echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!"
rm package-lock.json
else
echo "No package-lock.json file found, reconstructing..."
fi
node ${reconstructPackageLock}
''}
node ${addIntegrityFieldsScript}
''}
npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild
runHook postRebuild
if [ "''${dontNpmInstall-}" != "1" ]
then
# NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
rm -f npm-shrinkwrap.json
npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install
fi
# Link executables defined in package.json
node ${linkBinsScript}
'';
# Builds and composes an NPM package including all its dependencies
buildNodePackage =
{ name
, packageName
, version ? null
, dependencies ? []
, buildInputs ? []
, production ? true
, npmFlags ? ""
, dontNpmInstall ? false
, bypassCache ? false
, reconstructLock ? false
, preRebuild ? ""
, dontStrip ? true
, unpackPhase ? "true"
, buildPhase ? "true"
, meta ? {}
, ... }@args:
let
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" "meta" ];
in
stdenv.mkDerivation ({
name = "${name}${if version == null then "" else "-${version}"}";
buildInputs = [ tarWrapper python nodejs ]
++ lib.optional (stdenv.isLinux) utillinux
++ lib.optional (stdenv.isDarwin) libtool
++ buildInputs;
inherit nodejs;
inherit dontStrip; # Stripping may fail a build for some package deployments
inherit dontNpmInstall preRebuild unpackPhase buildPhase;
compositionScript = composePackage args;
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
passAsFile = [ "compositionScript" "pinpointDependenciesScript" ];
installPhase = ''
source ${installPackage}
# Create and enter a root node_modules/ folder
mkdir -p $out/lib/node_modules
cd $out/lib/node_modules
# Compose the package and all its dependencies
source $compositionScriptPath
${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
# Create symlink to the deployed executable folder, if applicable
if [ -d "$out/lib/node_modules/.bin" ]
then
ln -s $out/lib/node_modules/.bin $out/bin
# Fixup all executables
ls $out/bin/* | while read i
do
file="$(readlink -f "$i")"
chmod u+rwx "$file"
if isScript "$file"
then
sed -i 's/\r$//' "$file" # convert crlf to lf
fi
done
fi
# Create symlinks to the deployed manual page folders, if applicable
if [ -d "$out/lib/node_modules/${packageName}/man" ]
then
mkdir -p $out/share
for dir in "$out/lib/node_modules/${packageName}/man/"*
do
mkdir -p $out/share/man/$(basename "$dir")
for page in "$dir"/*
do
ln -s $page $out/share/man/$(basename "$dir")
done
done
fi
# Run post install hook, if provided
runHook postInstall
'';
meta = {
# default to Node.js' platforms
platforms = nodejs.meta.platforms;
} // meta;
} // extraArgs);
# Builds a node environment (a node_modules folder and a set of binaries)
buildNodeDependencies =
{ name
, packageName
, version ? null
, src
, dependencies ? []
, buildInputs ? []
, production ? true
, npmFlags ? ""
, dontNpmInstall ? false
, bypassCache ? false
, reconstructLock ? false
, dontStrip ? true
, unpackPhase ? "true"
, buildPhase ? "true"
, ... }@args:
let
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ];
in
stdenv.mkDerivation ({
name = "node-dependencies-${name}${if version == null then "" else "-${version}"}";
buildInputs = [ tarWrapper python nodejs ]
++ lib.optional (stdenv.isLinux) utillinux
++ lib.optional (stdenv.isDarwin) libtool
++ buildInputs;
inherit dontStrip; # Stripping may fail a build for some package deployments
inherit dontNpmInstall unpackPhase buildPhase;
includeScript = includeDependencies { inherit dependencies; };
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
passAsFile = [ "includeScript" "pinpointDependenciesScript" ];
installPhase = ''
source ${installPackage}
mkdir -p $out/${packageName}
cd $out/${packageName}
source $includeScriptPath
# Create fake package.json to make the npm commands work properly
cp ${src}/package.json .
chmod 644 package.json
${lib.optionalString bypassCache ''
if [ -f ${src}/package-lock.json ]
then
cp ${src}/package-lock.json .
chmod 644 package-lock.json
fi
''}
# Go to the parent folder to make sure that all packages are pinpointed
cd ..
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
# Expose the executables that were installed
cd ..
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
mv ${packageName} lib
ln -s $out/lib/node_modules/.bin $out/bin
'';
} // extraArgs);
# Builds a development shell
buildNodeShell =
{ name
, packageName
, version ? null
, src
, dependencies ? []
, buildInputs ? []
, production ? true
, npmFlags ? ""
, dontNpmInstall ? false
, bypassCache ? false
, reconstructLock ? false
, dontStrip ? true
, unpackPhase ? "true"
, buildPhase ? "true"
, ... }@args:
let
nodeDependencies = buildNodeDependencies args;
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "unpackPhase" "buildPhase" ];
in
stdenv.mkDerivation ({
name = "node-shell-${name}${if version == null then "" else "-${version}"}";
buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs;
buildCommand = ''
mkdir -p $out/bin
cat > $out/bin/shell <<EOF
#! ${stdenv.shell} -e
$shellHook
exec ${stdenv.shell}
EOF
chmod +x $out/bin/shell
'';
# Provide the dependencies in a development shell through the NODE_PATH environment variable
inherit nodeDependencies;
shellHook = lib.optionalString (dependencies != []) ''
export NODE_PATH=${nodeDependencies}/lib/node_modules
export PATH="${nodeDependencies}/bin:$PATH"
'';
} // extraArgs);
in
{
buildNodeSourceDist = lib.makeOverridable buildNodeSourceDist;
buildNodePackage = lib.makeOverridable buildNodePackage;
buildNodeDependencies = lib.makeOverridable buildNodeDependencies;
buildNodeShell = lib.makeOverridable buildNodeShell;
}

40
assets/node-packages.nix Normal file
View File

@ -0,0 +1,40 @@
# This file has been generated by node2nix 1.11.1. Do not edit!
{nodeEnv, fetchurl, fetchgit, nix-gitignore, stdenv, lib, globalBuildInputs ? []}:
let
sources = {};
args = {
name = "portfolio";
packageName = "portfolio";
version = "0.1.0";
src = ./.;
buildInputs = globalBuildInputs;
meta = {
description = "";
license = "ISC";
};
production = true;
bypassCache = true;
reconstructLock = false;
};
in
{
args = args;
sources = sources;
tarball = nodeEnv.buildNodeSourceDist args;
package = nodeEnv.buildNodePackage args;
shell = nodeEnv.buildNodeShell args;
nodeDependencies = nodeEnv.buildNodeDependencies (lib.overrideExisting args {
src = stdenv.mkDerivation {
name = args.name + "-package-json";
src = nix-gitignore.gitignoreSourcePure [
"*"
"!package.json"
"!package-lock.json"
] args.src;
dontBuild = true;
installPhase = "mkdir -p $out; cp -r ./* $out;";
};
});
}

13
assets/package-lock.json generated Normal file
View File

@ -0,0 +1,13 @@
{
"name": "portfolio",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "portfolio",
"version": "0.1.0",
"license": "ISC"
}
}
}

14
assets/package.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "portfolio",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "tsc",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,7 @@
/** @type {import('prettier').Options} */
module.exports = {
arrowParens: "always",
semi: false,
tabWidth: 2,
trailingComma: "es5",
}

94
assets/tailwind.config.js Normal file
View File

@ -0,0 +1,94 @@
// See the Tailwind configuration guide for advanced usage
// https://tailwindcss.com/docs/configuration
const plugin = require("tailwindcss/plugin")
const fs = require("fs")
const path = require("path")
module.exports = {
content: [
"./js/**/*.js",
"../lib/portfolio_web.ex",
"../lib/portfolio_web/**/*.*ex",
],
theme: {
extend: {
colors: {
brand: "#FD4F00",
info: "#0F3042",
tip: "#1B7A46",
warning: "#C68E08",
},
},
},
plugins: [
require("@tailwindcss/forms"),
// Allows prefixing tailwind classes with LiveView classes to add rules
// only when LiveView classes are applied, for example:
//
// <div class="phx-click-loading:animate-ping">
//
plugin(({ addVariant }) =>
addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])
),
plugin(({ addVariant }) =>
addVariant("phx-click-loading", [
".phx-click-loading&",
".phx-click-loading &",
])
),
plugin(({ addVariant }) =>
addVariant("phx-submit-loading", [
".phx-submit-loading&",
".phx-submit-loading &",
])
),
plugin(({ addVariant }) =>
addVariant("phx-change-loading", [
".phx-change-loading&",
".phx-change-loading &",
])
),
// Embeds Heroicons (https://heroicons.com) into your app.css bundle
// See your `CoreComponents.icon/1` for more information.
//
plugin(function ({ matchComponents, theme }) {
let iconsDir = path.join(__dirname, "./vendor/heroicons/optimized")
let values = {}
let icons = [
["", "/24/outline"],
["-solid", "/24/solid"],
["-mini", "/20/solid"],
]
icons.forEach(([suffix, dir]) => {
fs.readdirSync(path.join(iconsDir, dir)).forEach((file) => {
let name = path.basename(file, ".svg") + suffix
values[name] = { name, fullPath: path.join(iconsDir, dir, file) }
})
})
matchComponents(
{
hero: ({ name, fullPath }) => {
let content = fs
.readFileSync(fullPath)
.toString()
.replace(/\r?\n|\r/g, "")
return {
[`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
"-webkit-mask": `var(--hero-${name})`,
mask: `var(--hero-${name})`,
"mask-repeat": "no-repeat",
"background-color": "currentColor",
"vertical-align": "middle",
display: "inline-block",
width: theme("spacing.5"),
height: theme("spacing.5"),
}
},
},
{ values }
)
}),
],
}

30
assets/tsconfig.json Normal file
View File

@ -0,0 +1,30 @@
{
// https://esbuild.github.io/content-types/#tsconfig-json
"compilerOptions": {
// Keep in mind that ES6+ syntax to ES5 is not supported in esbuild yet.
"target": "es2016",
// Even when transpiling a single module, the TypeScript compiler actually
// parses imported files so it can tell whether an imported name is a type
// or a value. However, tools like esbuild compile each file in isolation so
// they can't tell if an imported name is a type or a value.
// https://esbuild.github.io/content-types/#isolated-modules
"isolatedModules": true,
// Disables legacy behavior around imports and makes TypeScript's type
// system compatible with ESM.
"esModuleInterop": true,
// Enables define semantics. In this mode, TypeScript class fields behave
// like normal JavaScript class fields. Field initializers do not trigger
// setters on the base class.
"useDefineForClassFields": true,
// If either of these options are enabled, esbuild will consider all code
// in all TypeScript files to be in strict mode and will prefix generated
// code with "use strict" unless the output format is set to esm (since all
// ESM files are automatically in strict mode).
"strict": true,
// Emit .js files with JSX changed to the equivalent React.createElement
// calls. It seems like the "react" value mirrors esbuild's native
// "transform" option, but it isn't obvious how these two relate from the
// documentation: https://esbuild.github.io/api/#jsx.
"jsx": "react"
}
}

21
assets/vendor/heroicons/LICENSE.md vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Refactoring UI Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
assets/vendor/heroicons/UPGRADE.md vendored Normal file
View File

@ -0,0 +1,6 @@
You are running heroicons v2.0.16. To upgrade in place, you can run the following command,
where your `HERO_VSN` export is your desired version:
export HERO_VSN="2.0.16" ; \
curl -L "https://github.com/tailwindlabs/heroicons/archive/refs/tags/v${HERO_VSN}.tar.gz" | \
tar -xvz --strip-components=1 heroicons-${HERO_VSN}/optimized

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M9.664 1.319a.75.75 0 01.672 0 41.059 41.059 0 018.198 5.424.75.75 0 01-.254 1.285 31.372 31.372 0 00-7.86 3.83.75.75 0 01-.84 0 31.508 31.508 0 00-2.08-1.287V9.394c0-.244.116-.463.302-.592a35.504 35.504 0 013.305-2.033.75.75 0 00-.714-1.319 37 37 0 00-3.446 2.12A2.216 2.216 0 006 9.393v.38a31.293 31.293 0 00-4.28-1.746.75.75 0 01-.254-1.285 41.059 41.059 0 018.198-5.424zM6 11.459a29.848 29.848 0 00-2.455-1.158 41.029 41.029 0 00-.39 3.114.75.75 0 00.419.74c.528.256 1.046.53 1.554.82-.21.324-.455.63-.739.914a.75.75 0 101.06 1.06c.37-.369.69-.77.96-1.193a26.61 26.61 0 013.095 2.348.75.75 0 00.992 0 26.547 26.547 0 015.93-3.95.75.75 0 00.42-.739 41.053 41.053 0 00-.39-3.114 29.925 29.925 0 00-5.199 2.801 2.25 2.25 0 01-2.514 0c-.41-.275-.826-.541-1.25-.797a6.985 6.985 0 01-1.084 3.45 26.503 26.503 0 00-1.281-.78A5.487 5.487 0 006 12v-.54z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10 3.75a2 2 0 10-4 0 2 2 0 004 0zM17.25 4.5a.75.75 0 000-1.5h-5.5a.75.75 0 000 1.5h5.5zM5 3.75a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5a.75.75 0 01.75.75zM4.25 17a.75.75 0 000-1.5h-1.5a.75.75 0 000 1.5h1.5zM17.25 17a.75.75 0 000-1.5h-5.5a.75.75 0 000 1.5h5.5zM9 10a.75.75 0 01-.75.75h-5.5a.75.75 0 010-1.5h5.5A.75.75 0 019 10zM17.25 10.75a.75.75 0 000-1.5h-1.5a.75.75 0 000 1.5h1.5zM14 10a2 2 0 10-4 0 2 2 0 004 0zM10 16.25a2 2 0 10-4 0 2 2 0 004 0z"/>
</svg>

After

Width:  |  Height:  |  Size: 576 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M17 2.75a.75.75 0 00-1.5 0v5.5a.75.75 0 001.5 0v-5.5zM17 15.75a.75.75 0 00-1.5 0v1.5a.75.75 0 001.5 0v-1.5zM3.75 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5a.75.75 0 01.75-.75zM4.5 2.75a.75.75 0 00-1.5 0v5.5a.75.75 0 001.5 0v-5.5zM10 11a.75.75 0 01.75.75v5.5a.75.75 0 01-1.5 0v-5.5A.75.75 0 0110 11zM10.75 2.75a.75.75 0 00-1.5 0v1.5a.75.75 0 001.5 0v-1.5zM10 6a2 2 0 100 4 2 2 0 000-4zM3.75 10a2 2 0 100 4 2 2 0 000-4zM16.25 10a2 2 0 100 4 2 2 0 000-4z"/>
</svg>

After

Width:  |  Height:  |  Size: 578 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M2 3a1 1 0 00-1 1v1a1 1 0 001 1h16a1 1 0 001-1V4a1 1 0 00-1-1H2zm0 4.5h16l-.811 7.71a2 2 0 01-1.99 1.79H4.802a2 2 0 01-1.99-1.79L2 7.5zM10 9a.75.75 0 01.75.75v2.546l.943-1.048a.75.75 0 111.114 1.004l-2.25 2.5a.75.75 0 01-1.114 0l-2.25-2.5a.75.75 0 111.114-1.004l.943 1.048V9.75A.75.75 0 0110 9z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 456 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M2 3a1 1 0 00-1 1v1a1 1 0 001 1h16a1 1 0 001-1V4a1 1 0 00-1-1H2z"/>
<path fill-rule="evenodd" d="M2 7.5h16l-.811 7.71a2 2 0 01-1.99 1.79H4.802a2 2 0 01-1.99-1.79L2 7.5zm5.22 1.72a.75.75 0 011.06 0L10 10.94l1.72-1.72a.75.75 0 111.06 1.06L11.06 12l1.72 1.72a.75.75 0 11-1.06 1.06L10 13.06l-1.72 1.72a.75.75 0 01-1.06-1.06L8.94 12l-1.72-1.72a.75.75 0 010-1.06z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M2 3a1 1 0 00-1 1v1a1 1 0 001 1h16a1 1 0 001-1V4a1 1 0 00-1-1H2z"/>
<path fill-rule="evenodd" d="M2 7.5h16l-.811 7.71a2 2 0 01-1.99 1.79H4.802a2 2 0 01-1.99-1.79L2 7.5zM7 11a1 1 0 011-1h4a1 1 0 110 2H8a1 1 0 01-1-1z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 359 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm.75-11.25a.75.75 0 00-1.5 0v4.59L7.3 9.24a.75.75 0 00-1.1 1.02l3.25 3.5a.75.75 0 001.1 0l3.25-3.5a.75.75 0 10-1.1-1.02l-1.95 2.1V6.75z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 330 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M14.78 5.22a.75.75 0 00-1.06 0L6.5 12.44V6.75a.75.75 0 00-1.5 0v7.5c0 .414.336.75.75.75h7.5a.75.75 0 000-1.5H7.56l7.22-7.22a.75.75 0 000-1.06z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M8 1a.75.75 0 01.75.75V6h-1.5V1.75A.75.75 0 018 1zm-.75 5v3.296l-.943-1.048a.75.75 0 10-1.114 1.004l2.25 2.5a.75.75 0 001.114 0l2.25-2.5a.75.75 0 00-1.114-1.004L8.75 9.296V6h2A2.25 2.25 0 0113 8.25v4.5A2.25 2.25 0 0110.75 15h-5.5A2.25 2.25 0 013 12.75v-4.5A2.25 2.25 0 015.25 6h2zM7 16.75v-.25h3.75a3.75 3.75 0 003.75-3.75V10h.25A2.25 2.25 0 0117 12.25v4.5A2.25 2.25 0 0114.75 19h-5.5A2.25 2.25 0 017 16.75z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 569 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M13.75 7h-3v5.296l1.943-2.048a.75.75 0 011.114 1.004l-3.25 3.5a.75.75 0 01-1.114 0l-3.25-3.5a.75.75 0 111.114-1.004l1.943 2.048V7h1.5V1.75a.75.75 0 00-1.5 0V7h-3A2.25 2.25 0 004 9.25v7.5A2.25 2.25 0 006.25 19h7.5A2.25 2.25 0 0016 16.75v-7.5A2.25 2.25 0 0013.75 7z"/>
</svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06l7.22 7.22H6.75a.75.75 0 000 1.5h7.5a.747.747 0 00.75-.75v-7.5a.75.75 0 00-1.5 0v5.69L6.28 5.22z"/>
</svg>

After

Width:  |  Height:  |  Size: 250 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.75 2.75a.75.75 0 00-1.5 0v8.614L6.295 8.235a.75.75 0 10-1.09 1.03l4.25 4.5a.75.75 0 001.09 0l4.25-4.5a.75.75 0 00-1.09-1.03l-2.955 3.129V2.75z"/>
<path d="M3.5 12.75a.75.75 0 00-1.5 0v2.5A2.75 2.75 0 004.75 18h10.5A2.75 2.75 0 0018 15.25v-2.5a.75.75 0 00-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 317 B

View File

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<g clip-path="url(#clip0_9_2121)">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.25-7.25a.75.75 0 000-1.5H8.66l2.1-1.95a.75.75 0 10-1.02-1.1l-3.5 3.25a.75.75 0 000 1.1l3.5 3.25a.75.75 0 001.02-1.1l-2.1-1.95h4.59z" clip-rule="evenodd"/>
</g>
<defs>
<clipPath id="clip0_9_2121">
<path d="M0 0h20v20H0z"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 475 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M3 4.25A2.25 2.25 0 015.25 2h5.5A2.25 2.25 0 0113 4.25v2a.75.75 0 01-1.5 0v-2a.75.75 0 00-.75-.75h-5.5a.75.75 0 00-.75.75v11.5c0 .414.336.75.75.75h5.5a.75.75 0 00.75-.75v-2a.75.75 0 011.5 0v2A2.25 2.25 0 0110.75 18h-5.5A2.25 2.25 0 013 15.75V4.25z" clip-rule="evenodd"/>
<path fill-rule="evenodd" d="M19 10a.75.75 0 00-.75-.75H8.704l1.048-.943a.75.75 0 10-1.004-1.114l-2.5 2.25a.75.75 0 000 1.114l2.5 2.25a.75.75 0 101.004-1.114l-1.048-.943h9.546A.75.75 0 0019 10z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 628 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 319 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 2a.75.75 0 01.75.75v12.59l1.95-2.1a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 111.1-1.02l1.95 2.1V2.75A.75.75 0 0110 2z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 309 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a.75.75 0 01-.75.75H4.66l2.1 1.95a.75.75 0 11-1.02 1.1l-3.5-3.25a.75.75 0 010-1.1l3.5-3.25a.75.75 0 111.02 1.1l-2.1 1.95h12.59A.75.75 0 0118 10z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M2 10a.75.75 0 01.75-.75h12.59l-2.1-1.95a.75.75 0 111.02-1.1l3.5 3.25a.75.75 0 010 1.1l-3.5 3.25a.75.75 0 11-1.02-1.1l2.1-1.95H2.75A.75.75 0 012 10z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 310 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a.75.75 0 01-.75-.75V4.66L7.3 6.76a.75.75 0 11-1.1-1.02l3.25-3.5a.75.75 0 011.1 0l3.25 3.5a.75.75 0 01-1.1 1.02l-1.95-2.1v12.59A.75.75 0 0110 18z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 4.5c1.215 0 2.417.055 3.604.162a.68.68 0 01.615.597c.124 1.038.208 2.088.25 3.15l-1.689-1.69a.75.75 0 00-1.06 1.061l2.999 3a.75.75 0 001.06 0l3.001-3a.75.75 0 10-1.06-1.06l-1.748 1.747a41.31 41.31 0 00-.264-3.386 2.18 2.18 0 00-1.97-1.913 41.512 41.512 0 00-7.477 0 2.18 2.18 0 00-1.969 1.913 41.16 41.16 0 00-.16 1.61.75.75 0 101.495.12c.041-.52.093-1.038.154-1.552a.68.68 0 01.615-.597A40.012 40.012 0 0110 4.5zM5.281 9.22a.75.75 0 00-1.06 0l-3.001 3a.75.75 0 101.06 1.06l1.748-1.747c.042 1.141.13 2.27.264 3.386a2.18 2.18 0 001.97 1.913 41.533 41.533 0 007.477 0 2.18 2.18 0 001.969-1.913c.064-.534.117-1.071.16-1.61a.75.75 0 10-1.495-.12c-.041.52-.093 1.037-.154 1.552a.68.68 0 01-.615.597 40.013 40.013 0 01-7.208 0 .68.68 0 01-.615-.597 39.785 39.785 0 01-.25-3.15l1.689 1.69a.75.75 0 001.06-1.061l-2.999-3z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 979 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM6.75 9.25a.75.75 0 000 1.5h4.59l-2.1 1.95a.75.75 0 001.02 1.1l3.5-3.25a.75.75 0 000-1.1l-3.5-3.25a.75.75 0 10-1.02 1.1l2.1 1.95H6.75z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M3 4.25A2.25 2.25 0 015.25 2h5.5A2.25 2.25 0 0113 4.25v2a.75.75 0 01-1.5 0v-2a.75.75 0 00-.75-.75h-5.5a.75.75 0 00-.75.75v11.5c0 .414.336.75.75.75h5.5a.75.75 0 00.75-.75v-2a.75.75 0 011.5 0v2A2.25 2.25 0 0110.75 18h-5.5A2.25 2.25 0 013 15.75V4.25z" clip-rule="evenodd"/>
<path fill-rule="evenodd" d="M6 10a.75.75 0 01.75-.75h9.546l-1.048-.943a.75.75 0 111.004-1.114l2.5 2.25a.75.75 0 010 1.114l-2.5 2.25a.75.75 0 11-1.004-1.114l1.048-.943H6.75A.75.75 0 016 10z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 624 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M3 10a.75.75 0 01.75-.75h10.638L10.23 5.29a.75.75 0 111.04-1.08l5.5 5.25a.75.75 0 010 1.08l-5.5 5.25a.75.75 0 11-1.04-1.08l4.158-3.96H3.75A.75.75 0 013 10z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 317 B

Some files were not shown because too many files have changed in this diff Show More