styles; inkify

This commit is contained in:
Chris W 2023-12-08 17:46:05 -07:00
parent d5da4b58a7
commit 064da49acd
18 changed files with 212 additions and 35 deletions

View File

@ -2,21 +2,17 @@ import { defineConfig } from 'astro/config';
import icon from "astro-icon"; import icon from "astro-icon";
import mdx from '@astrojs/mdx'; import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap'; import sitemap from '@astrojs/sitemap';
import tailwind from "@astrojs/tailwind"; import tailwind from "@astrojs/tailwind";
import vue from "@astrojs/vue";
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: 'https://watzon.tech', site: 'https://watzon.tech',
integrations: [ integrations: [mdx(), sitemap(), tailwind(), icon({
mdx(), include: {
sitemap(), mdi: ['*'],
tailwind(), ic: ['*']
icon({ }
include: { }), vue()]
mdi: ['*'],
ic: ['*'],
}
}),
]
}); });

BIN
bun.lockb

Binary file not shown.

View File

@ -15,9 +15,11 @@
"@astrojs/rss": "^4.0.1", "@astrojs/rss": "^4.0.1",
"@astrojs/sitemap": "^3.0.3", "@astrojs/sitemap": "^3.0.3",
"@astrojs/tailwind": "^5.0.3", "@astrojs/tailwind": "^5.0.3",
"@astrojs/vue": "^4.0.2",
"astro": "^4.0.3", "astro": "^4.0.3",
"tailwindcss": "^3.0.24", "tailwindcss": "^3.0.24",
"typescript": "^5.3.3" "typescript": "^5.3.3",
"vue": "^3.2.30"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/arcticons": "^1.1.85", "@iconify-json/arcticons": "^1.1.85",

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@ -1,5 +1,6 @@
--- ---
import HeaderLink from './HeaderLink.astro'; import HeaderLink from './HeaderLink.astro';
import ThemeToggle from './ThemeToggle.astro';
--- ---
<header class="m-0 px-4"> <header class="m-0 px-4">
@ -11,7 +12,7 @@ import HeaderLink from './HeaderLink.astro';
<HeaderLink href="/projects">Projects</HeaderLink> <HeaderLink href="/projects">Projects</HeaderLink>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<!-- Theme toggle --> <ThemeToggle />
</div> </div>
</nav> </nav>
</header> </header>

View File

@ -0,0 +1,48 @@
---
import { Icon } from 'astro-icon/components';
---
<button id="themeToggle">
<Icon name="mdi:weather-sunny" class="sun w-8 h-8 text-gray-800" />
<Icon name="mdi:weather-night" class="moon w-8 h-8 text-gray-100" />
</button>
<style>
#themeToggle .sun {
@apply dark:hidden;
}
#themeToggle .moon {
@apply hidden dark:block;
}
</style>
<script is:inline>
const theme = (() => {
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
return localStorage.getItem('theme');
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
})();
if (theme === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
window.localStorage.setItem('theme', theme);
const handleToggleClick = () => {
const element = document.documentElement;
element.classList.toggle("dark");
const isDark = element.classList.contains("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
}
document.getElementById("themeToggle").addEventListener("click", handleToggleClick);
</script>

View File

@ -0,0 +1,66 @@
<template>
<div>
<div class="w-full mb-4" v-if="imgUrl">
<a :href="imgUrl" target="_blank">
<img :src="imgUrl" class="w-full" />
</a>
</div>
<div class="flex justify-center items-center border-4 border-dashed h-32 w-full mb-4" v-else>
Image will appear here
</div>
<div class="flex flex-col gap-4">
<textarea v-model="code" class="bg-gray-300 dark:bg-gray-800 w-full h-32 p-2 font-mono" placeholder="// Put some code here"></textarea>
<div class="flex flex-row justify-between items-center">
<select class="bg-gray-300 dark:bg-gray-800 text-black dark:text-white font-bold py-2 px-4 rounded" v-model="theme">
<option v-for="theme in themes" :value="theme">{{ theme }}</option>
</select>
<button @click="inkifyRequest" class="bg-blue-500 hover:bg-blue-700 text-white text-lg py-2 px-4 rounded">
Submit Code
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const code = ref<string>('')
const theme = ref<string>('Nord')
const imgUrl = ref<string | undefined>(undefined)
const windowTitle = "Inkify"
const pad_horiz = 20;
const pad_vert = 20;
const font = "Monaspace Neon=32"
const themes = [
"1337",
"Coldark-Cold",
"Coldark-Dark",
"DarkNeon",
"Dracula",
"GitHub",
"Monokai Extended",
"Monokai Extended Bright",
"Monokai Extended Light",
"Monokai Extended Origin",
"Nord",
"OneHalfDark",
"OneHalfLight",
"Solarized (dark)",
"Solarized (light)",
"Sublime Snazzy",
"TwoDark",
"Visual Studio Dark+",
"gruvbox-dark",
"gruvbox-light",
"zenburn"
]
const inkifyRequest = () => {
const encodedCode = encodeURIComponent(code.value)
const endpoint = `https://inkify.0x45.st/generate?code=${encodedCode}&theme=${theme.value}&window_title=${windowTitle}&pad_horiz=${pad_horiz}&pad_vert=${pad_vert}&font=${font}`
imgUrl.value = endpoint
}
</script>

View File

@ -10,5 +10,4 @@ tags:
- library - library
heroImage: /images/cadmium-header.png heroImage: /images/cadmium-header.png
linkOnly: true linkOnly: true
featured: true
--- ---

View File

@ -0,0 +1,38 @@
---
name: Inkify
url: https://github.com/watzon/inkify
description: Generate images of your code with this open source server
tags:
- project
- rust
- server
- self-hosted
featured: true
heroImage: /images/inkify-header.webp
---
import CodeToImg from '../../components/inkify/CodeToImg.vue'
Many developers are familiar with the likes of [Carbon](https://carbon.now.sh/) and [Codeimg](https://codeimg.io/), which allow you to generate images of your code. They are extremely useful projects, but they are also closed source, don't have APIs, and are not self-hostable. Inkify is an open source alternative that aims to solve these problems.
Inkify is written in Rust and makes use of a couple of other open source projects, including [Silicon](https://github.com/Aloxaf/silicon) for rendering the code images, [guesslang](https://github.com/yoeo/guesslang) for detecting the programming language, and [Actix Web](https://github.com/actix/actix-web) for the server. Without these projects, Inkify would not be possible. Silicon is really the backbone and inspiration for the whole project, but since it's mostly made to be a CLI tool it took some work to make it into a server.
## The API
Inkify has a very simple to use JSON based API, which is entirely documented with a GET request to the server root. You can see it yourself [here](https://inkify.0x45.st), where I host the official instance. The most important route is `/generate`, which takes a JSON body and returns a PNG image, though there are several other routes available including `/detect` which does some language detection on the provided code.
## Self-hosting
I host pretty much everything I build with Docker, and Inkify is no exception. As such there is a ready-to-go Docker container available at [https://hub.docker.com/r/watzon/inkify](https://hub.docker.com/r/watzon/inkify). You can also, of course, build it youself with the provided Dockerfile; be warned, the build process takes a while as it has to compile Tensorflow, download fonts, and build the Rust project.
## Testing
Want to give Inkify a try without writing your own code? Here's a simple component that make use of the Inkify API:
<div class="mb-8">
<CodeToImg client:load />
</div>
## Conclusion
Inkify was inspired by my wanting to generate open graph images for my pastebin, [paste69](https://0x45.st), and I'm very happy with how it turned out. It's a simple project, but it's also very useful and I hope others find it useful as well. Be sure to give the project a [star on GitHub](https://github.com/watzon/inkify) if you like it, and feel free to open an issue if you have any questions or suggestions.

View File

@ -10,4 +10,5 @@ tags:
- webdriver - webdriver
linkOnly: true linkOnly: true
featured: true featured: true
heroImage: /images/marionette-header.webp
--- ---

View File

@ -10,5 +10,4 @@ tags:
- framework - framework
heroImage: /images/tourmaline-header.png heroImage: /images/tourmaline-header.png
linkOnly: true linkOnly: true
featured: true
--- ---

View File

@ -13,11 +13,11 @@ const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
<html lang="en"> <html lang="en">
<head> <head>
<BaseHead title={`${title} - ${SITE_TITLE}`} description={description} /> <BaseHead title={`${title} - ${SITE_TITLE}`} description={description} image={heroImage} />
<style> <style>
main { main {
width: calc(100% - 2em); width: calc(100% - 2em);
@apply max-w-full m-0; @apply max-w-full;
} }
.hero-image { .hero-image {
@apply w-full; @apply w-full;

View File

@ -1,4 +1,5 @@
--- ---
import { Icon } from 'astro-icon/components';
import type { CollectionEntry } from 'astro:content'; import type { CollectionEntry } from 'astro:content';
import BaseHead from '../components/BaseHead.astro'; import BaseHead from '../components/BaseHead.astro';
import Header from '../components/Header.astro'; import Header from '../components/Header.astro';
@ -7,16 +8,16 @@ import { SITE_TITLE } from '../consts';
type Props = CollectionEntry<'projects'>['data']; type Props = CollectionEntry<'projects'>['data'];
const { name, description, heroImage } = Astro.props; const { name, description, heroImage, url } = Astro.props;
--- ---
<html lang="en"> <html lang="en">
<head> <head>
<BaseHead title={`${name} - ${SITE_TITLE}`} description={description} /> <BaseHead title={`${name} - ${SITE_TITLE}`} description={description} image={heroImage} />
<style> <style>
main { main {
width: calc(100% - 2em); width: calc(100% - 2em);
@apply max-w-full m-0; @apply max-w-full;
} }
.hero-image { .hero-image {
@apply w-full; @apply w-full;
@ -33,7 +34,13 @@ const { name, description, heroImage } = Astro.props;
@apply mb-4 py-4 text-center leading-8; @apply mb-4 py-4 text-center leading-8;
} }
.title h1 { .title h1 {
@apply m-0 mb-4; @apply flex justify-center m-0 mb-4;
}
.title h1 a {
@apply block relative w-min text-gray-900 dark:text-gray-100 hover:text-gray-700 dark:hover:text-gray-300;
}
.title h1 svg {
@apply absolute left-0 top-1/2 transform -translate-y-1/2 -translate-x-[110%] w-12 h-12;
} }
.date { .date {
@apply mb-4 text-base text-gray-500; @apply mb-4 text-base text-gray-500;
@ -53,7 +60,13 @@ const { name, description, heroImage } = Astro.props;
</div> </div>
<div class="prose"> <div class="prose">
<div class="title"> <div class="title">
<h1>{name}</h1> <h1>
<a target="_blank" href={url}>
<Icon name="mdi:link" />
{name}
</a>
</h1>
<h5>{description}</h5>
<hr /> <hr />
</div> </div>
<slot /> <slot />

View File

@ -63,9 +63,21 @@ const latestPosts = (await getCollection('posts'))
</html> </html>
<style lang="postcss"> <style lang="postcss">
.header { :root {
/* Transitioning rainbow gradient */ --gradient-colors-light: #b36b6b, #d99e85, #e6d9a3, #7fbf8e, #7fbfbf, #7f7fbf, #b37fbf, #b36b6b, #b36b6b;
background: linear-gradient(90deg, #ffb3b3, #ffd9b3, #ffffb3, #b3ffcc, #b3ffff, #b3b3ff, #ffb3ff, #ffb3b3, #ffb3b3); --gradient-colors-dark: #ffb3b3, #ffd9b3, #ffffb3, #b3ffcc, #b3ffff, #b3b3ff, #ffb3ff, #ffb3b3, #ffb3b3;
}
:global(.dark) .header {
background: linear-gradient(90deg, var(--gradient-colors-dark));
background-size: 400% 400%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradient 15s linear infinite;
}
:global(:not(.dark)) .header {
background: linear-gradient(90deg, var(--gradient-colors-light));
background-size: 400% 400%; background-size: 400% 400%;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;

View File

@ -18,7 +18,7 @@ body {
} }
main { main {
@apply w-[720px] max-w-[calc(100%-2em)] m-auto px-4 py-12; @apply w-[720px] max-w-[calc(100%-2em)] m-0 mx-auto px-4 py-12;
} }
h1, h1,
@ -31,19 +31,19 @@ h6 {
@apply m-0 mb-6 text-4xl font-bold leading-10 text-gray-900 dark:text-gray-100; @apply m-0 mb-6 text-4xl font-bold leading-10 text-gray-900 dark:text-gray-100;
} }
h1 { h1 {
@apply text-6xl @apply text-5xl md:text-6xl;
} }
h2 { h2 {
@apply text-5xl @apply text-4xl md:text-5xl;
} }
h3 { h3 {
@apply text-4xl @apply text-3xl md:text-4xl;
} }
h4 { h4 {
@apply text-3xl @apply text-2xl md:text-3xl;
} }
h5 { h5 {
@apply text-2xl @apply text-xl md:text-2xl;
} }
strong, strong,
@ -93,7 +93,7 @@ pre {
} }
pre > code { pre > code {
all: unset; all: unset !important;
} }
blockquote { blockquote {
@ -109,7 +109,7 @@ hr {
@apply text-lg; @apply text-lg;
} }
main { main {
@apply p-4; @apply px-0;
} }
} }

View File

@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
darkMode: 'class',
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: { theme: {
extend: {}, extend: {},

View File

@ -1,6 +1,7 @@
{ {
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/strict",
"compilerOptions": { "compilerOptions": {
"strictNullChecks": true "strictNullChecks": true,
"jsx": "preserve"
} }
} }