Compare commits
3 Commits
ce4ddf5a17
...
post-siden
Author | SHA1 | Date | |
---|---|---|---|
b4a1097845 | |||
adc582116b | |||
7d5c696fa7 |
6
.gitignore
vendored
6
.gitignore
vendored
@ -3,9 +3,3 @@ node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
**/_test.*
|
14
README.md
14
README.md
@ -1,6 +1,6 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
|
||||
|
||||
## Creating a project
|
||||
|
||||
@ -8,12 +8,14 @@ If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
npm init svelte@next
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
npm init svelte@next my-app
|
||||
```
|
||||
|
||||
> Note: the `@next` is temporary
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
@ -27,12 +29,10 @@ npm run dev -- --open
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.
|
||||
|
11
jsconfig.json
Normal file
11
jsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$lib": ["src/lib"],
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
}
|
7834
package-lock.json
generated
7834
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -1,23 +1,18 @@
|
||||
{
|
||||
"name": "blog.jfmonty2.com",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"@sveltejs/kit": "^1.20.4",
|
||||
"hast-util-to-text": "^4.0.0",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"mdsvex": "^0.11.0",
|
||||
"svelte": "^4.0.5",
|
||||
"unist-util-find": "^3.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^4.4.2"
|
||||
},
|
||||
"type": "module"
|
||||
"name": "blog",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
"preview": "svelte-kit preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^1.0.0-next.21",
|
||||
"@sveltejs/kit": "next",
|
||||
"mdsvex": "^0.9.8",
|
||||
"node-sass": "^6.0.1",
|
||||
"svelte": "^3.42.6",
|
||||
"svelte-preprocess": "^4.9.8"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
10
src/app.html
10
src/app.html
@ -4,12 +4,12 @@
|
||||
<meta charset="utf-8" />
|
||||
<link rel="preload" href="/Tajawal-Regular.woff2" as="font" type="font/woff2" />
|
||||
<link rel="preload" href="/Baskerville-Regular.woff2" as="font" type="font/woff2" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -34,8 +34,9 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<span class="drop-cap">{initial}</span>
|
||||
{#if remainder.length}
|
||||
<p>
|
||||
<span class="drop-cap">{initial}</span>
|
||||
<span class="first-word" style:--shift={shift}>{remainder}</span>
|
||||
{/if}
|
||||
<slot></slot>
|
||||
</p>
|
||||
|
||||
|
@ -1,67 +0,0 @@
|
||||
<script>
|
||||
export let level;
|
||||
export let id = '';
|
||||
|
||||
const tag = `h${level}`;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
a {
|
||||
/* Works better to set the size here for line-height reasons */
|
||||
font-size: 0.9em;
|
||||
/* color: hsl(0, 0%, 25%); */
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-bottom: 0.05em solid currentcolor;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 1em;
|
||||
/* tiny tweak for optical alignment */
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
.before {
|
||||
display: none;
|
||||
padding-right: 0.25em;
|
||||
margin-left: -1.25em;
|
||||
}
|
||||
|
||||
@media(min-width: 58rem) {
|
||||
.before {
|
||||
display: inline;
|
||||
opacity: 0;
|
||||
transition: opacity 150ms;
|
||||
}
|
||||
|
||||
.h:hover .before, .before:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.h:hover {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:element this={tag} {id} class="h">
|
||||
<span class="before">
|
||||
<a href="#{id}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
|
||||
</svg></a></span><span> <!-- Looks ugly but necessary to get rid of spurious whitespace -->
|
||||
<slot></slot>
|
||||
</span>
|
||||
<!-- Icon from https://heroicons.com/ -->
|
||||
<a href="#{id}" class="after">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
|
||||
</svg>
|
||||
</a>
|
||||
</svelte:element>
|
@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
{#if href.startsWith('/') || host(href) === $page.host}
|
||||
<a data-sveltekit-preload-data="hover" {href}>
|
||||
<a sveltekit:prefetch {href}>
|
||||
<slot></slot>
|
||||
</a>
|
||||
{:else}
|
||||
|
@ -1,9 +1,8 @@
|
||||
<script context="module">
|
||||
import { onMount } from 'svelte';
|
||||
import { formatDate } from './datefmt.js';
|
||||
import { makeSlug } from '$lib/utils.js';
|
||||
import { makeSlug } from '$lib/slug.js';
|
||||
|
||||
import Toc from './Toc.svelte';
|
||||
import Link from './Link.svelte';
|
||||
export { Link as a };
|
||||
</script>
|
||||
@ -12,82 +11,14 @@
|
||||
export let title, date;
|
||||
export const description = '';
|
||||
export const draft = false;
|
||||
export let toc = null;
|
||||
|
||||
export let prev = null;
|
||||
export let next = null;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, var(--content-width)) minmax(0, 1fr);
|
||||
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
|
||||
.left-gutter {
|
||||
grid-column: 1 / 2;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.post {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
|
||||
.footer {
|
||||
grid-column: 2 / 3;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
hr {
|
||||
grid-column: 2 / 3;
|
||||
width: 100%;
|
||||
border-top: 1px solid hsl(0 0% 75%);
|
||||
border-bottom: none;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45em;
|
||||
|
||||
font-size: 1rem;
|
||||
color: var(--content-color-faded);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.25em;
|
||||
text-decoration-color: transparent;
|
||||
|
||||
transition: 150ms;
|
||||
}
|
||||
.footer a:hover {
|
||||
text-decoration-color: currentColor;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footer svg {
|
||||
width: 1em;
|
||||
transition: 150ms;
|
||||
}
|
||||
|
||||
a.prev:hover svg {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
a.next:hover svg {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
@ -95,40 +26,8 @@
|
||||
<link rel="stylesheet" href="/prism-dracula.css" />
|
||||
</svelte:head>
|
||||
|
||||
<div class="page">
|
||||
<div class="title">
|
||||
<h1 id="{makeSlug(title)}">{title}</h1>
|
||||
<p class="subtitle">{formatDate(date)}</p>
|
||||
</div>
|
||||
|
||||
<div class="left-gutter">
|
||||
<Toc items={toc} />
|
||||
</div>
|
||||
|
||||
<div class="post">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="footer">
|
||||
{#if prev}
|
||||
<a href="/{prev}" class="prev" data-sveltekit-preload-data="hover">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />
|
||||
</svg>
|
||||
Previous
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
{#if next}
|
||||
<!-- we use margin-left rather than justify-content so it works regardless of whether the "previous" link exists -->
|
||||
<a href="/{next}" class="next" style="margin-left: auto;" data-sveltekit-preload-data="hover">
|
||||
Next
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div id="post">
|
||||
<h1 id="{makeSlug(title)}">{title}</h1>
|
||||
<p class="subtitle">{formatDate(date)}</p>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<style>
|
||||
<style lang="scss">
|
||||
/* always applicable */
|
||||
:global(body) {
|
||||
counter-reset: sidenote;
|
||||
@ -8,30 +8,30 @@
|
||||
counter-increment: sidenote;
|
||||
color: #444;
|
||||
margin-left: 0.05rem;
|
||||
}
|
||||
|
||||
.counter:after {
|
||||
font-size: 0.75em;
|
||||
position: relative;
|
||||
bottom: 0.3rem;
|
||||
color: #8c0606;
|
||||
&:after {
|
||||
font-size: 0.75em;
|
||||
position: relative;
|
||||
bottom: 0.3rem;
|
||||
color: #8c0606;
|
||||
}
|
||||
}
|
||||
|
||||
.sidenote {
|
||||
color: #555;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.sidenote:before {
|
||||
content: var(--sidenote-index, counter(sidenote)) " ";
|
||||
/* absolute positioning puts it at the top-left corner of the sidenote, overlapping with the content
|
||||
(because the sidenote is floated it counts as a positioned parent, I think) */
|
||||
position: absolute;
|
||||
/* translate moves it out to the left (and just a touch up to mimic the superscript efect)
|
||||
-100% refers to the width of the element, so it pushes it out further if necessary (i.e. two digits instead of one) */
|
||||
transform: translate(calc(-100% - 0.2rem), -0.15rem);
|
||||
font-size: 0.75rem;
|
||||
color: #8c0606;
|
||||
&:before {
|
||||
content: counter(sidenote) " ";
|
||||
/* absolute positioning puts it at the top-left corner of the sidenote, overlapping with the content
|
||||
(because the sidenote is floated it counts as a positioned parent, I think) */
|
||||
position: absolute;
|
||||
/* translate moves it out to the left (and just a touch up to mimic the superscript efect)
|
||||
-100% refers to the width of the element, so it pushes it out further if necessary (i.e. two digits instead of one) */
|
||||
transform: translate(calc(-100% - 0.2rem), -0.15rem);
|
||||
font-size: 0.75rem;
|
||||
color: #8c0606;
|
||||
}
|
||||
}
|
||||
|
||||
.sidenote-toggle {
|
||||
@ -46,28 +46,16 @@
|
||||
|
||||
.sidenote {
|
||||
--gap: 2rem;
|
||||
--sidenote-width: min(16rem, calc(50vw - var(--gap) - 1rem - var(--content-width) / 2));
|
||||
--sidenote-width: min(14rem, calc(50vw - var(--gap) - var(--content-width) / 2));
|
||||
width: var(--sidenote-width);
|
||||
hyphens: auto;
|
||||
position: relative;
|
||||
float: right;
|
||||
clear: right;
|
||||
margin-right: calc(0rem - var(--sidenote-width) - var(--gap)); /* gives us 2rem of space between content and sidenote */
|
||||
margin-right: calc(0rem - var(--sidenote-width) - var(--gap)); // gives us 2rem of space between content and sidenote
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
/* fade-in animation */
|
||||
.sidenote {
|
||||
opacity: 0;
|
||||
animation: fade-in 600ms ease-out;
|
||||
animation-delay: 500ms;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes fade-in {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
||||
|
||||
.nested.sidenote {
|
||||
margin-right: 0;
|
||||
margin-top: 0.7rem;
|
||||
@ -119,13 +107,26 @@
|
||||
font-size: 1.25rem;
|
||||
color: #8c0606;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dismiss:hover {
|
||||
transform: scale(1.1);
|
||||
font-weight: 800;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// /* slight tweaks for in between state */
|
||||
// @media (min-width: 52.5em) and (max-width: 70em) {
|
||||
// .sidenote {
|
||||
// padding-left: calc(50vw - 19rem);
|
||||
// }
|
||||
// }
|
||||
// @media (max-width: 52.5em) {
|
||||
// .sidenote {
|
||||
// padding-left: 2rem;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<script context="module">
|
||||
|
@ -1,143 +0,0 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { makeSlug } from '$lib/utils.js';
|
||||
|
||||
export let items;
|
||||
|
||||
items.forEach(i => i.slug = makeSlug(i.text));
|
||||
|
||||
let headings = [];
|
||||
let currentHeadingSlug = null;
|
||||
let currentSubheadingSlug = null;
|
||||
|
||||
function setCurrentHeading() {
|
||||
for (const h of headings) {
|
||||
const yPos = h.getBoundingClientRect().y;
|
||||
if (yPos > (window.innerHeight / 3)) {
|
||||
break;
|
||||
}
|
||||
if (h.tagName === 'H2') {
|
||||
currentHeadingSlug = h.id;
|
||||
currentSubheadingSlug = null;
|
||||
}
|
||||
if (h.tagName === 'H3') {
|
||||
currentSubheadingSlug = h.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount (() => {
|
||||
// These shouldn't change over the life of the page, so we can cache them
|
||||
headings = Array.from(document.querySelectorAll('h2[id], h3[id]'));
|
||||
setCurrentHeading();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:scroll={setCurrentHeading} />
|
||||
|
||||
<style>
|
||||
#toc {
|
||||
position: sticky;
|
||||
top: 1.5rem;
|
||||
margin-right: 2rem;
|
||||
|
||||
max-width: 14rem;
|
||||
color: var(--content-color-faded);
|
||||
opacity: 0;
|
||||
animation: fade-in 600ms ease-out;
|
||||
animation-delay: 500ms;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {opacity: 0}
|
||||
to {opacity: 1}
|
||||
}
|
||||
|
||||
/* margin-left is to match the padding on the top-level list items,
|
||||
but here it needs to be margin so that the border is also shifted */
|
||||
h5 {
|
||||
font-variant: petite-caps;
|
||||
font-weight: 500;
|
||||
max-width: fit-content;
|
||||
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.25em;
|
||||
|
||||
padding-bottom: 0.25em;
|
||||
border-bottom: 1px solid currentcolor;
|
||||
/* make the border stretch beyond the text just a bit, because I like the effect */
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
li.depth-2 {
|
||||
align-items: stretch;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
li.depth-3 {
|
||||
align-items: center;
|
||||
margin-bottom: 0.05rem;
|
||||
}
|
||||
|
||||
.marker {
|
||||
position: absolute;
|
||||
left: -0.6rem;
|
||||
}
|
||||
.bar {
|
||||
width: 0.1rem;
|
||||
height: 100%;
|
||||
}
|
||||
.dot {
|
||||
width: 0.15rem;
|
||||
height: 0.15rem;
|
||||
border-radius: 50%;
|
||||
/* vertically center within its containing block */
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
li.current, li:hover {
|
||||
color: var(--content-color);
|
||||
}
|
||||
.current .marker, li:hover .marker {
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<div id="toc">
|
||||
<h5>
|
||||
<span class="heading">Contents</span>
|
||||
</h5>
|
||||
<ul>
|
||||
{#each items as item}
|
||||
{#if item.depth === 2}
|
||||
<li class="depth-2" class:current={item.slug === currentHeadingSlug} style:align-items="stretch">
|
||||
<span class="marker bar"></span>
|
||||
<a href="#{item.slug}">{item.text}</a>
|
||||
</li>
|
||||
{:else if item.depth === 3}
|
||||
<li class="depth-3" class:current={item.slug === currentSubheadingSlug} style:align-items="center" style:margin-left="0.75em">
|
||||
<span class="marker dot"></span>
|
||||
<a href="#{item.slug}">{item.text}</a>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
20
src/lib/projects/sidenotes/Frame.svelte
Normal file
20
src/lib/projects/sidenotes/Frame.svelte
Normal file
@ -0,0 +1,20 @@
|
||||
<script>
|
||||
import Step from './Step.svelte';
|
||||
import {onMount} from 'svelte';
|
||||
|
||||
let frame;
|
||||
onMount(() => {
|
||||
frame.setAttribute('srcdoc', frame.innerHTML);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<iframe bind:this={frame}>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<Step />
|
||||
<p>Goodbye world!</p>
|
||||
</body>
|
||||
</html>
|
||||
</iframe>
|
7
src/lib/projects/sidenotes/Step.svelte
Normal file
7
src/lib/projects/sidenotes/Step.svelte
Normal file
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<p>hello world!</p>
|
||||
<button on:click={() => count++}>Increment</button>
|
||||
<p>The count is: {count}</p>
|
50
src/lib/slug.js
Normal file
50
src/lib/slug.js
Normal file
@ -0,0 +1,50 @@
|
||||
const nonAlphaNum = /[^A-Za-z0-9\-]/g;
|
||||
const space = /\s/g
|
||||
|
||||
export function makeSlug(text) {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(space, '-')
|
||||
.replace(nonAlphaNum, '')
|
||||
}
|
||||
|
||||
function apply(node, types, fn) {
|
||||
if (typeof types === 'string') {
|
||||
types = new Set([types]);
|
||||
}
|
||||
else if (!(types instanceof Set)) {
|
||||
types = new Set(types)
|
||||
console.log(types)
|
||||
}
|
||||
|
||||
if (types.has(node.type)) {
|
||||
fn(node);
|
||||
}
|
||||
if ('children' in node) {
|
||||
for (let child of node.children) {
|
||||
apply(child, types, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTextContent(node) {
|
||||
let segments = [];
|
||||
apply(node, 'text', textNode => {
|
||||
// skip all-whitespace strings
|
||||
if (textNode.value.match(/^\s+$/)) return;
|
||||
segments.push(textNode.value.trim());
|
||||
});
|
||||
|
||||
return segments.join(' ');
|
||||
}
|
||||
|
||||
export default function slug() {
|
||||
return (tree) => {
|
||||
apply(tree, 'element', e => {
|
||||
if (e.tagName.match(/h[1-6]/)) {
|
||||
let text = getTextContent(e);
|
||||
e.properties.id = makeSlug(text);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
const nonAlphaNum = /[^A-Za-z0-9\-]/g;
|
||||
const space = /\s+/g;
|
||||
export function makeSlug(text) {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(space, '-')
|
||||
.replace(nonAlphaNum, '');
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
// const Node = {
|
||||
// addChild(child) {
|
||||
// this.children.push(child);
|
||||
// return child;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
export function tag(name, attrs, children) {
|
||||
return {
|
||||
type: 'tag',
|
||||
tag: name,
|
||||
attrs: attrs || {},
|
||||
children: children || [],
|
||||
|
||||
addTag(name, attrs, children) {
|
||||
const child = tag(name, attrs, children);
|
||||
this.children.push(child);
|
||||
return child;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function text(content) {
|
||||
return {
|
||||
type: 'text',
|
||||
text: content,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function serialize(node, depth) {
|
||||
if (!depth) {
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
const indent = ' '.repeat(depth * 4);
|
||||
let fragments = [];
|
||||
|
||||
// version tag, if this is the top level
|
||||
if (depth === 0) {
|
||||
fragments.push('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||
}
|
||||
|
||||
fragments.push(`${indent}<${node.tag}`);
|
||||
|
||||
// this happens if there are multiple text nodes within the same parent
|
||||
if (node.type === 'text') {
|
||||
return `${indent}${escape(node.text)}`;
|
||||
}
|
||||
|
||||
if (node.children === undefined) {
|
||||
console.log(node);
|
||||
}
|
||||
|
||||
// opening tag <element attr="value">
|
||||
for (const attr in node.attrs) {
|
||||
fragments.push(` ${attr}="${node.attrs[attr]}"`);
|
||||
}
|
||||
if (node.children.length === 0) {
|
||||
fragments.push(' />');
|
||||
return fragments.join('');
|
||||
}
|
||||
fragments.push('>');
|
||||
|
||||
// if the only child is a single text node, skip recursion and just dump contents directly
|
||||
if (node.children.length === 1 && node.children[0].type === 'text') {
|
||||
const text = escape(node.children[0].text);
|
||||
fragments.push(text);
|
||||
}
|
||||
// otherwise, start a new line for each child node, then recurse
|
||||
else {
|
||||
for (const child of node.children) {
|
||||
fragments.push('\n');
|
||||
fragments.push(serialize(child, depth + 1));
|
||||
}
|
||||
// no need to verify that there were children, we already did that
|
||||
fragments.push(`\n${indent}`);
|
||||
}
|
||||
|
||||
fragments.push(`</${node.tag}>`);
|
||||
|
||||
return fragments.join('');
|
||||
}
|
||||
|
||||
|
||||
function escape(text) {
|
||||
// we aren't going to bother with escaping attributes, so we won't worry about quotes
|
||||
return text
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>');
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
import { visit, CONTINUE, EXIT, SKIP, } from 'unist-util-visit';
|
||||
import { find } from 'unist-util-find';
|
||||
import { toText } from 'hast-util-to-text';
|
||||
import { makeSlug } from '../lib/utils.js';
|
||||
|
||||
|
||||
export function localRehype() {
|
||||
let printed = false;
|
||||
|
||||
return (tree, vfile) => {
|
||||
const needsDropcap = vfile.data.fm.dropcap !== false
|
||||
let dropcapAdded = false;
|
||||
|
||||
let moduleScript;
|
||||
let imports = new Set();
|
||||
if (needsDropcap) {
|
||||
imports.add("import Dropcap from '$lib/Dropcap.svelte';");
|
||||
}
|
||||
|
||||
visit(tree, node => {
|
||||
// add slugs to headings
|
||||
if (isHeading(node)) {
|
||||
processHeading(node);
|
||||
imports.add("import Heading from '$lib/Heading.svelte';");
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
// mdsvex adds a <script context="module"> so we just hijack that for our own purposes
|
||||
if (isModuleScript(node)) {
|
||||
moduleScript = node;
|
||||
}
|
||||
|
||||
// convert first letter/word of first paragraph to <Dropcap word="{whatever}">
|
||||
if (needsDropcap && !dropcapAdded && isParagraph(node)) {
|
||||
addDropcap(node);
|
||||
dropcapAdded = true;
|
||||
return SKIP;
|
||||
}
|
||||
});
|
||||
|
||||
// insert our imports at the top of the `<script context="module">` tag
|
||||
if (imports.size > 0) {
|
||||
const script = moduleScript.value;
|
||||
// split the script where the opening tag ends
|
||||
const i = script.indexOf('>');
|
||||
const openingTag = script.slice(0, i + 1);
|
||||
const remainder = script.slice(i + 1);
|
||||
|
||||
// mdvsex uses tabs so we will as well
|
||||
const importScript = Array.from(imports).join('\n\t');
|
||||
|
||||
moduleScript.value = `${openingTag}\n\t${importScript}${remainder}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function processHeading(node) {
|
||||
const level = node.tagName.slice(1);
|
||||
node.tagName = 'Heading';
|
||||
node.properties.level = level;
|
||||
node.properties.id = makeSlug(toText(node));
|
||||
}
|
||||
|
||||
|
||||
function addDropcap(par) {
|
||||
let txtNode = find(par, {type: 'text'});
|
||||
const i = txtNode.value.search(/\s/);
|
||||
const firstWord = txtNode.value.slice(0, i);
|
||||
const remainder = txtNode.value.slice(i);
|
||||
|
||||
par.children.unshift({
|
||||
type: 'raw',
|
||||
value: `<Dropcap word="${firstWord}" />`,
|
||||
});
|
||||
txtNode.value = remainder;
|
||||
}
|
||||
|
||||
|
||||
function isHeading(node) {
|
||||
return node.type === 'element' && node.tagName.match(/h[1-6]/);
|
||||
}
|
||||
|
||||
function isModuleScript(node) {
|
||||
return node.type === 'raw' && node.value.match(/^<script context="module">/);
|
||||
}
|
||||
|
||||
function isParagraph(node) {
|
||||
return node.type === 'element' && node.tagName === 'p';
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
import { visit } from 'unist-util-visit';
|
||||
import { toString } from 'mdast-util-to-string';
|
||||
|
||||
import fs from 'node:fs';
|
||||
|
||||
// build table of contents and inject into frontmatter
|
||||
export function localRemark() {
|
||||
return (tree, vfile) => {
|
||||
let toc = [];
|
||||
let description = null;
|
||||
|
||||
visit(tree, ['heading', 'paragraph'], node => {
|
||||
// build table of contents and inject into frontmatter
|
||||
if (node.type === 'heading') {
|
||||
toc.push({
|
||||
text: toString(node),
|
||||
depth: node.depth,
|
||||
});
|
||||
}
|
||||
|
||||
// inject description (first 25 words of the first paragraph)
|
||||
if (node.type === 'paragraph' && description === null) {
|
||||
description = summarize(node);
|
||||
}
|
||||
});
|
||||
|
||||
vfile.data.fm.toc = toc;
|
||||
vfile.data.fm.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convert paragraph to single string after stripping everything between html tags
|
||||
function summarize(par) {
|
||||
let newChildren = [];
|
||||
let push = true;
|
||||
for (const child of par.children) {
|
||||
if (child.type === 'html') {
|
||||
push = !push;
|
||||
continue;
|
||||
}
|
||||
if (push) {
|
||||
newChildren.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return toString({type: 'paragraph', children: newChildren});
|
||||
}
|
@ -1 +0,0 @@
|
||||
export const prerender = true;
|
@ -1,39 +0,0 @@
|
||||
<style>
|
||||
.header {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 4;
|
||||
background-color: #4f5f68;
|
||||
}
|
||||
|
||||
nav {
|
||||
max-width: 30rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
nav a {
|
||||
width: 8rem;
|
||||
min-width: 6rem;
|
||||
font-size: 1.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
nav a:hover {
|
||||
background-color: #00000025;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="header">
|
||||
<nav>
|
||||
<a data-sveltekit-preload-data="hover" href="/">Home</a>
|
||||
<a data-sveltekit-preload-data="hover" href="/posts">Posts</a>
|
||||
<a data-sveltekit-preload-data="hover" href="/">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<slot></slot>
|
||||
</main>
|
@ -1,7 +0,0 @@
|
||||
export async function load({ data }) {
|
||||
let post = await import(`./_posts/${data.slug}.svx`);
|
||||
post.metadata.next = data.next;
|
||||
return {
|
||||
post: post.default,
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { postData, siblingPosts } from './_posts/all.js';
|
||||
|
||||
|
||||
// this is in a "servserside" loader so that we don't end up embedding the metadata
|
||||
// for every post into the final page
|
||||
export function load() {
|
||||
return {
|
||||
slug: postData[0].slug,
|
||||
next: postData[1].slug,
|
||||
};
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
<script>
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<svelte:component this={data.post} />
|
24
src/routes/[slug].svelte
Normal file
24
src/routes/[slug].svelte
Normal file
@ -0,0 +1,24 @@
|
||||
<script context="module">
|
||||
export async function load({ url, params }) {
|
||||
try {
|
||||
let post = await import(`./_posts/${params.slug}.svx`);
|
||||
return {
|
||||
props: {
|
||||
post: post.default
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
return {
|
||||
status: 404,
|
||||
error: `Not found: ${url.pathname}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export let post;
|
||||
</script>
|
||||
|
||||
<svelte:component this={post} />
|
@ -1,18 +0,0 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
|
||||
export async function load({ url, params, data }) {
|
||||
try {
|
||||
let post = await import(`../_posts/${params.slug}.svx`);
|
||||
post.metadata.prev = data.prev;
|
||||
post.metadata.next = data.next;
|
||||
return {
|
||||
post: post.default,
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// throw error(404, `Not found: ${url.pathname}`);
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import { postData } from '../_posts/all.js';
|
||||
|
||||
|
||||
export function load({ params }) {
|
||||
const i = postData.findIndex(p => p.slug === params.slug);
|
||||
return {
|
||||
prev: i > 0 ? postData[i - 1].slug : null,
|
||||
next: i < postData.length - 1 ? postData[i + 1].slug : null,
|
||||
};
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
<script>
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<svelte:component this={data.post} />
|
45
src/routes/__layout.svelte
Normal file
45
src/routes/__layout.svelte
Normal file
@ -0,0 +1,45 @@
|
||||
<style>
|
||||
:global(main) {
|
||||
--content-width: 42rem;
|
||||
box-sizing: border-box;
|
||||
max-width: var(--content-width);
|
||||
margin: 0 auto;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: #4f5f68;
|
||||
}
|
||||
|
||||
#nav-main {
|
||||
max-width: 30rem;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(6rem, 8rem));
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#nav-main a {
|
||||
font-size: 1.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
#nav-main a:hover {
|
||||
background-color: #00000025;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="header">
|
||||
<nav id="nav-main">
|
||||
<a sveltekit:prefetch href="/">Home</a>
|
||||
<a sveltekit:prefetch href="/posts">Posts</a>
|
||||
<a sveltekit:prefetch href="/">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<slot></slot>
|
||||
</main>
|
@ -3,7 +3,6 @@ title: Imagining A Passwordless Future
|
||||
description: Can we replace passwords with something more user-friendly?
|
||||
date: 2021-04-30
|
||||
draft: true
|
||||
dropcap: false
|
||||
---
|
||||
<script>
|
||||
import Sidenote from '$lib/Sidenote.svelte';
|
||||
|
@ -4,8 +4,10 @@ description: An entirely-too-detailed dive into how I implemented sidenotes for
|
||||
date: 2023-08-14
|
||||
---
|
||||
<script>
|
||||
import Dropcap from '$lib/Dropcap.svelte';
|
||||
import Sidenote from '$lib/Sidenote.svelte';
|
||||
import UnstyledSidenote from '$lib/UnstyledSidenote.svelte';
|
||||
import Frame from '$lib/projects/sidenotes/Frame.svelte';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@ -62,7 +64,7 @@ date: 2023-08-14
|
||||
}
|
||||
</style>
|
||||
|
||||
One of my major goals when building this blog was to have sidenotes. I've always been a fan of sidenotes on the web, because the most comfortable reading width for a column of text is <em>far</em> less than the absurd amounts of screen width we tend to have available, and what else are we going to use it for?<Sidenote>Some sites use it for ads, of course, which is yet another example of how advertising ruins everything.</Sidenote>
|
||||
<Dropcap word="One">of my major goals when building this blog was to have sidenotes. I've always been a fan of sidenotes on the web, because the most comfortable reading width for a column of text is <em>far</em> less than the absurd amounts of screen width we tend to have available, and what else are we going to use it for?<Sidenote>Some sites use it for ads, of course, which is yet another example of how advertising ruins everything.</Sidenote></Dropcap>
|
||||
|
||||
Footnotes don't really work on the web the way they do on paper, since the web doesn't have page breaks. You _can_ stick your footnotes in a floating box at the bottom of the page, so they're visible at the bottom of the text just like they would be on a printed page, but this sacrifices precious vertical space.<Sidenote>On mobile, it's _horizontal_ space that's at a premium, so I do use this approach there. Although I'm a pretty heavy user of sidenotes, so I have to make them toggleable as well or they'd fill up the entire screen.</Sidenote> Plus, you usually end up with the notes further away from the point of divergence than they would be as sidenotes anyway.
|
||||
|
||||
|
@ -4,11 +4,12 @@ description: They're more similar than they are different, but they say the most
|
||||
date: 2023-06-29
|
||||
---
|
||||
<script>
|
||||
import Dropcap from '$lib/Dropcap.svelte';
|
||||
import Sidenote from '$lib/Sidenote.svelte';
|
||||
</script>
|
||||
|
||||
|
||||
Recently I've had a chance to get to know Vue a bit. Since my frontend framework of choice has previously been Svelte (this blog is built in Svelte, for instance) I was naturally interested in how they compared.
|
||||
<Dropcap word="Recently">I've had a chance to get to know Vue a bit. Since my frontend framework of choice has previously been Svelte (this blog is built in Svelte, for instance) I was naturally interested in how they compared.</Dropcap>
|
||||
|
||||
Of course, this is only possible because Vue and Svelte are really much more similar than they are different. Even among frontend frameworks, they share a lot of the same basic ideas and high-level concepts, which means that we get to dive right into the nitpicky details and have fun debating `bind:attr={value}` versus `:attr="value"`. In the meantime, a lot of the building blocks are basically the same or at least have equivalents, such as:
|
||||
* Single-file components with separate sections for markup, style, and logic
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { tag, text, serialize } from '$lib/xml.js';
|
||||
import { postData } from '../_posts/all.js';
|
||||
|
||||
|
||||
export function GET() {
|
||||
return new Response(renderFeed(), {
|
||||
headers: {'Content-Type': 'application/atom+xml'}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function renderFeed() {
|
||||
const feed = tag('feed', {xmlns: 'http://www.w3.org/2005/Atom'});
|
||||
|
||||
feed.addTag('id', {}, [text('https://blog.jfmonty2.com/')])
|
||||
feed.addTag('title', {}, [text("Joe's Blog")]);
|
||||
feed.addTag('link', {rel: 'alternate', href: 'https://blog.jfmonty2.com/'});
|
||||
feed.addTag('link', {rel: 'self', href: 'https://blog.jfmonty2.com/feed/'});
|
||||
|
||||
const lastUpdate = iso(postData[0].updated || postData[0].date);
|
||||
feed.addTag('updated', {}, [text(lastUpdate)]);
|
||||
|
||||
const author = feed.addTag('author');
|
||||
author.addTag('name', {}, [text('Joseph Montanaro')]);
|
||||
|
||||
for (const post of postData) {
|
||||
const url = `https://blog.jfmonty2.com/${post.slug}`
|
||||
const entry = feed.addTag('entry');
|
||||
entry.addTag('title', {}, [text(post.title)]);
|
||||
entry.addTag('link', {rel: 'alternate', href: url});
|
||||
entry.addTag('id', {}, [text(url)]);
|
||||
|
||||
const publishedDate = iso(post.date);
|
||||
entry.addTag('published', {}, [text(publishedDate)])
|
||||
const updatedDate = iso(post.updated || post.date);
|
||||
entry.addTag('updated', {}, [text(updatedDate)]);
|
||||
|
||||
entry.addTag('content', {type: 'html'}, [text(renderDescription(post))]);
|
||||
}
|
||||
return serialize(feed);
|
||||
}
|
||||
|
||||
|
||||
function renderDescription(post) {
|
||||
return `<p>${post.description} <a href="https://blog.jfmonty2.com/${post.slug}">Read more</a></p>`;
|
||||
}
|
||||
|
||||
|
||||
function iso(datetimeStr) {
|
||||
return new Date(datetimeStr).toISOString();
|
||||
}
|
18
src/routes/index.svelte
Normal file
18
src/routes/index.svelte
Normal file
@ -0,0 +1,18 @@
|
||||
<script context="module">
|
||||
export async function load({ fetch }) {
|
||||
const resp = await fetch('/latest.json');
|
||||
const postMeta = await resp.json();
|
||||
const post = await import(`./_posts/${postMeta.slug}.svx`);
|
||||
return {
|
||||
props: {
|
||||
post: post.default,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export let post;
|
||||
</script>
|
||||
|
||||
<svelte:component this={post} />
|
5
src/routes/latest.json.js
Normal file
5
src/routes/latest.json.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { postData } from './posts.js';
|
||||
|
||||
export async function get() {
|
||||
return {body: postData[0]};
|
||||
}
|
@ -1,26 +1,28 @@
|
||||
import { dev } from '$app/environment';
|
||||
const posts = import.meta.globEager('./*.svx');
|
||||
import { dev } from '$app/env';
|
||||
const posts = import.meta.globEager('./_posts/*.svx');
|
||||
|
||||
export let postData = [];
|
||||
|
||||
let postData = [];
|
||||
for (const path in posts) {
|
||||
// skip draft posts in production mode
|
||||
if (!dev && posts[path].metadata.draft) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// slice off the ./ and the .svx
|
||||
const slug = path.slice(2, -4);
|
||||
const slug = path.slice(9, -4)
|
||||
posts[path].metadata.slug = slug;
|
||||
postData.push(posts[path].metadata);
|
||||
}
|
||||
|
||||
|
||||
postData.sort((a, b) => {
|
||||
// sorting in reverse, so we flip the intuitive order
|
||||
if (a.date > b.date) return -1;
|
||||
if (a.date < b.date) return 1;
|
||||
return 0;
|
||||
});
|
||||
})
|
||||
|
||||
export async function get() {
|
||||
return {
|
||||
body: {postData}
|
||||
};
|
||||
}
|
||||
|
||||
export { postData };
|
@ -1,20 +1,27 @@
|
||||
<script>
|
||||
import { formatDate } from '$lib/datefmt.js';
|
||||
import { postData } from '../_posts/all.js';
|
||||
export let postData;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
#posts {
|
||||
/*text-align: center;*/
|
||||
max-width: var(--content-width);
|
||||
margin: 0 auto;
|
||||
max-width: 24rem;
|
||||
// margin-top: 1.25rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 2rem 0;
|
||||
border-color: #eee;
|
||||
.post {
|
||||
border-bottom: 2px solid #eee;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* .post-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
}*/
|
||||
|
||||
.post-date {
|
||||
color: #808080;
|
||||
}
|
||||
@ -37,14 +44,9 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
h2 a {
|
||||
color: currentcolor;
|
||||
h3 {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -54,22 +56,18 @@
|
||||
|
||||
<div id="posts">
|
||||
<h1 style:text-align="center">All Posts</h1>
|
||||
{#each postData as post, idx}
|
||||
{#each postData as post}
|
||||
<div class="post">
|
||||
<div class="post-date">{new Date(post.date).toISOString().split('T')[0]}</div>
|
||||
<h2>
|
||||
<a data-sveltekit-preload-data="hover" class="post-link" href="/{post.slug}">
|
||||
{post.title}
|
||||
<div>
|
||||
<a sveltekit:prefetch class="post-link" href="/{post.slug}">
|
||||
<h3>{post.title}<h3>
|
||||
</a>
|
||||
{#if post.draft}
|
||||
<span class="draft-notice">Draft</span>
|
||||
{/if}
|
||||
</h2>
|
||||
</div>
|
||||
<p>{post.description}</p>
|
||||
</div>
|
||||
|
||||
{#if idx < postData.length - 1}
|
||||
<hr>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
@ -29,12 +29,10 @@ html {
|
||||
line-height: var(--content-line-height);
|
||||
letter-spacing: -0.005em;
|
||||
color: var(--content-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
--content-width: 42rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
|
@ -1,24 +1,24 @@
|
||||
import staticAdapter from '@sveltejs/adapter-static';
|
||||
import { mdsvex } from 'mdsvex';
|
||||
import staticAdapter from '@sveltejs/adapter-static';
|
||||
import svp from 'svelte-preprocess';
|
||||
import slug from './src/lib/slug.js';
|
||||
|
||||
import { localRemark } from './src/plugins/remark.js';
|
||||
import { localRehype } from './src/plugins/rehype.js';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
extensions: ['.svelte', '.svx'],
|
||||
preprocess: [
|
||||
mdsvex({
|
||||
layout: './src/lib/Post.svelte',
|
||||
remarkPlugins: [localRemark],
|
||||
rehypePlugins: [localRehype],
|
||||
rehypePlugins: [slug],
|
||||
}),
|
||||
svp.scss(),
|
||||
],
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
// hydrate the <div id="svelte"> element in src/app.html
|
||||
adapter: staticAdapter(),
|
||||
prerender: {
|
||||
default: true,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
281
tmp/crane.svg
Normal file
281
tmp/crane.svg
Normal file
@ -0,0 +1,281 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<!-- Crane image created by macrovector on Freepik: https://www.freepik.com/free-vector/construction-icons-set_1537228.htm#query=crane&position=3&from_view=keyword -->
|
||||
|
||||
<svg
|
||||
width="28.305676mm"
|
||||
height="28.174238mm"
|
||||
viewBox="0 0 28.305676 28.174238"
|
||||
version="1.1"
|
||||
id="svg1392"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
sodipodi:docname="crane.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1394"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.1089995"
|
||||
inkscape:cx="-5.6899017"
|
||||
inkscape:cy="78.710306"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1389" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-139.84716,-103.71933)">
|
||||
<path
|
||||
d="m 166.79605,124.82179 h 0.18627 v -20.83188 h -0.18627 v 20.83188"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path324" />
|
||||
<path
|
||||
d="m 166.25101,125.97184 h 1.27635 v -0.51893 c 0,-0.1323 -0.10724,-0.23919 -0.23918,-0.23919 h -0.79763 c -0.13229,0 -0.23954,0.10689 -0.23954,0.23919 v 0.51893"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path326" />
|
||||
<path
|
||||
d="m 166.6934,125.21372 h 0.39193 v -0.64981 h -0.39193 v 0.64981"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path328" />
|
||||
<path
|
||||
d="m 165.62554,126.85626 c 0,0.69814 0.56585,1.26365 1.26365,1.26365 0.69779,0 1.26365,-0.56551 1.26365,-1.26365 0,-0.6978 -0.56586,-1.26365 -1.26365,-1.26365 -0.6978,0 -1.26365,0.56585 -1.26365,1.26365"
|
||||
style="fill:#f9a727;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path330" />
|
||||
<path
|
||||
d="m 143.76637,124.8673 19.84057,-20.49675 -0.0935,-0.0903 -19.84093,20.49639 0.0938,0.0907"
|
||||
style="fill:#100f0d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path332" />
|
||||
<path
|
||||
d="m 149.82885,128.1506 2.90478,-6.56802 10.82675,-17.25718 1.80869,1.13488 -10.81899,17.2466 -4.61751,5.50863 z m 13.67261,-24.08167 -10.93188,17.42652 -2.97638,6.72711 0.37782,0.23707 4.72899,-5.64338 10.92553,-17.41488 -2.12408,-1.33244"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path334" />
|
||||
<path
|
||||
d="m 154.67073,122.68783 -0.0988,0.1577 -1.96638,-1.23367 0.0988,-0.15769 1.96638,1.23366"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path336" />
|
||||
<path
|
||||
d="m 154.712,122.74604 -0.18132,0.0413 -0.71791,-3.13443 0.18168,-0.0416 0.71755,3.13478"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path338" />
|
||||
<path
|
||||
d="m 155.85042,120.93135 -3.13443,0.71755 -0.0416,-0.18132 3.13478,-0.71791 0.0413,0.18168"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path340" />
|
||||
<path
|
||||
d="m 155.87935,120.76167 -0.0991,0.15769 -1.96639,-1.23366 0.0991,-0.1577 1.96639,1.23367"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path342" />
|
||||
<path
|
||||
d="m 155.92062,120.81952 -0.18168,0.0416 -0.71755,-3.13443 0.18133,-0.0416 0.7179,3.13443"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path344" />
|
||||
<path
|
||||
d="m 157.05903,119.00483 -3.13478,0.71791 -0.0413,-0.18133 3.13443,-0.7179 0.0416,0.18132"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path346" />
|
||||
<path
|
||||
d="m 157.08761,118.8355 -0.0988,0.15769 -1.96638,-1.23366 0.0988,-0.15769 1.96638,1.23366"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path348" />
|
||||
<path
|
||||
d="m 157.12888,118.89336 -0.18132,0.0416 -0.71791,-3.13478 0.18168,-0.0413 0.71755,3.13443"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path350" />
|
||||
<path
|
||||
d="m 158.2673,117.07867 -3.13443,0.7179 -0.0416,-0.18168 3.13478,-0.71755 0.0413,0.18133"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path352" />
|
||||
<path
|
||||
d="m 158.29623,116.90898 -0.0991,0.15769 -1.96639,-1.23331 0.0991,-0.15804 1.96639,1.23366"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path354" />
|
||||
<path
|
||||
d="m 158.3375,116.96719 -0.18168,0.0416 -0.71755,-3.13479 0.18133,-0.0413 0.7179,3.13443"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path356" />
|
||||
<path
|
||||
d="m 159.47592,115.1525 -3.13479,0.7179 -0.0413,-0.18168 3.13443,-0.71755 0.0416,0.18133"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path358" />
|
||||
<path
|
||||
d="m 159.50449,114.98282 -0.0988,0.15804 -1.96638,-1.23366 0.0988,-0.15805 1.96638,1.23367"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path360" />
|
||||
<path
|
||||
d="m 159.54577,115.04102 -0.18133,0.0416 -0.71791,-3.13478 0.18169,-0.0413 0.71755,3.13443"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path362" />
|
||||
<path
|
||||
d="m 160.68418,113.22633 -3.13443,0.71791 -0.0416,-0.18168 3.13478,-0.71755 0.0413,0.18132"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path364" />
|
||||
<path
|
||||
d="m 160.71311,113.05665 -0.0991,0.15769 -1.96639,-1.23331 0.0991,-0.15805 1.96639,1.23367"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path366" />
|
||||
<path
|
||||
d="m 160.75438,113.11486 -0.18168,0.0416 -0.7179,-3.13478 0.18168,-0.0413 0.7179,3.13443"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path368" />
|
||||
<path
|
||||
d="m 161.8928,111.30017 -3.13479,0.7179 -0.0413,-0.18168 3.13443,-0.71755 0.0416,0.18133"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path370" />
|
||||
<path
|
||||
d="m 161.92137,111.13048 -0.0991,0.15805 -1.96603,-1.23367 0.0988,-0.15804 1.96638,1.23366"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path372" />
|
||||
<path
|
||||
d="m 161.96265,111.18869 -0.18133,0.0416 -0.71791,-3.13479 0.18169,-0.0416 0.71755,3.13478"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path374" />
|
||||
<path
|
||||
d="m 163.10106,109.374 -3.13443,0.7179 -0.0416,-0.18168 3.13478,-0.7179 0.0413,0.18168"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path376" />
|
||||
<path
|
||||
d="m 163.12999,109.20432 -0.0991,0.15769 -1.96639,-1.23367 0.0991,-0.15769 1.96639,1.23367"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path378" />
|
||||
<path
|
||||
d="m 163.17126,109.26252 -0.18168,0.0413 -0.71755,-3.13443 0.18133,-0.0416 0.7179,3.13478"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path380" />
|
||||
<path
|
||||
d="m 164.30968,107.44783 -3.13479,0.71755 -0.0413,-0.18132 3.13443,-0.71791 0.0416,0.18168"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path382" />
|
||||
<path
|
||||
d="m 164.33825,107.27815 -0.0988,0.15769 -1.96638,-1.23366 0.0988,-0.1577 1.96638,1.23367"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path384" />
|
||||
<path
|
||||
d="m 164.37953,107.33636 -0.18133,0.0413 -0.7179,-3.13443 0.18168,-0.0416 0.71755,3.13479"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path386" />
|
||||
<path
|
||||
d="m 165.51794,105.52167 -3.13443,0.71755 -0.0416,-0.18133 3.13443,-0.7179 0.0416,0.18168"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path388" />
|
||||
<path
|
||||
d="m 153.38944,124.32366 -0.18133,0.0416 -0.64382,-2.81128 0.18133,-0.0416 0.64382,2.81128"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path390" />
|
||||
<path
|
||||
d="m 153.34817,124.26581 -0.0988,0.15769 -1.48908,-0.93415 0.0991,-0.1577 1.48873,0.93416"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path392" />
|
||||
<path
|
||||
d="m 154.64215,122.85752 -2.81163,0.64382 -0.0413,-0.18168 2.81129,-0.64382 0.0416,0.18168"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path394" />
|
||||
<path
|
||||
d="m 152.06476,125.91399 -0.18556,0.012 -0.16228,-2.50931 0.18591,-0.0123 0.16193,2.50966"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path396" />
|
||||
<path
|
||||
d="m 152.02137,125.84096 -0.0988,0.15769 -0.98954,-0.62053 0.0991,-0.15769 0.98919,0.62053"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path398" />
|
||||
<path
|
||||
d="m 153.33405,124.43056 -2.31598,0.95461 -0.0709,-0.17215 2.31599,-0.95462 0.0709,0.17216"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path400" />
|
||||
<path
|
||||
d="m 167.06769,103.88196 c -0.20602,-0.12912 -0.44944,-0.1838 -0.69074,-0.15523 l -2.87549,0.3422 -0.0935,0.16122 2.09691,1.31551 1.58573,-1.32151 c 0.0522,-0.0434 0.0801,-0.10936 0.0759,-0.17709 -0.005,-0.0677 -0.0416,-0.12912 -0.0988,-0.1651"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path402" />
|
||||
<path
|
||||
d="m 163.77733,104.24179 2.71922,-0.33902 c 0.13687,-0.0169 0.27551,0.0138 0.39264,0.0871 l 0.0395,0.0247 c 0.0162,0.0102 0.0265,0.0275 0.0279,0.0466 0.001,0.019 -0.007,0.0378 -0.0215,0.0501 l -1.44533,1.20474 -1.71239,-1.07421"
|
||||
style="fill:#f9a727;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path404" />
|
||||
<path
|
||||
d="m 145.63997,128.61168 h 3.81459 v 0.62547 h -3.81459 v -0.62547"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path406" />
|
||||
<path
|
||||
d="m 140.21425,126.26888 c 0.0783,-0.35489 0.34149,-0.63994 0.68897,-0.74683 l 6.68832,-2.05246 -0.3549,5.14209 h -6.77756 c -0.18556,0 -0.3609,-0.084 -0.47696,-0.2286 -0.11606,-0.14429 -0.16051,-0.33373 -0.12065,-0.5147 l 0.35278,-1.5995"
|
||||
style="fill:#f9a727;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path408" />
|
||||
<path
|
||||
d="m 151.90037,131.89357 c 0.75917,0 1.37689,-0.61771 1.37689,-1.37724 0,-0.75918 -0.61772,-1.3769 -1.37689,-1.3769 -0.0243,-0.002 -3.12667,-0.22013 -4.15961,-0.22013 -1.03293,0 -4.13526,0.21802 -4.1663,0.22049 h -7.1e-4 c -0.75212,0 -1.36948,0.61736 -1.36948,1.37654 0,0.75953 0.61771,1.37724 1.37689,1.37724 h 8.31921"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path410" />
|
||||
<path
|
||||
d="m 143.58116,131.69778 c -0.65158,0 -1.18145,-0.52987 -1.18145,-1.18145 0,-0.65123 0.52669,-1.1811 1.17475,-1.1811 l 0.0141,-7.1e-4 c 0.03,-0.002 3.12667,-0.21943 4.15219,-0.21943 1.03082,0 4.11551,0.21696 4.14338,0.21908 l 0.008,7e-4 h 0.008 c 0.65158,0 1.18145,0.53023 1.18145,1.18146 0,0.65158 -0.52987,1.18145 -1.18145,1.18145 h -8.31921"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path412" />
|
||||
<path
|
||||
d="m 150.71891,130.51633 c 0,-0.65229 0.52882,-1.18146 1.18146,-1.18146 0.65263,0 1.18145,0.52917 1.18145,1.18146 0,0.65263 -0.52882,1.18145 -1.18145,1.18145 -0.65264,0 -1.18146,-0.52882 -1.18146,-1.18145"
|
||||
style="fill:#b8bbb6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path414" />
|
||||
<path
|
||||
d="m 151.90037,131.19507 c 0.37429,0 0.67874,-0.30445 0.67874,-0.67874 0,-0.3743 -0.30445,-0.67875 -0.67874,-0.67875 -0.3743,0 -0.67875,0.30445 -0.67875,0.67875 0,0.37429 0.30445,0.67874 0.67875,0.67874"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path416" />
|
||||
<path
|
||||
d="m 145.53555,129.58146 c 0,-0.19014 0.15416,-0.34431 0.34431,-0.34431 0.19015,0 0.34431,0.15417 0.34431,0.34431 0,0.19015 -0.15416,0.34432 -0.34431,0.34432 -0.19015,0 -0.34431,-0.15417 -0.34431,-0.34432"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path418" />
|
||||
<path
|
||||
d="m 145.54331,131.35311 c 0,-0.19014 0.15416,-0.34431 0.34431,-0.34431 0.19015,0 0.34396,0.15417 0.34396,0.34431 0,0.19015 -0.15381,0.34432 -0.34396,0.34432 -0.19015,0 -0.34431,-0.15417 -0.34431,-0.34432"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path420" />
|
||||
<path
|
||||
d="m 149.25735,131.35311 c 0,-0.19014 0.15417,-0.34431 0.34432,-0.34431 0.19014,0 0.34431,0.15417 0.34431,0.34431 0,0.19015 -0.15417,0.34432 -0.34431,0.34432 -0.19015,0 -0.34432,-0.15417 -0.34432,-0.34432"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path422" />
|
||||
<path
|
||||
d="m 149.94598,129.58146 c 0,-0.19014 -0.15417,-0.34431 -0.34431,-0.34431 -0.19015,0 -0.34432,0.15417 -0.34432,0.34431 0,0.19015 0.15417,0.34432 0.34432,0.34432 0.19014,0 0.34431,-0.15417 0.34431,-0.34432"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path424" />
|
||||
<path
|
||||
d="m 144.76261,130.51633 c 0,-0.65229 -0.52881,-1.18146 -1.18145,-1.18146 -0.65264,0 -1.18145,0.52917 -1.18145,1.18146 0,0.65263 0.52881,1.18145 1.18145,1.18145 0.65264,0 1.18145,-0.52882 1.18145,-1.18145"
|
||||
style="fill:#b8bbb6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path426" />
|
||||
<path
|
||||
d="m 143.58116,131.19507 c 0.3743,0 0.67874,-0.30445 0.67874,-0.67874 0,-0.3743 -0.30444,-0.67875 -0.67874,-0.67875 -0.3743,0 -0.67874,0.30445 -0.67874,0.67875 0,0.37429 0.30444,0.67874 0.67874,0.67874"
|
||||
style="fill:#252529;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path428" />
|
||||
<path
|
||||
d="m 143.43652,131.0088 h 8.60848 v -0.98495 h -8.60848 v 0.98495"
|
||||
style="fill:#f9a727;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path430" />
|
||||
<path
|
||||
d="m 147.23664,128.61168 h 3.55812 c 0.33761,0 0.61172,-0.27376 0.61172,-0.61172 v -0.73236 c 0,-0.72073 0.11465,-2.63737 -0.76377,-3.69429 -0.0744,-0.0896 -0.18485,-0.14147 -0.30092,-0.14147 h -2.60173 c -0.16158,0 -0.29704,0.12277 -0.31256,0.28364 l -0.19086,1.97943 v 2.91677"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path432" />
|
||||
<path
|
||||
d="m 150.76301,128.2649 c 0.16969,0 0.30797,-0.13794 0.30797,-0.30762 v -0.7165 c 0,-0.066 7.1e-4,-0.14217 0.002,-0.22648 0.01,-0.76165 0.03,-2.34562 -0.68192,-3.20216 -0.0173,-0.0212 -0.0437,-0.0335 -0.0709,-0.0335 h -2.54529 c -0.008,0 -0.0155,0.006 -0.0162,0.0148 l -0.16404,1.69827 c -0.0141,0.14464 0.025,0.28928 0.10971,0.40711 l 1.52153,2.11702 c 0.11219,0.15628 0.29316,0.24906 0.48578,0.24906 h 1.05163"
|
||||
style="fill:#4c5462;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path434" />
|
||||
<path
|
||||
d="m 141.47331,125.91928 h 2.36891 c 0.0723,0 0.13052,0.0924 0.13052,0.20637 h -0.13052 -0.13053 -2.10785 -0.13053 -0.13053 c 0,-0.11394 0.0582,-0.20637 0.13053,-0.20637"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path436" />
|
||||
<path
|
||||
d="m 141.47331,126.34649 h 2.36891 c 0.0723,0 0.13052,0.0924 0.13052,0.20673 h -0.13052 -0.13053 -2.10785 -0.13053 -0.13053 c 0,-0.1143 0.0582,-0.20673 0.13053,-0.20673"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path438" />
|
||||
<path
|
||||
d="m 141.47331,126.77406 h 2.36891 c 0.0723,0 0.13052,0.0924 0.13052,0.20637 h -0.13052 -0.13053 -2.10785 -0.13053 -0.13053 c 0,-0.11394 0.0582,-0.20637 0.13053,-0.20637"
|
||||
style="fill:#e4731a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path440" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 17 KiB |
99
tmp/index.html
Normal file
99
tmp/index.html
Normal file
@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Under Construction</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: #f2f2f2;
|
||||
padding: 1rem;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
#hero {
|
||||
padding: 4rem;
|
||||
background-color: white;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#hero img {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: sans-serif;
|
||||
margin-bottom: 2rem;
|
||||
margin-top: 2rem;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/3.0.4/luxon.min.js" integrity="sha512-XdACFfCJeqqfVU8mvvXReyFR130qjFvfv/PZOFGwVyBz0HC+57fNkSacMPF2Dyek5jqi4D7ykFrx/T7N6F2hwQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<p style="font-size:2.5rem;color:#505050">Coming Soon™</p>
|
||||
<div id="hero">
|
||||
<img src="/crane.svg">
|
||||
</div>
|
||||
<p>
|
||||
Under Construction for <br />
|
||||
<span id="counter" style="margin-top:0.5rem"></span>
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
function u(v, unit) {
|
||||
if (v === 1) {
|
||||
return `${v} ${unit}`;
|
||||
}
|
||||
else {
|
||||
return `${v} ${unit}s`;
|
||||
}
|
||||
}
|
||||
|
||||
function f(n) {
|
||||
let s = n.toString();
|
||||
if (s.length == 1) {
|
||||
return '0' + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
const start = luxon.DateTime.fromSeconds(1634529923);
|
||||
function setDuration() {
|
||||
var diff = luxon.DateTime.now().diff(start);
|
||||
const years = Math.floor(diff.as('years'));
|
||||
diff = diff.minus(luxon.Duration.fromObject({years}));
|
||||
const months = Math.floor(diff.as('months'));
|
||||
diff = diff.minus(luxon.Duration.fromObject({months}));
|
||||
const days = Math.floor(diff.as('days'));
|
||||
diff = diff.minus(luxon.Duration.fromObject({days}));
|
||||
const hours = Math.floor(diff.as('hours'))
|
||||
diff = diff.minus(luxon.Duration.fromObject({hours}));
|
||||
const minutes = Math.floor(diff.as('minutes'));
|
||||
diff = diff.minus(luxon.Duration.fromObject({minutes}));
|
||||
const seconds = Math.floor(diff.as('seconds'));
|
||||
diff = diff.minus(luxon.Duration.fromObject({seconds}));
|
||||
const millis = diff.as('milliseconds');
|
||||
|
||||
const timeString = `${u(years, "year")}, ${u(months, "month")}, ${u(days, "day")}, ${f(hours)}:${f(minutes)}:${f(seconds)}.${Math.floor(millis / 100)}`;
|
||||
document.getElementById('counter').innerHTML = timeString;
|
||||
|
||||
window.setTimeout(setDuration, 10);
|
||||
}
|
||||
setDuration();
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
});
|
Reference in New Issue
Block a user