first commit

master
Facinorous 1 year ago
commit 6788948d62

@ -0,0 +1,18 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{yaml,yml,md}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false

@ -0,0 +1,37 @@
name: Github Pages Astro CI
on:
schedule:
- cron: '20 0 * * *'
# Trigger the workflow every time you push to the `main` branch
# Using a different branch name? Replace `main` with your branchs name
push:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab on GitHub.
workflow_dispatch:
# Allow this job to clone the repo and create a page deployment
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout your repository using git
uses: actions/checkout@v2
- name: Install, build, and upload your site
uses: withastro/action@v0
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

19
.gitignore vendored

@ -0,0 +1,19 @@
# build output
dist/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

@ -0,0 +1,4 @@
{
"tabWidth": 4,
"printWidth": 120
}

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

@ -0,0 +1,5 @@
{
"files.exclude": {
"**/node_modules": true
}
}

@ -0,0 +1 @@
gethomepage.dev

@ -0,0 +1,17 @@
import { defineConfig } from "astro/config";
import preact from "@astrojs/preact";
import react from "@astrojs/react";
// https://astro.build/config
import mdx from "@astrojs/mdx";
// https://astro.build/config
export default defineConfig({
integrations: [
// Enable Preact to support Preact JSX components.
preact(),
// Enable React for the Algolia search component.
react(), mdx()],
site: "https://Facinorous-420.github.io",
base: "/dick-docs"
});

@ -0,0 +1,36 @@
{
"name": "@example/docs",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"check": "astro check && tsc",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@algolia/client-search": "^4.13.1",
"@astrojs/mdx": "^0.11.4",
"@astrojs/preact": "^1.2.0",
"@astrojs/react": "^1.2.0",
"@astrojs/tailwind": "^2.0.2",
"@docsearch/css": "^3.1.0",
"@docsearch/react": "^3.1.0",
"@torchlight-api/torchlight-cli": "^0.1.7",
"@types/node": "^18.0.0",
"@types/react": "^17.0.45",
"@types/react-dom": "^18.0.0",
"astro": "^1.4.7",
"hast-util-from-parse5": "^7.1.0",
"marked": "^4.1.1",
"parse5": "^7.1.1",
"preact": "^10.7.3",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"tailwindcss": "^3.0.24",
"unist-util-map": "^3.1.2"
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#2b5797</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

@ -0,0 +1,3 @@
Array.from(document.getElementsByTagName("pre")).forEach((element) => {
element.setAttribute("tabindex", "0");
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="461.000000pt" height="461.000000pt" viewBox="0 0 461.000000 461.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,461.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2139 4599 c-721 -51 -1369 -432 -1769 -1039 -82 -125 -200 -366
-249 -512 -130 -382 -157 -814 -76 -1213 121 -592 489 -1129 1005 -1465 218
-141 525 -270 765 -318 197 -41 257 -46 490 -46 192 0 251 4 355 22 174 31
328 73 465 126 781 306 1321 982 1451 1816 25 159 25 509 0 670 -116 749 -579
1386 -1252 1723 -275 137 -560 214 -879 237 -152 11 -150 11 -306 -1z m1277
-850 c60 -30 126 -94 155 -150 44 -85 24 -204 -53 -319 -36 -55 -39 -63 -29
-92 6 -17 11 -40 11 -52 0 -14 12 -28 38 -41 20 -11 51 -41 68 -66 64 -96 69
-240 15 -410 -39 -121 -46 -173 -31 -228 6 -23 9 -46 6 -49 -12 -11 -88 33
-122 71 -20 22 -49 67 -65 102 -30 63 -79 238 -79 281 0 12 -4 25 -8 28 -10 6
-125 -189 -143 -241 -6 -18 -15 -33 -19 -33 -12 0 -65 143 -81 215 -17 82 -15
238 5 300 9 28 16 53 16 56 0 4 -55 9 -122 12 -197 8 -341 59 -400 141 -41 56
-37 63 26 58 62 -5 125 13 229 65 87 43 165 63 248 63 123 0 207 -41 241 -117
10 -23 24 -43 31 -43 7 0 24 -6 39 -14 31 -17 33 -15 71 59 34 66 49 134 37
174 -20 71 -81 123 -168 145 -45 12 -47 14 -50 50 -5 60 2 69 47 63 22 -3 61
-16 87 -28z m-791 -582 c61 -41 188 -75 315 -84 l91 -6 -8 -46 c-14 -83 -7
-221 16 -311 25 -97 116 -295 131 -285 5 3 10 12 10 18 0 7 25 64 56 127 l56
115 27 -86 c15 -47 41 -111 58 -142 l31 -57 -81 -73 c-482 -430 -579 -543
-984 -1147 -114 -170 -222 -263 -378 -326 -108 -44 -195 -58 -329 -51 -81 3
-133 12 -191 31 -229 74 -417 261 -491 491 -38 117 -45 298 -15 415 67 268
261 473 521 550 331 98 533 187 630 276 32 30 101 105 154 167 86 101 143 172
298 375 27 34 51 62 56 62 4 0 16 -6 27 -13z"/>
<path d="M2997 3379 c-21 -5 -89 -31 -151 -59 -62 -27 -119 -50 -126 -50 -9 0
-11 -3 -4 -10 21 -21 173 -51 294 -57 107 -5 128 -9 148 -26 27 -25 27 -44 2
-125 -23 -72 -25 -195 -6 -277 l11 -50 53 86 c54 90 64 99 111 99 43 0 62 -30
77 -122 17 -97 46 -193 78 -257 l23 -45 8 34 c4 19 20 77 36 128 34 108 40
237 13 300 -17 42 -61 92 -81 92 -25 0 -53 39 -53 75 0 46 -48 101 -101 115
-39 11 -69 41 -69 70 0 24 -40 57 -89 75 -47 16 -114 18 -174 4z"/>
<path d="M2309 2668 c-193 -225 -248 -272 -401 -342 -95 -43 -248 -98 -368
-131 -118 -33 -216 -84 -286 -148 -45 -40 -54 -55 -54 -82 0 -24 7 -39 26 -54
40 -31 67 -26 126 25 61 52 149 96 228 114 160 37 435 152 559 234 113 76 341
334 341 386 0 41 -32 70 -75 70 -30 0 -41 -9 -96 -72z"/>
<path d="M1103 1810 c-28 -11 -40 -34 -59 -116 -18 -77 -9 -114 32 -133 60
-27 89 -1 115 104 22 90 16 124 -27 144 -29 13 -31 13 -61 1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

@ -0,0 +1,173 @@
# Astro Starter Kit: Docs Site
```bash
npm create astro@latest -- --template docs
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/docs)
![docs](https://user-images.githubusercontent.com/4677417/186189283-0831b9ab-d6b9-485d-8955-3057e532ab31.png)
## Features
- ✅ **Full Markdown support**
- ✅ **Responsive mobile-friendly design**
- ✅ **Sidebar navigation**
- ✅ **Search (powered by Algolia)**
- ✅ **Multi-language i18n**
- ✅ **Automatic table of contents**
- ✅ **Automatic list of contributors**
- ✅ (and, best of all) **dark mode**
## Commands Cheatsheet
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro --help` | Get help using the Astro CLI |
To deploy your site to production, check out our [Deploy an Astro Website](https://docs.astro.build/guides/deploy) guide.
## New to Astro?
Welcome! Check out [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
## Customize This Theme
### Site metadata
`src/config.ts` contains several data objects that describe metadata about your site like title, description, default language, and Open Graph details. You can customize these to match your project.
### CSS styling
The theme's look and feel is controlled by a few key variables that you can customize yourself. You'll find them in the `src/styles/theme.css` CSS file.
If you've never worked with CSS variables before, give [MDN's guide on CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) a quick read.
This theme uses a "cool blue" accent color by default. To customize this for your project, change the `--theme-accent` variable to whatever color you'd like:
```diff
/* src/styles/theme.css */
:root {
color-scheme: light;
- --theme-accent: hsla(var(--color-blue), 1);
+ --theme-accent: hsla(var(--color-red), 1); /* or: hsla(#FF0000, 1); */
```
## Page metadata
Astro uses frontmatter in Markdown pages to choose layouts and pass properties to those layouts. If you are using the default layout, you can customize the page in many different ways to optimize SEO and other things. For example, you can use the `title` and `description` properties to set the document title, meta title, meta description, and Open Graph description.
```markdown
---
title: Example title
description: Really cool docs example that uses Astro
layout: ../../layouts/MainLayout.astro
---
# Page content...
```
For more SEO related properties, look at `src/components/HeadSEO.astro`
### Sidebar navigation
The sidebar navigation is controlled by the `SIDEBAR` variable in your `src/config.ts` file. You can customize the sidebar by modifying this object. A default, starter navigation has already been created for you.
```ts
export const SIDEBAR = {
en: [
{ text: "Section Header", header: true },
{ text: "Introduction", link: "en/introduction" },
{ text: "Page 2", link: "en/page-2" },
{ text: "Page 3", link: "en/page-3" },
{ text: "Another Section", header: true },
{ text: "Page 4", link: "en/page-4" },
],
};
```
Note the top-level `en` key: This is needed for multi-language support. You can change it to whatever language you'd like, or add new languages as you go. More details on this below.
### Multiple Languages support
The Astro docs template supports multiple langauges out of the box. The default theme only shows `en` documentation, but you can enable multi-language support features by adding a second language to your project.
To add a new language to your project, you'll want to extend the current `src/pages/[lang]/...` layout:
```diff
📂 src/pages
┣ 📂 en
┃ ┣ 📜 page-1.md
┃ ┣ 📜 page-2.md
┃ ┣ 📜 page-3.astro
+ ┣ 📂 es
+ ┃ ┣ 📜 page-1.md
+ ┃ ┣ 📜 page-2.md
+ ┃ ┣ 📜 page-3.astro
```
You'll also need to add the new language name to the `KNOWN_LANGUAGES` map in your `src/config.ts` file. This will enable your new language switcher in the site header.
```diff
// src/config.ts
export const KNOWN_LANGUAGES = {
English: 'en',
+ Spanish: 'es',
};
```
Last step: you'll need to add a new entry to your sidebar, to create the table of contents for that language. While duplicating every page might not sound ideal to everyone, this extra control allows you to create entirely custom content for every language.
> Make sure the sidebar `link` value points to the correct language!
```diff
// src/config.ts
export const SIDEBAR = {
en: [
{ text: 'Section Header', header: true, },
{ text: 'Introduction', link: 'en/introduction' },
// ...
],
+ es: [
+ { text: 'Encabezado de sección', header: true, },
+ { text: 'Introducción', link: 'es/introduction' },
+ // ...
+ ],
};
// ...
```
If you plan to use Spanish as the the default language, you just need to modify the redirect path in `src/pages/index.astro`:
```diff
<script>
- window.location.pathname = `/en/introduction`;
+ window.location.pathname = `/es/introduction`;
</script>
```
You can also remove the above script and write a landing page in Spanish instead.
### What if I don't plan to support multiple languages?
That's totally fine! Not all projects need (or can support) multiple languages. You can continue to use this theme without ever adding a second language.
If that single language is not English, you can just replace `en` in directory layouts and configurations with the preferred language.
### Search (Powered by Algolia)
[Algolia](https://www.algolia.com/) offers a free service to qualified open source projects called [DocSearch](https://docsearch.algolia.com/). If you are accepted to the DocSearch program, provide your API Key & index name in `src/config.ts` and a search box will automatically appear in your site header.
Note that Aglolia and Astro are not affiliated. We have no say over acceptance to the DocSearch program.
If you'd prefer to remove Algolia's search and replace it with your own, check out the `src/components/Header.astro` component to see where the component is added.

@ -0,0 +1,32 @@
---
import { marked } from 'marked';
const releasesResponse = await fetch('https://api.github.com/repos/Facinorous-420/dick/releases');
console.log(releasesResponse)
const releases = await releasesResponse.json();
---
<ul>
{releases.map((release) => (
<li>
<h2 id={release.id}><a href={release.html_url}>{release.name}</a> <small>({new Date(release.created_at).toLocaleDateString()})</small></h2>
<div set:html={marked(release.body)}></div>
</li>
))}
</ul>
<style>
ul {
list-style: none;
padding: 0;
}
li {
margin: 0 0 3em;
}
li div {
margin-left: 1rem;
}
li div :global(h2) {
font-size: 1.3rem;
}
</style>

@ -0,0 +1,53 @@
---
import { SIDEBAR } from "../../config";
type Props = {
section: string;
language: string;
};
const { section, language } = Astro.props as Props;
---
<ul>
{
SIDEBAR[language || "en"][section].map((item) => (
<li>
{item.links ? (
<strong>{item.text}</strong>
<ul>
{item.links.map((link) => (
<li>
<a href={Astro.site?.pathname + link.link}>{link.text}</a>
</li>
))}
</ul>
) : (
<a href={Astro.site?.pathname + (item.link ?? item.url)}>{item.text ?? item.title}</a>
)}
</li>
))
}
</ul>
<style>
ul {
list-style: none;
padding: 0;
}
li {
margin: 0;
}
a {
color: var(--color-text);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
details {
margin: 0;
}
summary {
cursor: pointer;
}
</style>

@ -0,0 +1,182 @@
---
// fetch all commits for just this page's path
type Props = {
path: string;
};
const { path } = Astro.props as Props;
const resolvedPath = `${path}`;
const url = `https://api.github.com/repos/benphelps/homepage-docs/commits?path=${resolvedPath}`;
const commitsURL = `https://api.github.com/repos/benphelps/homepage-docs/main/${resolvedPath}`;
type Commit = {
author: {
id: string;
login: string;
};
};
async function getCommits(url: string) {
try {
const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN ?? "hello";
if (!token) {
throw new Error(
'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'
);
}
const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`;
const res = await fetch(url, {
method: "GET",
headers: {
Authorization: auth,
"User-Agent": "astro-docs/1.0",
},
});
const data = await res.json();
if (!res.ok) {
throw new Error(
`Request to fetch commits failed. Reason: ${res.statusText}
Message: ${data.message}`
);
}
return data as Commit[];
} catch (e) {
console.warn(`[error] /src/components/AvatarList.astro
${(e as any)?.message ?? e}`);
return [] as Commit[];
}
}
function removeDups(arr: Commit[]) {
const map = new Map<string, Commit["author"]>();
for (let item of arr) {
const author = item.author;
// Deduplicate based on author.id
map.set(author.id, { login: author.login, id: author.id });
}
return [...map.values()];
}
const data = await getCommits(url);
const unique = removeDups(data);
const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors
const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors
---
<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
<div class="contributors">
<ul
class="avatar-list"
style={`--avatar-count: ${recentContributors.length}`}
>
{
recentContributors.map((item) => (
<li>
<a href={`https://github.com/${item.login}`}>
<img
alt={`Contributor ${item.login}`}
title={`Contributor ${item.login}`}
width="64"
height="64"
src={`https://avatars.githubusercontent.com/u/${item.id}`}
/>
</a>
</li>
))
}
</ul>
{
additionalContributors > 0 && (
<span>
<a
href={commitsURL}
>{`and ${additionalContributors} additional contributor${
additionalContributors > 1 ? "s" : ""
}.`}</a>
</span>
)
}
{unique.length === 0 && <a href={commitsURL}>Contributors</a>}
</div>
<style>
.avatar-list {
--avatar-size: 2.5rem;
--avatar-count: 3;
display: grid;
list-style: none;
/* Default to displaying most of the avatar to
enable easier access on touch devices, ensuring
the WCAG touch target size is met or exceeded */
grid-template-columns: repeat(
var(--avatar-count),
max(44px, calc(var(--avatar-size) / 1.15))
);
/* `padding` matches added visual dimensions of
the `box-shadow` to help create a more accurate
computed component size */
padding: 0.08em;
font-size: var(--avatar-size);
}
@media (any-hover: hover) and (any-pointer: fine) {
.avatar-list {
/* We create 1 extra cell to enable the computed
width to match the final visual width */
grid-template-columns: repeat(
calc(var(--avatar-count) + 1),
calc(var(--avatar-size) / 1.75)
);
}
}
.avatar-list li {
width: var(--avatar-size);
height: var(--avatar-size);
}
.avatar-list li:hover ~ li a,
.avatar-list li:focus-within ~ li a {
transform: translateX(33%);
}
.avatar-list img,
.avatar-list a {
display: block;
border-radius: 50%;
}
.avatar-list a {
transition: transform 180ms ease-in-out;
}
.avatar-list img {
width: 100%;
height: 100%;
object-fit: cover;
background-color: #fff;
box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
}
.avatar-list a:focus {
outline: 2px solid transparent;
/* Double-layer trick to work for dark and light backgrounds */
box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
}
.contributors {
display: flex;
align-items: center;
}
.contributors > * + * {
margin-left: 0.75rem;
}
</style>

@ -0,0 +1,19 @@
---
import AvatarList from "./AvatarList.astro";
type Props = {
path: string;
};
const { path } = Astro.props as Props;
---
<footer>
<AvatarList path={path} />
</footer>
<style>
footer {
margin-top: auto;
padding: 2rem;
border-top: 3px solid var(--theme-divider);
}
</style>

@ -0,0 +1,51 @@
---
import "../styles/theme.css";
import "../styles/index.css";
---
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="sitemap" href="/sitemap.xml" />
<!-- Preload Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Manrope:wght@200;300;400;600;800&display=swap"
rel="stylesheet"
/>
<!-- Scrollable a11y code helper -->
<script src="/make-scrollable-code-focusable.js" is:inline></script>
<!-- This is intentionally inlined to avoid FOUC -->
<script is:inline>
const root = document.documentElement;
const theme = localStorage.getItem("theme");
if (
theme === "dark" ||
(!theme && window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEL60V1WM9" is:inline></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-TEL60V1WM9');
</script> -->

@ -0,0 +1,48 @@
---
import { SITE, OPEN_GRAPH, Frontmatter } from "../config";
export interface Props {
frontmatter: Frontmatter;
canonicalUrl: URL;
}
const { frontmatter, canonicalUrl } = Astro.props as Props;
const formattedContentTitle = `${frontmatter.title} - ${SITE.title}`;
const imageSrc = frontmatter.image?.src ?? OPEN_GRAPH.image.src;
const canonicalImageSrc = new URL(imageSrc, Astro.site);
const imageAlt = frontmatter.image?.alt ?? OPEN_GRAPH.image.alt;
---
<!-- Page Metadata -->
<link rel="canonical" href={canonicalUrl} />
<!-- OpenGraph Tags -->
<meta property="og:title" content={formattedContentTitle} />
<meta property="og:type" content="article" />
<meta property="og:url" content={canonicalUrl} />
<meta
property="og:locale"
content={frontmatter.ogLocale ?? SITE.defaultLanguage}
/>
<meta property="og:image" content={canonicalImageSrc} />
<meta property="og:image:alt" content={imageAlt} />
<meta
name="description"
property="og:description"
content={frontmatter.description ?? SITE.description}
/>
<meta property="og:site_name" content={SITE.title} />
<!-- Icons -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#1e9cd7" />
<!--
TODO: Add json+ld data, maybe https://schema.org/APIReference makes sense?
Docs: https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data
https://www.npmjs.com/package/schema-dts seems like a great resource for implementing this.
Even better, there's a React component that integrates with `schema-dts`: https://github.com/google/react-schemaorg
-->

File diff suppressed because one or more lines are too long

@ -0,0 +1,157 @@
---
import { getLanguageFromURL, KNOWN_LANGUAGE_CODES } from "../../languages";
import * as CONFIG from "../../config";
import ThemeToggleButton from "./ThemeToggleButton";
import AstroLogo from "./AstroLogo.astro";
import SkipToContent from "./SkipToContent.astro";
import SidebarToggle from "./SidebarToggle";
import LanguageSelect from "./LanguageSelect";
import Search from "./Search";
type Props = {
currentPage: string;
};
const { currentPage } = Astro.props as Props;
const lang = getLanguageFromURL(currentPage);
---
<header>
<SkipToContent />
<nav class="nav-wrapper" title="Top Navigation">
<div class="menu-toggle">
<SidebarToggle client:idle />
</div>
<div class="logo flex">
<a href="/">
<AstroLogo size={40} />
<h1>{CONFIG.SITE.title ?? "Documentation"}</h1>
</a>
</div>
<div style="flex-grow: 1;"></div>
{KNOWN_LANGUAGE_CODES.length > 1 && <LanguageSelect lang={lang} client:idle />}
<div class="search-item">
<Search client:idle />
</div>
<div style="text-align: right;">
<ThemeToggleButton client:visible />
</div>
</nav>
</header>
<style>
header {
z-index: 11;
height: var(--theme-navbar-height);
width: 100%;
background-color: var(--theme-navbar-bg);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: sticky;
top: 0;
}
.logo {
flex: 1;
display: flex;
overflow: hidden;
width: 30px;
font-size: 2rem;
flex-shrink: 0;
font-weight: 600;
line-height: 1;
color: hsla(var(--color-base-white), 100%, 1);
gap: 0.25em;
z-index: -1;
}
.logo a {
display: flex;
padding: 0.5em 0.25em;
margin: -0.5em -0.25em;
text-decoration: none;
font-weight: bold;
gap: 0;
}
.logo a {
transition: color 100ms ease-out;
color: var(--theme-text);
}
.logo a:hover,
.logo a:focus {
color: var(--theme-text-accent);
}
.logo h1 {
display: none;
font: inherit;
color: inherit;
margin: 0;
font-weight: 200;
text-transform: lowercase;
}
.nav-wrapper {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 1rem;
width: 100%;
max-width: 86em;
padding: 1rem;
}
@media (min-width: 50em) {
header {
position: static;
padding: 2rem 0rem;
}
.logo {
width: auto;
margin: 0;
z-index: 0;
}
.logo h1 {
display: initial;
}
.menu-toggle {
display: none;
}
}
/** Style Algolia */
:root {
--docsearch-primary-color: var(--theme-accent);
--docsearch-logo-color: var(--theme-text);
}
.search-item {
display: none;
position: relative;
z-index: 10;
flex-grow: 1;
padding-right: 0.7rem;
display: flex;
max-width: 200px;
}
@media (min-width: 50em) {
.search-item {
max-width: 400px;
}
}
</style>
<style is:global>
.search-item > * {
flex-grow: 1;
}
</style>

@ -0,0 +1,47 @@
.language-select {
flex-grow: 1;
width: 48px;
box-sizing: border-box;
margin: 0;
padding: 0.33em 0.5em;
overflow: visible;
font-weight: 500;
font-size: 1rem;
font-family: inherit;
line-height: inherit;
background-color: var(--theme-bg);
border-color: var(--theme-text-lighter);
color: var(--theme-text-light);
border-style: solid;
border-width: 1px;
border-radius: 0.25rem;
outline: 0;
cursor: pointer;
transition-timing-function: ease-out;
transition-duration: 0.2s;
transition-property: border-color, color;
-webkit-font-smoothing: antialiased;
padding-left: 30px;
padding-right: 1rem;
}
.language-select-wrapper .language-select:hover,
.language-select-wrapper .language-select:focus {
color: var(--theme-text);
border-color: var(--theme-text-light);
}
.language-select-wrapper {
color: var(--theme-text-light);
position: relative;
}
.language-select-wrapper > svg {
position: absolute;
top: 7px;
left: 10px;
pointer-events: none;
}
@media (min-width: 50em) {
.language-select {
width: 100%;
}
}

@ -0,0 +1,49 @@
/** @jsxImportSource react */
import type { FunctionComponent } from 'react';
import './LanguageSelect.css';
import { KNOWN_LANGUAGES, langPathRegex } from '../../languages';
const LanguageSelect: FunctionComponent<{ lang: string }> = ({ lang }) => {
return (
<div className="language-select-wrapper">
<svg
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 88.6 77.3"
height="1.2em"
width="1.2em"
>
<path
fill="currentColor"
d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z"
/>
<path
fill="currentColor"
d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"
/>
</svg>
<select
className="language-select"
value={lang}
onChange={(e) => {
const newLang = e.target.value;
let actualDest = window.location.pathname.replace(langPathRegex, '/');
if (actualDest == '/') actualDest = `/introduction`;
window.location.pathname = '/' + newLang + actualDest;
}}
>
{Object.entries(KNOWN_LANGUAGES).map(([key, value]) => {
return (
<option value={value} key={value}>
<span>{key}</span>
</option>
);
})}
</select>
</div>
);
};
export default LanguageSelect;

@ -0,0 +1,76 @@
/** Style Algolia */
:root {
--docsearch-primary-color: var(--theme-accent);
--docsearch-logo-color: var(--theme-text);
}
.search-input {
flex-grow: 1;
box-sizing: border-box;
width: 100%;
margin: 0;
padding: 0.33em 0.5em;
overflow: visible;
font-weight: 500;
font-size: 1rem;
font-family: inherit;
line-height: inherit;
background-color: var(--theme-divider);
border-color: var(--theme-divider);
color: var(--theme-text-light);
border-style: solid;
border-width: 1px;
border-radius: 0.25rem;
outline: 0;
cursor: pointer;
transition-timing-function: ease-out;
transition-duration: 0.2s;
transition-property: border-color, color;
-webkit-font-smoothing: antialiased;
}
.search-input:hover,
.search-input:focus {
color: var(--theme-text);
border-color: var(--theme-text-light);
}
.search-input:hover::placeholder,
.search-input:focus::placeholder {
color: var(--theme-text-light);
}
.search-input::placeholder {
color: var(--theme-text-light);
}
.search-hint {
position: absolute;
top: 7px;
right: 19px;
padding: 3px 5px;
display: none;
display: none;
align-items: center;
justify-content: center;
letter-spacing: 0.125em;
font-size: 13px;
font-family: var(--font-mono);
pointer-events: none;
border-color: var(--theme-text-lighter);
color: var(--theme-text-light);
border-style: solid;
border-width: 1px;
border-radius: 0.25rem;
line-height: 14px;
}
@media (min-width: 50em) {
.search-hint {
display: flex;
}
}
/* ------------------------------------------------------------ *\
DocSearch (Algolia)
\* ------------------------------------------------------------ */
.DocSearch-Modal .DocSearch-Hit a {
box-shadow: none;
border: 1px solid var(--theme-accent);
}

@ -0,0 +1,97 @@
/** @jsxImportSource react */
import { useState, useCallback, useRef } from 'react';
import { ALGOLIA } from '../../config';
import '@docsearch/css';
import './Search.css';
import { createPortal } from 'react-dom';
import * as docSearchReact from '@docsearch/react';
/** FIXME: This is still kinda nasty, but DocSearch is not ESM ready. */
const DocSearchModal =
docSearchReact.DocSearchModal || (docSearchReact as any).default.DocSearchModal;
const useDocSearchKeyboardEvents =
docSearchReact.useDocSearchKeyboardEvents ||
(docSearchReact as any).default.useDocSearchKeyboardEvents;
export default function Search() {
const [isOpen, setIsOpen] = useState(false);
const searchButtonRef = useRef<HTMLButtonElement>(null);
const [initialQuery, setInitialQuery] = useState('');
const onOpen = useCallback(() => {
setIsOpen(true);
}, [setIsOpen]);
const onClose = useCallback(() => {
setIsOpen(false);
}, [setIsOpen]);
const onInput = useCallback(
(e) => {
setIsOpen(true);
setInitialQuery(e.key);
},
[setIsOpen, setInitialQuery]
);
useDocSearchKeyboardEvents({
isOpen,
onOpen,
onClose,
onInput,
searchButtonRef,
});
return (
<>
<button type="button" ref={searchButtonRef} onClick={onOpen} className="search-input">
<svg width="24" height="24" fill="none">
<path
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<span>Search</span>
<span className="search-hint">
<span className="sr-only">Press </span>
<kbd>/</kbd>
<span className="sr-only"> to search</span>
</span>
</button>
{isOpen &&
createPortal(
<DocSearchModal
initialQuery={initialQuery}
initialScrollY={window.scrollY}
onClose={onClose}
indexName={ALGOLIA.indexName}
appId={ALGOLIA.appId}
apiKey={ALGOLIA.apiKey}
transformItems={(items) => {
return items.map((item) => {
// We transform the absolute URL into a relative URL to
// work better on localhost, preview URLS.
const a = document.createElement('a');
a.href = item.url;
const hash = a.hash === '#overview' ? '' : a.hash;
return {
...item,
url: `${a.pathname}${hash}`,
};
});
}}
/>,
document.body
)}
</>
);
}

@ -0,0 +1,44 @@
/** @jsxImportSource preact */
import type { FunctionalComponent } from 'preact';
import { useState, useEffect } from 'preact/hooks';
const MenuToggle: FunctionalComponent = () => {
const [sidebarShown, setSidebarShown] = useState(false);
useEffect(() => {
const body = document.querySelector('body')!;
if (sidebarShown) {
body.classList.add('mobile-sidebar-toggle');
} else {
body.classList.remove('mobile-sidebar-toggle');
}
}, [sidebarShown]);
return (
<button
type="button"
aria-pressed={sidebarShown ? 'true' : 'false'}
id="menu-toggle"
onClick={() => setSidebarShown(!sidebarShown)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<span className="sr-only">Toggle sidebar</span>
</button>
);
};
export default MenuToggle;

@ -0,0 +1,28 @@
---
type Props = {};
---
<a href="#article" class="sr-only focus:not-sr-only skiplink"
><span>Skip to Content</span>
</a>
<style>
.skiplink,
.skiplink:focus,
.skiplink:focus-visible {
position: absolute;
padding: 0.25em;
font-size: larger;
top: 0;
left: 0;
right: 0;
z-index: 9;
display: block;
text-align: center;
background-color: var(--theme-text-accent);
color: var(--theme-bg);
border-radius: 0.25em;
outline: var(--theme-bg) solid 1px;
outline-offset: 0;
}
</style>

@ -0,0 +1,37 @@
.theme-toggle {
display: inline-flex;
align-items: center;
gap: 0.25em;
padding: 0.33em 0.67em;
border-radius: 99em;
background-color: var(--theme-code-inline-bg);
}
.theme-toggle > label:focus-within {
outline: 2px solid transparent;
box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
}
.theme-toggle > label {
color: var(--theme-code-inline-text);
position: relative;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.5;
}
.theme-toggle .checked {
color: var(--theme-accent);
opacity: 1;
}
input[name="theme-toggle"] {
position: absolute;
opacity: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
}

@ -0,0 +1,82 @@
import type { FunctionalComponent } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import './ThemeToggleButton.css';
const themes = ['light', 'dark'];
const icons = [
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clipRule="evenodd"
/>
</svg>,
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>,
];
const ThemeToggle: FunctionalComponent = () => {
const [theme, setTheme] = useState(() => {
if (import.meta.env.SSR) {
return undefined;
}
if (typeof localStorage !== undefined && localStorage.getItem('theme')) {
return localStorage.getItem('theme');
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
});
useEffect(() => {
const root = document.documentElement;
if (theme === 'light') {
root.classList.remove('theme-dark');
} else {
root.classList.add('theme-dark');
}
}, [theme]);
return (
<div className="theme-toggle">
{themes.map((t, i) => {
const icon = icons[i];
const checked = t === theme;
return (
<label className={checked ? ' checked' : ''}>
{icon}
<input
type="radio"
name="theme-toggle"
checked={checked}
value={t}
title={`Use ${t} theme`}
aria-label={`Use ${t} theme`}
onChange={() => {
localStorage.setItem('theme', t);
setTheme(t);
}}
/>
</label>
);
})}
</div>
);
};
export default ThemeToggle;

@ -0,0 +1,227 @@
---
import { getLanguageFromURL } from "../../languages";
import { SIDEBAR } from "../../config";
import type { SidebarLink } from "../../config";
type Props = {
currentPage: string;
};
const { currentPage } = Astro.props as Props;
const currentPageMatch = currentPage.endsWith("/") ? currentPage.slice(1, -1) : currentPage.slice(1);
const langCode = getLanguageFromURL(currentPage);
const sidebar = SIDEBAR[langCode];
---
<nav aria-labelledby="grid-left">
<ul class="nav-groups">
{
Object.entries(sidebar).map(([header, children]) => (
<li>
<div class="nav-group">
<h2>{header}</h2>
<ul>
{children.map((child) => {
const url = Astro.site?.pathname + child.link;
if (child.links) {
return (
<>
<li class="nav-sub-group">
<details
open={
child.links.find((link) => link.link === currentPageMatch)
? true
: false
}
>
<summary class="nav-link title">{child.text}</summary>
<ul>
{child.links.map((child) => {
const url = Astro.site?.pathname + child.link;
return (
<li class="nav-sub-link">
<a
href={url}
aria-current={
currentPageMatch === child.link
? "page"
: false
}
>
{child.text}
</a>
</li>
);
})}
</ul>
</details>
</li>
</>
);
} else {
const url = Astro.site?.pathname + child.link;
return (
<li class="nav-link">
<a
href={url}
aria-current={currentPageMatch === child.link ? "page" : false}
>
{child.text}
</a>
</li>
);
}
})}
</ul>
</div>
</li>
))
}
</ul>
</nav>
<script is:inline>
window.addEventListener("DOMContentLoaded", () => {
var target = document.querySelector('[aria-current="page"]');
if (target && target.offsetTop > window.innerHeight - 100) {
document.querySelector(".nav-groups").scrollTop = target.offsetTop;
}
});
</script>
<style>
details {
position: relative;
}
details summary {
list-style: none;
cursor: pointer;
}
summary::-webkit-details-marker {
display: none;
}
summary::after {
position: absolute;
right: 1em;
content: " +";
}
details[open] summary:after {
content: " -";
}
nav {
width: auto;
margin-right: 0rem;
}
.nav-groups {
height: 100%;
padding: 2rem 0;
overflow-x: visible;
overflow-y: auto;
max-height: 100vh;
scrollbar-color: var(--theme-accent) var(--theme-bg-accent);
scrollbar-width: thin;
}
.nav-groups > li + li {
margin-top: 2rem;
}
.nav-groups > :first-child {
padding-top: var(--doc-padding);
}
.nav-groups > :last-child {
padding-bottom: 2rem;
margin-bottom: var(--theme-navbar-height);
}
.nav-group-title {
font-size: 1rem;
font-weight: 700;
padding: 0.1rem 1rem;
text-transform: uppercase;
margin-bottom: 0.5rem;
}
.nav-link a {
border-radius: 5px;
font-size: 1rem;
padding: 0.15rem 1rem;
margin: 0.15rem 0;
font: inherit;
color: inherit;
text-decoration: none;
display: block;
}
.nav-link a:hover,
.nav-link a:focus {
background-color: var(--theme-bg-hover);
}
.nav-link a[aria-current="page"] {
color: var(--theme-text-accent);
background-color: var(--theme-bg-accent);
font-weight: 600;
}
.nav-sub-link a {
border-radius: 5px;
font-size: 1rem;
padding: 0.15rem 1rem;
margin: 0;
margin-left: 0rem;
font: inherit;
color: inherit;
text-decoration: none;
display: block;
}
.nav-sub-link a:hover,
.nav-sub-link a:focus {
background-color: var(--theme-bg-hover);
}
.nav-sub-link a[aria-current="page"] {
color: var(--theme-text-accent);
background-color: var(--theme-bg-accent);
font-weight: 600;
}
.nav-link.title {
font-weight: 600;
padding: 0.15rem 0;
margin-left: 0.5rem;
}
@media (min-width: 50em) {
.nav-groups {
padding: 0;
}
nav {
margin-left: 1rem;
width: 100%;
}
}
@media (max-width: 50em) {
nav {
margin-left: 1rem;
}
}
</style>
<style is:global>
:root.theme-dark .nav-link a[aria-current="page"] {
color: hsla(var(--color-base-white), 100%, 1);
}
</style>

@ -0,0 +1,56 @@
---
import type { Frontmatter } from "../../config";
import MoreMenu from "../RightSidebar/MoreMenu.astro";
import TableOfContents from "../RightSidebar/TableOfContents";
import type { MarkdownHeading } from "astro";
type Props = {
frontmatter: Frontmatter;
headings: MarkdownHeading[];
githubEditUrl: string;
};
const { frontmatter, headings, githubEditUrl } = Astro.props as Props;
const title = frontmatter.title;
---
<article id="article" class="content">
<section class="main-section">
<h1 class="content-title" id="overview">{title}</h1>
<nav class="block sm:hidden">
<TableOfContents
client:media="(max-width: 50em)"
headings={headings}
/>
</nav>
<slot />
</section>
<nav class="block sm:hidden">
<MoreMenu editHref={githubEditUrl} />
</nav>
</article>
<style>
.content {
padding: 0;
max-width: 75ch;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.content > section {
margin-bottom: 4rem;
}
.block {
display: block;
}
@media (min-width: 50em) {
.sm\:hidden {
display: none;
}
}
</style>

@ -0,0 +1,100 @@
---
import * as CONFIG from "../../config";
type Props = {
editHref: string;
};
const { editHref } = Astro.props as Props;
const showMoreSection = CONFIG.COMMUNITY_INVITE_URL;
---
<ul>
{
editHref && (
<li class={`heading-link depth-2`}>
<a class="edit-on-github" href={editHref} target="_blank">
<svg
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="pen"
class="svg-inline--fa fa-pen fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
height="1em"
width="1em"
>
<path
fill="currentColor"
d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"
/>
</svg>
<span>Edit this page</span>
</a>
</li>
)
}
{
CONFIG.COMMUNITY_INVITE_URL && (
<li class={`heading-link depth-2`}>
<a href={CONFIG.COMMUNITY_INVITE_URL} target="_blank">
<svg
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="comment-alt"
class="svg-inline--fa fa-comment-alt fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
height="1em"
width="1em"
>
<path
fill="currentColor"
d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z"
/>
</svg>
<span>Join our community</span>
</a>
</li>
)
}
{
CONFIG.GITHUB_URL && (
<li class={`heading-link depth-2`}>
<a href={CONFIG.GITHUB_URL} target="_blank">
<svg
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="github"
class="svg-inline--fa fa-github fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
height="1em"
width="1em"
>
<path
fill="currentColor"
d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
/>
</svg>
<span>Homepage on GitHub</span>
</a>
</li>
)
}
</ul>
<style>
.edit-on-github {
text-decoration: none;
font: inherit;
color: inherit;
font-size: 1rem;
}
</style>

@ -0,0 +1,35 @@
---
import TableOfContents from "./TableOfContents";
import MoreMenu from "./MoreMenu.astro";
import type { MarkdownHeading } from "astro";
type Props = {
headings: MarkdownHeading[];
githubEditUrl: string;
};
const { headings, githubEditUrl } = Astro.props as Props;
---
<nav class="sidebar-nav" aria-labelledby="grid-right">
<div class="sidebar-nav-inner">
<TableOfContents client:media="(min-width: 50em)" headings={headings} />
<hr style="margin: 1rem;" />
<MoreMenu editHref={githubEditUrl} />
</div>
</nav>
<style>
.sidebar-nav {
width: 100%;
position: sticky;
top: 0;
}
.sidebar-nav-inner {
height: 100%;
padding: 0;
padding-top: var(--doc-padding);
overflow: auto;
}
</style>

@ -0,0 +1,56 @@
import type { FunctionalComponent } from 'preact';
import { useState, useEffect, useRef } from 'preact/hooks';
import type { MarkdownHeading } from 'astro';
type ItemOffsets = {
id: string;
topOffset: number;
};
const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({
headings = [],
}) => {
const itemOffsets = useRef<ItemOffsets[]>([]);
// FIXME: Not sure what this state is doing. It was never set to anything truthy.
const [activeId] = useState<string>('');
useEffect(() => {
const getItemOffsets = () => {
const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
itemOffsets.current = Array.from(titles).map((title) => ({
id: title.id,
topOffset: title.getBoundingClientRect().top + window.scrollY,
}));
};
getItemOffsets();
window.addEventListener('resize', getItemOffsets);
return () => {
window.removeEventListener('resize', getItemOffsets);
};
}, []);
return (
<>
<h2 className="heading">On this page</h2>
<ul>
<li className={`heading-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}>
<a href="#overview">Overview</a>
</li>
{headings
.filter(({ depth }) => depth > 1 && depth < 4)
.map((heading) => (
<li
className={`heading-link depth-${heading.depth} ${
activeId === heading.slug ? 'active' : ''
}`.trim()}
>
<a href={`#${heading.slug}`}>{heading.text}</a>
</li>
))}
</ul>
</>
);
};
export default TableOfContents;

@ -0,0 +1,78 @@
export const SITE = {
title: 'DICK',
description: 'Directly Integrated Client for Keisters',
defaultLanguage: 'en_US',
};
export const OPEN_GRAPH = {
image: {
src: 'https://raw.githubusercontent.com/Facinorous-420/dick/master/src/public/images/profile.png',
alt: 'DICK Logo'
},
};
// This is the type of the frontmatter you put in the docs markdown files.
export type Frontmatter = {
title: string;
description: string;
layout: string;
image?: { src: string; alt: string };
dir?: 'ltr' | 'rtl';
ogLocale?: string;
lang?: string;
};
export const KNOWN_LANGUAGES = {
English: 'en',
} as const;
export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES);
export const GITHUB_EDIT_URL = `https://github.com/Facinorous-420/dick-docs/tree/main`;
export const COMMUNITY_INVITE_URL = `https://discord.gg/xhZxFjEk8E`;
export const GITHUB_URL = `https://github.com/Facinorous-420/dick`;
// See "Algolia" section of the README for more information.
export const ALGOLIA = {
indexName: '',
appId: '',
apiKey: '',
};
export type SidebarLink = { text: string; link?: string, links?: SidebarLink[] };
export type Sidebar = Record<
typeof KNOWN_LANGUAGE_CODES[number],
Record<string, SidebarLink[]>
>;
export const SIDEBAR: Sidebar = {
en: {
'Getting Started': [
{ text: 'Home', link: 'dick-docs/en/' },
{ text: 'Production', links: [
{ text: 'Source', link: 'dick-docs/en/installation/production/source' },
{ text: 'Docker', link: 'dick-docs/en/installation/production/docker' },
]},
{ text: 'Development', links: [
{ text: 'Source', link: 'dick-docs/en/installation/development/source' }
]},
],
'Configuration': [
{ text: 'White Labeling', link: 'dick-docs/en/configs/whitelabel' }
],
'Features': [
{ text: 'File Manager', link: 'dick-docs/en/features/filemanager' },
{ text: 'Config Generator', link: 'dick-docs/en/features/configgen' },
{ text: 'Admin Panel', link: 'dick-docs/en/features/adminpanel' },
{ text: 'User Management', link: 'dick-docs/en/features/users' }
],
'More': [
{ text: 'Troubleshooting', link: 'dick-docs/en/more/troubleshooting' },
{ text: 'Changelog', link: 'dick-docs/en/more/changelog' }
],
},
};

1
src/env.d.ts vendored

@ -0,0 +1 @@
/// <reference types="astro/client" />

@ -0,0 +1,10 @@
import { KNOWN_LANGUAGES, KNOWN_LANGUAGE_CODES } from './config';
export { KNOWN_LANGUAGES, KNOWN_LANGUAGE_CODES };
export const langPathRegex = /\/([a-z]{2}-?[A-Z]{0,2})\//;
export function getLanguageFromURL(pathname: string) {
const langCodeMatch = pathname.match(langPathRegex);
const langCode = langCodeMatch ? langCodeMatch[1] : 'en';
return langCode as typeof KNOWN_LANGUAGE_CODES[number];
}

@ -0,0 +1,168 @@
---
import HeadCommon from "../components/HeadCommon.astro";
import HeadSEO from "../components/HeadSEO.astro";
import Header from "../components/Header/Header.astro";
import PageContent from "../components/PageContent/PageContent.astro";
import LeftSidebar from "../components/LeftSidebar/LeftSidebar.astro";
import RightSidebar from "../components/RightSidebar/RightSidebar.astro";
import * as CONFIG from "../config";
import type { MarkdownHeading } from "astro";
import Footer from "../components/Footer/Footer.astro";
type Props = {
frontmatter: CONFIG.Frontmatter;
headings: MarkdownHeading[];
};
const { frontmatter, headings } = Astro.props as Props;
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const currentPage = Astro.url.pathname;
const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.md`;
const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
---
<html dir={frontmatter.dir ?? "ltr"} lang={frontmatter.lang ?? "en-us"} class="initial">
<head>
<HeadCommon />
<HeadSEO frontmatter={frontmatter} canonicalUrl={canonicalURL} />
<title>
{frontmatter.title ? `${frontmatter.title} - ${CONFIG.SITE.title}` : CONFIG.SITE.title}
</title>
<style>
body {
width: 100%;
display: grid;
grid-template-rows: var(--theme-navbar-height) 1fr;
--gutter: 1rem;
--doc-padding: 2rem;
}
.layout {
display: grid;
grid-auto-flow: column;
grid-template-columns:
minmax(var(--gutter), 1fr) minmax(0, var(--max-width))
minmax(var(--gutter), 1fr);
overflow-x: hidden;
}
.grid-sidebar {
height: 100vh;
position: sticky;
top: 0;
padding: 0;
}
#grid-left {
position: fixed;
background-color: var(--theme-bg);
z-index: 10;
display: none;
}
#grid-main {
padding: var(--doc-padding) var(--gutter);
grid-column: 2;
display: flex;
flex-direction: column;
height: 100%;
}
#grid-right {
display: none;
}
@media (min-width: 50em) {
.layout {
overflow: initial;
grid-template-columns: 20rem minmax(0, var(--max-width));
gap: 1em;
}
#grid-left {
display: flex;
position: sticky;
grid-column: 1;
}
}
@media (min-width: 72em) {
.layout {
grid-template-columns: 20rem minmax(0, var(--max-width)) 18rem;
padding-left: 0;
padding-right: 0;
margin: 0 auto;
}
#grid-right {
grid-column: 3;
display: flex;
}
}
</style>
<style is:global>
.layout > * {
width: 100%;
height: 100%;
}
.mobile-sidebar-toggle {
overflow: hidden;
}
.mobile-sidebar-toggle #grid-left {
display: block;
top: 2rem;
}
::-webkit-scrollbar {
width: 0.5em;
border-radius: 0.5em;
}
::-webkit-scrollbar-track {
background: var(--theme-bg-accent);
border-radius: 0.5em;
}
::-webkit-scrollbar-thumb {
background-color: var(--theme-accent);
border-radius: 0.5em;
}
pre::-webkit-scrollbar {
width: 0.5em;
height: 0.5em;
}
pre::-webkit-scrollbar-track {
background: var(--theme-bg-accent);
border-radius: 0.5em;
}
pre::-webkit-scrollbar-thumb {
background-color: var(--theme-accent);
border-radius: 0.5em;
border-radius: 0.5em;
}
</style>
</head>
<body>
<Header currentPage={currentPage} />
<main class="layout">
<aside id="grid-left" class="grid-sidebar" title="Site Navigation">
<LeftSidebar currentPage={currentPage} />
</aside>
<div id="grid-main">
<PageContent frontmatter={frontmatter} headings={headings} githubEditUrl={githubEditUrl}>
<slot />
</PageContent>
</div>
<aside id="grid-right" class="grid-sidebar" title="Table of Contents">
<RightSidebar headings={headings} githubEditUrl={githubEditUrl} />
</aside>
</main>
<!-- <Footer path={currentFile} /> -->
</body>
</html>

@ -0,0 +1,18 @@
---
title: Services
description: Service Configuration
layout: ../../../layouts/MainLayout.astro
---
Inside of your dick root folder, you will see `src/constants.ts.example`. Copy this to `constants.ts`<br/>
Inside this file, is some basic configuration you can change for your set up. There are only 5 variables you need to worry about in this file:
| Variable | Description |
| --------------------------------------------- | :---------------------: |
| `ASS_SECURE = false` | Put this to true if you are running ASS behind a domain with HTTPS,. false if HTTP |
| `ASSDOMAIN = "127.0.0.1:40115"` | Put this to your ASS domain. Can be an ip, or domain for example `https://cdn.mydomain.com` |
| `ASS_LOCATION = "../ass"` | If running DICK seperately, DICK will use this to find your ASS install folder. Leave this alone if using Docker. |
| `PORT = "3000"` | Change this number to the port you wish DICK to run on. Leave this alone if using Docker. |
> **Note**
> If you want to set a user as admin, currently you must do it via the database file generated at `/src/database/users.json` and change the users role from `user` to `admin`. By default, the first user to login to your dick instance will be admin.

@ -0,0 +1,7 @@
---
title: Autobrr
description: Autobrr Widget Configuration
layout: ../../../layouts/MainLayout.astro
---
*Added in v1.1.0*

@ -0,0 +1,7 @@
---
title: Bazarr
description: Bazarr Widget Configuration
layout: ../../../layouts/MainLayout.astro
---
*Not Yet Added*

@ -0,0 +1,8 @@
---
title: Adguard Home
description: Adguard Home Widget Configuration
layout: ../../../layouts/MainLayout.astro
---
*Added in v1.0.0*

@ -0,0 +1,7 @@
---
title: Authentik
description: Authentik Widget Configuration
layout: ../../../layouts/MainLayout.astro
---
*Added in v1.1.0*

@ -0,0 +1,28 @@
---
title: Installation
description: Docs intro
layout: ../../layouts/MainLayout.astro
---
## About The Project
Dick was created to be an easy to use front end for <a href="https://github.com/tycrek/ass">Ass</a> as there was no public option to allow users to view their saved images on their server. I decided to learn tailwind and also at the same time build this. I did this all in my spare time, and will keep updating as best as I can while I use it. I'm still learning all this so if anyone has suggestions on how to do things better, I am all ears! I love learning! :)
**Current Feautres:**
1. General statistics on your file uploads
2. File browser, allows you to see all your uploads on one webpage
3. Deletion of items
4. Copy link of items
5. Customize DICK (completely white label)
6. Register new ASS users
7. hCaptcha on login and register pages <a href="https://www.hcaptcha.com/">You can learn more here</a>.
## Deploying
You have a few options for deploying DICK, depending on your needs.
[Production - Source](/en/installation/production/source)
[Production - Docker](/en/installation/production/docker)
[Development - Source](/en/installation/development/source)

@ -0,0 +1,14 @@
---
title: Source Installation
description: Install and run homepage from source
layout: ../../../../layouts/MainLayout.astro
---
1. Create a folder, call it whatever you wish
2. Install, and run ASS https://github.com/tycrek/ass#installation (This will create an `ass` folder)
3. Go back into the folder you created and clone this repo `git clone https://github.com/Facinorous-420/dick`
4. Go into the newly created `dick` folder `cd dick`
5. Go into `/src` and copy `constants.ts.example` to `constants.ts` and edit it as needed
6. Go back to the root of `dick` and install the dependancies for the frontend, `npm i`
7. Run `npm run build:dev` to compile the code base in watch mode
8. In a new terminal, run `npm run serve:dev` to run DICK using nodemon

@ -0,0 +1,14 @@
---
title: Docker Installation
description: Install and run homepage from Docker
layout: ../../../../layouts/MainLayout.astro
---
1. Create a folder, call it whatever you wish
2. Install, and run ASS https://github.com/tycrek/ass#installation (This will create an `ass` folder)
3. Go back into the folder you created and clone this repo `git clone https://github.com/Facinorous-420/dick.git`
4. Go into the newly created `dick` folder `cd dick`
5. Go into `/src` and copy `constants.ts.example` to `constants.ts` and edit it as needed
6. Go back to the root of `dick` and edit the `docker-compose.yml` to match your specific needs by changing the port, and voluime location where your ass files are on your system.
7. Run the docker compose file `docker-compose up -d` this will create a new container named `dick`.
8.

@ -0,0 +1,13 @@
---
title: Source Installation
description: Install and run homepage from source
layout: ../../../../layouts/MainLayout.astro
---
1. Create a folder, call it whatever you wish
2. Install, and run ASS https://github.com/tycrek/ass#installation (This will create an `ass` folder)
3. Go back into the folder you created and clone this repo `git clone https://github.com/Facinorous-420/dick.git`
4. Go into the newly created `dick` folder `cd dick`
5. Go into `/src` and copy `constants.ts.example` to `constants.ts` and edit it as needed
6. Go back to the root of `dick` and install the dependancies for the frontend, `npm i`
7. Run `npm start` to compile the code base and run DICK

@ -0,0 +1,9 @@
---
title: Changelog
description: Changelog
layout: ../../../layouts/MainLayout.astro
---
import Changelog from "../../../components/Content/Changelog.astro";
<Changelog />

@ -0,0 +1,14 @@
---
title: Troubleshooting
description: Basic Troubleshooting
layout: ../../../layouts/MainLayout.astro
---
## General Troubleshooting Tips
- Read this documentation and you should have no issues to troubleshoot.
## FAQ
1. **Why is there no FAQ?**
We still need to build this over time.

@ -0,0 +1,5 @@
<script is:inline>
// Redirect your homepage to the first page of documentation.
// If you have a landing page, remove this script and add it here!
window.location.pathname = `/en/installation`;
</script>

@ -0,0 +1,401 @@
* {
box-sizing: border-box;
margin: 0;
}
/* Global focus outline reset */
*:focus:not(:focus-visible) {
outline: none;
}
:root {
--user-font-scale: 1rem - 16px;
--max-width: calc(100% - 1rem);
}
@media (min-width: 50em) {
:root {
--max-width: 46em;
}
}
html {
scrollbar-color: var(--theme-accent) var(--theme-bg-accent);
scrollbar-width: thin;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
font-family: var(--font-body);
font-size: 1rem;
font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
line-height: 1.5;
max-width: 100vw;
}
nav ul {
list-style: none;
padding: 0;
}
.content > section > * + * {
margin-top: 1.25rem;
}
.content > section > :first-child {
margin-top: 0;
}
/* Typography */
h1,
h2,
h3,
h4,
h5,
h6 {
margin-bottom: 1rem;
font-weight: bold;
line-height: 1;
}
h1,
h2 {
max-width: 40ch;
}
:is(h2, h3):not(:first-child) {
margin-top: 3rem;
}
:is(h4, h5, h6):not(:first-child) {
margin-top: 2rem;
}
h1 {
font-size: 2rem;
font-weight: 800;
}
h2 {
font-size: 1.5rem;
font-weight: 600;
}
h3 {
font-size: 1.25rem;
}
h4 {
font-size: 1.15rem;
}
h5 {
font-size: 1rem;
}
p {
line-height: 1.65em;
}
.content ul {
line-height: 1.1em;
}
p,
.content ul {
color: var(--theme-text-light);
}
small,
.text_small {
font-size: 0.833rem;
}
a {
color: var(--theme-text-accent);
font-weight: 400;
text-underline-offset: 0.08em;
align-items: center;
gap: 0.5rem;
}
article > section :is(ul, ol) > * + * {
margin-top: 0.75rem;
}
article > section nav :is(ul, ol) > * + * {
margin-top: inherit;
}
article > section ol {
margin-left: 1rem;
}
article > section li {
margin-top: 1rem;
font-weight: 400;
}
article > section li p {
margin-top: 1rem;
}
article > section li pre {
margin-top: 0.25rem;
}
article > section :is(ul, ol) {
padding-left: 1em;
}
article > section nav :is(ul, ol) {
padding-left: inherit;
}
article > section nav {
margin-top: 1rem;
margin-bottom: 2rem;
}
article > section ::marker {
color: var(--theme-text-light);
}
article > section iframe {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}
a > code {
position: relative;
color: var(--theme-text-accent);
background: transparent;
text-underline-offset: var(--padding-block);
}
a > code::before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
background: var(--theme-accent);
opacity: var(--theme-accent-opacity);
border-radius: var(--border-radius);
}
a:hover,
a:focus {
text-decoration: underline;
}
a:focus {
outline: 2px solid currentColor;
outline-offset: 0.25em;
}
strong {
font-weight: 600;
color: inherit;
}
/* Supporting Content */
code {
--border-radius: 8px;
--padding-block: 0.2rem;
--padding-inline: 0.33rem;
font-family: var(--font-mono);
font-size: 0.85em;
color: inherit;
background-color: var(--theme-code-inline-bg);
padding: var(--padding-block) var(--padding-inline);
margin: calc(var(--padding-block) * -1) var(--padding-block / 2);
border-radius: var(--border-radius);
word-break: break-word;
}
pre.astro-code > code {
all: unset;
}
pre > code {
font-size: 1em;
}
table,
pre {
position: relative;
--padding-block: 1rem;
--padding-inline: 2rem;
padding: var(--padding-block) var(--padding-inline);
padding-right: calc(var(--padding-inline) * 2);
margin-left: calc(var(--padding-inline) * -1);
margin-right: calc(var(--padding-inline) * -1);
font-family: var(--font-mono);
line-height: 1.5;
font-size: 0.85em;
overflow-y: hidden;
overflow-x: auto;
}
table {
width: 100%;
padding: var(--padding-block) 0;
margin: 0;
border-collapse: collapse;
}
/* Zebra striping */
tr:nth-of-type(odd) {
background: var(--theme-bg-hover);
}
th {
background: var(--color-black);
color: var(--theme-color);
font-weight: bold;
}
td,
th {
padding: 6px;
text-align: left;
}
pre {
background-color: var(--theme-code-bg);
color: var(--theme-code-text);
}
blockquote code {
background-color: var(--theme-bg);
}
@media (min-width: 37.75em) {
pre {
--padding-inline: 1.25rem;
border-radius: 0;
margin-left: 0;
margin-right: 0;
}
}
blockquote {
margin: 2rem 0;
padding: 1.25em 1.5rem;
border-left: 3px solid var(--theme-text-light);
background-color: var(--theme-bg-offset);
border-radius: 0 0.25rem 0.25rem 0;
line-height: 1.7;
}
img {
max-width: 100%;
}
.flex {
display: flex;
align-items: center;
}
button {
display: flex;
align-items: center;
justify-items: center;
gap: 0.25em;
padding: 0.33em 0.67em;
border: 0;
background: var(--theme-bg);
display: flex;
font-size: 1rem;
align-items: center;
gap: 0.25em;
border-radius: 99em;
color: var(--theme-text);
background-color: var(--theme-bg);
}
h2.heading {
font-size: 1rem;
font-weight: 700;
padding: 0.1rem 1rem;
text-transform: uppercase;
margin-bottom: 0.5rem;
}
.heading-link {
font-size: 1rem;
list-style: none;
padding: 0.1rem 0 0.1rem 1rem;
/* border-left: 4px solid var(--theme-divider); */
}
.heading-link:hover,
.heading-link:focus {
border-left-color: var(--theme-accent);
color: var(--theme-accent);
}
.heading-link:focus-within {
color: var(--theme-text-light);
border-left-color: hsla(var(--color-gray-40), 1);
}
.heading-link svg {
opacity: 0.6;
}
.heading-link:hover svg {
opacity: 0.8;
}
.heading-link a {
display: inline-flex;
gap: 0.5em;
width: 100%;
padding: 0.15em 0 0.15em 0;
}
.heading-link.depth-3 {
padding-left: 2rem;
}
.heading-link.depth-4 {
padding-left: 3rem;
}
.heading-link a {
font: inherit;
color: inherit;
text-decoration: none;
}
/* Screenreader Only Text */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.focus\:not-sr-only:focus,
.focus\:not-sr-only:focus-visible {
position: static;
width: auto;
height: auto;
padding: 0;
margin: 0;
overflow: visible;
clip: auto;
white-space: normal;
}
:target {
scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem;
}

@ -0,0 +1,129 @@
:root {
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica,
Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
--font-body: "Manrope", system-ui, var(--font-fallback);
--font-mono: "IBM Plex Mono", Consolas, "Andale Mono WT", "Andale Mono",
"Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
"Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco,
"Courier New", Courier, monospace;
/*
* Variables with --color-base prefix define
* the hue, and saturation values to be used for
* hsla colors.
*
* ex:
*
* --color-base-{color}: {hue}, {saturation};
*
*/
--color-base-white: 0, 0%;
--color-base-black: 240, 100%;
--color-base-gray: 212, 10%;
--color-base-blue: 212, 80%;
--color-base-blue-dark: 212, 72%;
--color-base-green: 158, 79%;
--color-base-orange: 22, 100%;
--color-base-purple: 269, 79%;
--color-base-red: 351, 100%;
--color-base-yellow: 41, 100%;
/*
* Color palettes are made using --color-base
* variables, along with a lightness value to
* define different variants.
*
*/
--color-gray-5: var(--color-base-gray), 5%;
--color-gray-10: var(--color-base-gray), 10%;
--color-gray-20: var(--color-base-gray), 20%;
--color-gray-30: var(--color-base-gray), 30%;
--color-gray-40: var(--color-base-gray), 40%;
--color-gray-50: var(--color-base-gray), 50%;
--color-gray-60: var(--color-base-gray), 60%;
--color-gray-70: var(--color-base-gray), 70%;
--color-gray-80: var(--color-base-gray), 80%;
--color-gray-90: var(--color-base-gray), 90%;
--color-gray-95: var(--color-base-gray), 95%;
--color-blue: var(--color-base-blue), 61%;
--color-blue-dark: var(--color-base-blue-dark), 39%;
--color-green: var(--color-base-green), 42%;
--color-orange: var(--color-base-orange), 50%;
--color-purple: var(--color-base-purple), 54%;
--color-red: var(--color-base-red), 54%;
--color-yellow: var(--color-base-yellow), 59%;
}
:root {
color-scheme: light;
--theme-accent: hsla(var(--color-blue), 1);
--theme-text-accent: hsla(var(--color-blue), 1);
--theme-accent-opacity: 0.15;
--theme-divider: hsla(var(--color-gray-95), 1);
--theme-text: hsla(var(--color-gray-10), 1);
--theme-text-light: hsla(var(--color-gray-40), 1);
/* @@@: not used anywhere */
--theme-text-lighter: hsla(var(--color-gray-80), 1);
--theme-bg: hsla(var(--color-base-white), 100%, 1);
--theme-bg-hover: hsla(var(--color-gray-95), 1);
--theme-bg-offset: hsla(var(--color-gray-90), 1);
--theme-bg-accent: hsla(var(--color-blue), var(--theme-accent-opacity));
--theme-code-inline-bg: hsla(var(--color-gray-95), 1);
--theme-code-inline-text: var(--theme-text);
--theme-code-bg: hsla(217, 19%, 27%, 1);
--theme-code-text: hsla(var(--color-gray-95), 1);
--theme-navbar-bg: hsla(var(--color-base-white), 100%, 1);
--theme-navbar-height: 6rem;
--theme-selection-color: hsla(var(--color-blue), 1);
--theme-selection-bg: hsla(var(--color-blue), var(--theme-accent-opacity));
}
body {
background: var(--theme-bg);
color: var(--theme-text);
}
:root.theme-dark {
color-scheme: dark;
--theme-accent-opacity: 0.15;
--theme-accent: hsla(var(--color-blue), 1);
--theme-text-accent: hsla(var(--color-blue), 1);
--theme-divider: hsla(var(--color-gray-10), 1);
--theme-text: hsla(var(--color-gray-90), 1);
--theme-text-light: hsla(var(--color-gray-80), 1);
/* @@@: not used anywhere */
--theme-text-lighter: hsla(var(--color-gray-40), 1);
--theme-bg: hsla(215, 28%, 17%, 1);
--theme-bg-hover: hsla(var(--color-gray-40), 1);
--theme-bg-offset: hsla(var(--color-gray-5), 1);
--theme-code-inline-bg: hsla(var(--color-gray-10), 1);
--theme-code-inline-text: hsla(var(--color-base-white), 100%, 1);
--theme-code-bg: hsla(var(--color-gray-5), 1);
--theme-code-text: hsla(var(--color-base-white), 100%, 1);
--theme-navbar-bg: hsla(215, 28%, 17%, 1);
--theme-selection-color: hsla(var(--color-base-white), 100%, 1);
--theme-selection-bg: hsla(
var(--color-purple),
var(--theme-accent-opacity)
);
/* DocSearch [Algolia] */
--docsearch-modal-background: var(--theme-bg);
--docsearch-searchbox-focus-background: var(--theme-divider);
--docsearch-footer-background: var(--theme-divider);
--docsearch-text-color: var(--theme-text);
--docsearch-hit-background: var(--theme-divider);
--docsearch-hit-shadow: none;
--docsearch-hit-color: var(--theme-text);
--docsearch-footer-shadow: inset 0 2px 10px #000;
--docsearch-modal-shadow: inset 0 0 8px #000;
}
::selection {
color: var(--theme-selection-color);
background-color: var(--theme-selection-bg);
}

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}

@ -0,0 +1,7 @@
{
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"jsx": "preserve",
"skipLibCheck": true
}
}
Loading…
Cancel
Save