aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-03-12 15:04:31 +0100
committerLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-03-12 15:04:31 +0100
commit114dc8c8549a4a1271d43c42c7931209b5a9f5b5 (patch)
tree4646c7ec8ccfab89d75ee9d7b464f87bc6d457cf /src
parent8b2de4edf7a2b7ae5c4bcf6aa0b63313da80b46d (diff)
downloadmem-estrogen-zone-114dc8c8549a4a1271d43c42c7931209b5a9f5b5.tar.gz
mem-estrogen-zone-114dc8c8549a4a1271d43c42c7931209b5a9f5b5.tar.bz2
mem-estrogen-zone-114dc8c8549a4a1271d43c42c7931209b5a9f5b5.tar.lz
mem-estrogen-zone-114dc8c8549a4a1271d43c42c7931209b5a9f5b5.zip

feat: properly handle skip-animation

Diffstat (limited to 'src')
-rw-r--r--src/routes/+page.svelte417
-rw-r--r--src/routes/IndexPage.svelte462
-rw-r--r--src/routes/anim-gen.ts223
-rw-r--r--src/routes/skip-animation/+page.svelte3
4 files changed, 584 insertions, 521 deletions
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index b5c8d91..f70592b 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -12,421 +12,10 @@
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-->
+
<script lang="ts">
- import logo from './boot-logo.webp';
import './anim.css';
- import { architecture, hostname, tty, versions } from './distro-info';
- import { login, ttyLines, type RenderBlock, type TTYText } from './shared';
- import { onDestroy, onMount } from 'svelte';
- import { base } from '$app/paths';
-
- const initTagLine = `line flex flex-row flex-wrap gap-3 justify-between`;
-
- const fsckFileCount = Math.floor(Math.random() * 32768 + 6732);
- const fsckTotalCount = Math.round(
- (fsckFileCount / 6732) * 753664 + Math.random() * 10000,
- );
- const blocksTotal = Math.floor(Math.random() * 8000000 + 4000000);
- const blocksCount = Math.floor(Math.random() * 600000 + 200000);
-
- const lanLeastSignificantIPPart = Math.floor(Math.random() * 253 + 2);
-
- let {
- skipAnimation = false,
- }: {
- skipAnimation?: boolean;
- } = $props();
- onMount(() => {
- if (skipAnimation && location.pathname !== base + '/skip-animation')
- history.replaceState({}, '', base + '/skip-animation');
- });
-
- let isScripted = $state(false);
- onMount(() => (isScripted = true));
- onDestroy(() => (isScripted = false));
+ import IndexPage from './IndexPage.svelte';
</script>
-<svelte:head>
- <title>/~mem/</title>
- <meta name="description" content="memdmp's pure-css homepage demo thing" />
-</svelte:head>
-
-{#snippet tagLeft(colour: string)}
- <span class="text-[{colour}]">*</span>
-{/snippet}
-{#snippet okTagLeft()}
- {@render tagLeft('#51f051')}
-{/snippet}
-{#snippet tagRight(innerText: string, innerColour: string)}
- <span class="text-[#5f5fff]">[</span>
- <span class="text-[{innerColour}]">{innerText}</span>
- <span class="text-[#5f5fff]">]</span>
-{/snippet}
-{#snippet okTagRight()}
- {@render tagRight('ok', '#51f051')}
-{/snippet}
-{#snippet okLine(text: string, classes?: string, rightClass?: string)}
- <div class="{initTagLine}{classes ? ' ' + classes : ''}">
- <div class="left">
- {@render okTagLeft()}
- {text}
- </div>
- <div class="right{rightClass ? ' ' + rightClass : ''}">
- {@render okTagRight()}
- </div>
- </div>
-{/snippet}
-{#snippet ttyTextInnerRenderer(section: RenderBlock)}
- <span
- class="ttytext-block {section.colour
- ? ` text-[${section.colour}]`
- : ''}{section.bg ? ` bg-[${section.bg}]` : ''} {section.weight
- ? ` ${['', 'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold', 'font-black'][section.weight / 100]}`
- : ''}{typeof section.italic === 'undefined'
- ? ''
- : section.italic
- ? ' italic'
- : ' not-italic'}{typeof section.underlined === 'undefined'
- ? ''
- : section.underlined
- ? ` underline`
- : ' no-underline'} inline"
- >{#if section.raw}{@html section.value}{:else}{#each section.value
- .split('\n')
- .map( (l, i, a) => (i === a.length - 1 ? [0, l] : [1, l]), ) as [nl, l]}{l}{#if nl}<br
- />{/if}{/each}{/if}</span
- >
-{/snippet}
-{#snippet ttyTextMiddleRenderer(
- section: RenderBlock,
- line: TTYText & { kind: 'text' },
-)}
- {#if section.url}
- {#if typeof section.url === 'string'}
- {#if section.url.startsWith('newtab:')}
- {@const url = section.url.substring(7)}
- <a
- href={url.startsWith('/') ? base + url : url}
- target="_blank"
- rel="noopener noreferrer"
- class="no-underline text-inherit"
- download={section.dl}>{@render ttyTextInnerRenderer(section)}</a
- >
- {:else if section.url.startsWith('currenttab:')}
- {@const url = section.url.substring(11)}
- <a
- href={url.startsWith('/') ? base + url : url}
- class="no-underline text-inherit"
- download={section.dl}>{@render ttyTextInnerRenderer(section)}</a
- >
- {:else}
- ERR: Unknown Link Format
- {/if}
- {:else}
- <span
- onclick={() =>
- typeof section.url !== 'function' ? void 0 : section.url(line)}
- onkeypress={() =>
- typeof section.url !== 'function' ? void 0 : section.url(line)}
- role="link"
- tabindex="0">{@render ttyTextInnerRenderer(section)}</span
- >
- {/if}
- {:else}
- {@render ttyTextInnerRenderer(section)}
- {/if}
-{/snippet}
-{#snippet ttyText(line: TTYText)}
- {#if line.kind === 'text'}
- {#if (line.renderrestriction ?? 'everywhere') === 'everywhere' || line.renderrestriction === 'noscript' || (line.renderrestriction === 'js-only' && isScripted)}
- {#if line.renderrestriction === 'noscript'}
- <noscript class="inline-block max-w-[100%] {line.classes.join(' ')}"
- >{#each line.value as v}{@render ttyTextMiddleRenderer(
- v,
- line,
- )}{/each}</noscript
- >
- {:else}
- <span class="inline-block max-w-[100%] {line.classes.join(' ')}"
- >{#each line.value as v}{@render ttyTextMiddleRenderer(
- v,
- line,
- )}{/each}</span
- >
- {/if}
- {/if}
- {/if}
-{/snippet}
-
-<div class="hidden">
- {#each ['inline-block', 'block', 'flex', 'inline-flex', 'max-w-full', 'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold', 'font-black'] as c}
- <span class={c}></span>
- {/each}
-</div>
-
-<div
- class="fixed top-0 left-0 w-screen h-screen font-mono"
- id="app"
- class:skip-animation={skipAnimation}
->
- <div
- id="bios"
- class="flex flex-col items-center justify-between relative font-bios text-lg"
- >
- <div class="top flex items-center justify-center flex-col">
- <div class="inner text-[#999999]">
- <p class="leading-4 mt-8 -mb-8 max-w-[36rem]">
- <span class="bdsdxe-load"
- >BdsDxe: loading Boot0002 "UEFI Misc Device" from PciRoot
- (0x0)/Pci(0x2,0x4)/Pci(0x0,0x0)</span
- ><br />
- <span class="bdsdxe-start"
- >BdsDxe: starting Boot0002 "UEFI Misc Device" from PciRoot
- (0x0)/Pci(0x2,0x4)/Pci(0x0,0x0)</span
- >
- </p>
- </div>
- </div>
- <div class="middle">
- <img
- src={logo}
- alt="Bootloader Logo"
- class="w-32 h-32 flex items-center justify-center"
- />
- </div>
- <div
- class="bottom flex flex-col items-stretch justify-center text-center gap-0.5 w-full pb-4"
- >
- <div class="start-text">Start boot option</div>
- <div
- class="bar h-4"
- style={`background-image: url("data:image/svg+xml,${encodeURIComponent(`<svg width="32" height="32" viewbox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
- <rect x="1" y="1" width="30" height="30" fill="white" stroke="black" stroke-width="2"/>
-</svg>
-`)}");background-size:contain;`}
- ></div>
- </div>
- </div>
- <div id="grub" class="font-grub text-sm">
- <div
- class="relative p-4 flex items-stretch justify-stretch flex-col w-screen h-screen"
- >
- <p class="title text-[#a2a2a2] mb-4 h-4 text-center">
- GNU GRUB &nbsp;version 2.12
- </p>
- <div
- class="outer-border border-2 border-[#a8a8a8] border-solid h-full px-1 py-2 flex flex-col items-stretch"
- >
- <div class="entry bg-[#a8a8a8] text-[#000] text-left">
- *Alpine Linux {versions.alpine.number}, with {versions.kernel
- .humanReadable}
- </div>
- <div class="entry text-[#a8a8a8] text-left">
- &nbsp;UEFI Firmware Setup
- </div>
- </div>
- <p class="footer text-[#a2a2a2] my-4 text-left pl-2">
- <span class="block pl-6">
- Use the ↑ and ↓ keys to select which entry is highlighted.<br />
- Press enter to boot the selected OS, `e' to edit the commands before booting
- or `c' for a command-line.<br />
- </span>
- The highlighted entry will be executed automatically in
- <span class="grub-2s inline-block">2</span><span
- class="grub-1s inline-block">1</span
- >s.
- </p>
- </div>
- </div>
- <div id="grub-term" class="font-grub text-sm text-left text-[#a2a2a2]">
- <p class="p-8">
- &nbsp;&nbsp;Booting `Alpine Linux {versions.alpine.number}, with {versions
- .kernel.humanReadable}'<br /><br /><span class="load-kernel"
- >Loading {versions.kernel.humanReadable} ...</span
- ><br /><span class="load-ramdisk">Loading initial ramdisk ...</span>
- </p>
- </div>
- <!--
- flex col reverse and mb auto are hacks for it to auto scroll to bottom(!)
- flex col reverse only works due to nested inner (which would be at bottom of screen, if it wasnt for mb-auto div above it)
- -->
- <div id="openrc" class="overflow-y-auto flex flex-col-reverse">
- <div class="mb-auto"></div>
- <div class="font-mono leading-4 text-[#b9b9b9] flex flex-col p-4">
- <div class="line">
- &nbsp;&nbsp;<span class="text-[#51f051]">OpenRC</span>
- <span class="text-[#4bdfdf]">{versions.openrc}</span>
- is starting up Linux
- <span class="text-[#5f5fff]">{versions.kernel.id} ({architecture})</span
- >
- </div>
- <div class="line">&ZeroWidthSpace;</div>
- {@render okLine('/proc/ is already mounted')}
- {@render okLine('Mounting /run ...')}
- {@render okLine('/run/openrc: creating directory')}
- {@render okLine('/run/lock: creating directory', 'openrc-boot-step-1')}
- {@render okLine('/run/lock: correcting owner', 'openrc-boot-step-1')}
- {@render okLine('Caching service dependencies ...', 'openrc-boot-step-1')}
- {@render okLine('Remounting devtmpfs on /dev ...', 'openrc-boot-step-2')}
- {@render okLine('Mounting /dev/mqueue ...', 'openrc-boot-step-3')}
- {@render okLine('Mounting security filesystem ...', 'openrc-boot-step-4')}
- {@render okLine('Mounting debug filesystem ...', 'openrc-boot-step-5')}
- {@render okLine(
- 'Mounting persistent storage (pstore) filesystem ...',
- 'openrc-boot-step-5',
- )}
- {@render okLine('Mounting efivarfs filesystem ...', 'openrc-boot-step-5')}
- {@render okLine('Starting busybox mdev ...', 'openrc-boot-step-6')}
- {@render okLine('Scanning hardware for mdev ...', 'openrc-boot-step-6')}
- {@render okLine('Loading hardware drivers ...', 'openrc-boot-step-7')}
- {@render okLine('Loading modules ...', 'openrc-boot-step-8')}
- {@render okLine(
- 'Setting system clock using the hardware clock [UTC] ...',
- 'openrc-boot-step-9',
- )}
- {@render okLine('Checking local filesystems ...', 'openrc-boot-step-10')}
- <div class="line openrc-boot-step-11">
- /dev/mapper/bepis: clean, {fsckFileCount}/{fsckTotalCount} files, {blocksCount}/{blocksTotal}
- blocks
- </div>
- {@render okLine(
- 'Remounting root filesystem read/write ...',
- 'openrc-boot-step-12',
- )}
- {@render okLine('Remounting filesystems ...', 'openrc-boot-step-13')}
- {@render okLine('Activating swap devices ...', 'openrc-boot-step-14')}
- {@render okLine('Mounting local filesystems ...', 'openrc-boot-step-15')}
- {@render okLine(
- 'Configuring kernel parameters ...',
- 'openrc-boot-step-16',
- )}
- {@render okLine('Creating user login records ...', 'openrc-boot-step-17')}
- {@render okLine('Setting hostname ...', 'openrc-boot-step-18')}
- {@render okLine('Setting keymap ...', 'openrc-boot-step-19')}
- {@render okLine('Starting networking ...', 'openrc-boot-step-20')}
- {@render okLine('\xa0\xa0lo ...', 'openrc-boot-step-21')}
- {@render okLine('\xa0\xa0eth0 ...', 'openrc-boot-step-22')}
- <div class="line openrc-boot-step-22">udhcpd: started</div>
- <div class="line openrc-boot-step-23">udhcpd: broadcasting discover</div>
- <!-- TODO: configurable subnet uwu -->
- <div class="line openrc-boot-step-24">
- udhcpd: broadcasting select for 192.168.1.{lanLeastSignificantIPPart},
- server 192.168.1.1
- </div>
- <div class="line openrc-boot-step-25">
- udhcpd: lease of 192.168.1.{lanLeastSignificantIPPart} obtained from 192.168.1.1,
- lease time 3600
- </div>
- {@render okLine(
- 'Seeding random number generator ...',
- 'openrc-boot-step-26',
- )}
- {@render okLine(
- 'Seeding 256 bits without crediting',
- 'openrc-boot-step-26',
- )}
- {@render okLine(
- 'Saving 256 bits of creditable seed for next boot',
- 'openrc-boot-step-26',
- )}
- {@render okLine('Starting logbookd ...', 'openrc-boot-step-27')}
- {@render okLine('Starting busybox acpid ...', 'openrc-boot-step-28')}
- {@render okLine('Starting busybox crond ...', 'openrc-boot-step-29')}
- {@render okLine('Starting busybox ntpd ...', 'openrc-boot-step-29')}
- <div class="line openrc-boot-step-30">&ZeroWidthSpace;</div>
- <div class="line openrc-boot-step-30">
- Welcome to Alpine Linux {versions.alpine
- .number}{#if versions.alpine.isEdge}&nbsp;(edge){/if}
- </div>
- <div class="line openrc-boot-step-30">
- Kernel {versions.kernel.humanReadable} on an {architecture} (/dev/{tty})
- </div>
- <div class="line openrc-boot-step-30">&ZeroWidthSpace;</div>
- <div class="line openrc-boot-step-30">
- {hostname} login:&nbsp;<span class="openrc-username"
- >{#each login.username
- .split('')
- .map((v, i) => [v, i] as const) as [char, idx]}<span
- class="openrc-username-char openrc-username-char-{idx} inline-block"
- >{char}</span
- >{/each}</span
- ><span class="openrc-hide-at-login-prompt-username-done"
- ><span class="openrc-username-anim"
- ><code class="hidden-after-anim">_</code></span
- ></span
- ><span class="openrc-pw-line"
- ><br />
- Password:&nbsp;<span class="openrc-password-anim"
- ><code class="hidden-after-anim">_</code></span
- ></span
- >
- </div>
- <!-- TODO: Animate login and launching a tui here (for js enjoyers) or a minimal terminal after clearing the screen, and a no js screen alongside a plaintext version of the thing (for schizo noscript lovers) -->
- <div class="hidden-after-anim">
- <span class="openrc-hide-at-last-boot-step inline-block">
- <pre class="flashing-cursor">_</pre>
- </span>
- </div>
- <div class="line ttylines-openrc font-mono text-[#070505] flex flex-col">
- {#each ttyLines.filter(((maxidx) => (_, i) => i < (maxidx === -1 ? Infinity : maxidx))(ttyLines.findIndex((v) => v.kind === 'clear'))) as line}
- {@render ttyText(line)}
- {/each}
- </div>
- </div>
- </div>
- {#if ttyLines.find((v) => v.kind === 'clear')}
- {#each ttyLines
- .map((v, i) => [v, i] as const)
- .filter(([v]) => v.kind === 'clear')
- .map((v, i, a) => [v[1], a[i + 1]?.[1], i]) as [idx, nextIdx, clearIdx]}
- <div
- id="tty-{clearIdx}"
- class="overflow-y-auto max-h-screen flex flex-col-reverse"
- >
- <div class="mb-auto"></div>
- <div class="font-mono leading-4 text-[#070505] flex flex-col p-4">
- {#each ttyLines.filter((_, i) => i >= idx && i < (nextIdx ?? Infinity)) as line}
- {@render ttyText(line)}
- {/each}
- </div>
- </div>
- {/each}
- {/if}
-</div>
-
-<style>
- @keyframes flashing-cursor {
- 0% {
- opacity: 0;
- }
- 49% {
- opacity: 0;
- }
- 50% {
- opacity: 1;
- }
- 99% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- }
- }
- .flashing-cursor {
- animation-name: flashing-cursor;
- animation-duration: 0.4s;
- animation-iteration-count: infinite;
- animation-timing-function: linear;
- }
- .hidden-after-anim {
- margin-top: -99999vh;
- margin-bottom: 99999vh;
- margin-left: -99999vw;
- margin-right: 99999vw;
- transform: scaleX(0);
- opacity: 0;
- }
- .ttytext-block {
- white-space-collapse: preserve-spaces;
- }
-</style>
+<IndexPage />
diff --git a/src/routes/IndexPage.svelte b/src/routes/IndexPage.svelte
new file mode 100644
index 0000000..8a56f08
--- /dev/null
+++ b/src/routes/IndexPage.svelte
@@ -0,0 +1,462 @@
+<!--
+/**
+ * @license AGPL-3.0-OR-LATER
+ * @copyright 2024 memdmp
+ *
+ * Copyright (C) 2024 memdmp
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ -->
+<script lang="ts">
+ import logo from './boot-logo.webp';
+ import { architecture, hostname, tty, versions } from './distro-info';
+ import { login, ttyLines, type RenderBlock, type TTYText } from './shared';
+ import { onDestroy, onMount } from 'svelte';
+ import { base } from '$app/paths';
+
+ const initTagLine = `line flex flex-row flex-wrap gap-3 justify-between`;
+
+ const fsckFileCount = Math.floor(Math.random() * 32768 + 6732);
+ const fsckTotalCount = Math.round(
+ (fsckFileCount / 6732) * 753664 + Math.random() * 10000,
+ );
+ const blocksTotal = Math.floor(Math.random() * 8000000 + 4000000);
+ const blocksCount = Math.floor(Math.random() * 600000 + 200000);
+
+ const lanLeastSignificantIPPart = Math.floor(Math.random() * 253 + 2);
+
+ let {
+ skipAnimation = false,
+ }: {
+ skipAnimation?: boolean;
+ } = $props();
+ onMount(() => {
+ if (skipAnimation && location.pathname !== base + '/skip-animation')
+ history.replaceState({}, '', base + '/skip-animation');
+ });
+
+ let isScripted = $state(false);
+ onMount(() => (isScripted = true));
+ onDestroy(() => (isScripted = false));
+</script>
+
+<svelte:head>
+ <title>/~mem/</title>
+ <meta name="description" content="memdmp's pure-css homepage demo thing" />
+</svelte:head>
+
+{#snippet tagLeft(colour: string)}
+ <span class="text-[{colour}]">*</span>
+{/snippet}
+{#snippet okTagLeft()}
+ {@render tagLeft('#51f051')}
+{/snippet}
+{#snippet tagRight(innerText: string, innerColour: string)}
+ <span class="text-[#5f5fff]">[</span>
+ <span class="text-[{innerColour}]">{innerText}</span>
+ <span class="text-[#5f5fff]">]</span>
+{/snippet}
+{#snippet okTagRight()}
+ {@render tagRight('ok', '#51f051')}
+{/snippet}
+{#snippet okLine(text: string, classes?: string, rightClass?: string)}
+ <div class="{initTagLine}{classes ? ' ' + classes : ''}">
+ <div class="left">
+ {@render okTagLeft()}
+ {text}
+ </div>
+ <div class="right{rightClass ? ' ' + rightClass : ''}">
+ {@render okTagRight()}
+ </div>
+ </div>
+{/snippet}
+{#snippet ttyTextInnerRenderer(section: RenderBlock)}
+ <span
+ class="ttytext-block {section.colour
+ ? ` text-[${section.colour}]`
+ : ''}{section.bg ? ` bg-[${section.bg}]` : ''} {section.weight
+ ? ` ${['', 'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold', 'font-black'][section.weight / 100]}`
+ : ''}{typeof section.italic === 'undefined'
+ ? ''
+ : section.italic
+ ? ' italic'
+ : ' not-italic'}{typeof section.underlined === 'undefined'
+ ? ''
+ : section.underlined
+ ? ` underline`
+ : ' no-underline'} inline"
+ >{#if section.raw}{@html section.value}{:else}{#each section.value
+ .split('\n')
+ .map( (l, i, a) => (i === a.length - 1 ? [0, l] : [1, l]), ) as [nl, l]}{l}{#if nl}<br
+ />{/if}{/each}{/if}</span
+ >
+{/snippet}
+{#snippet ttyTextMiddleRenderer(
+ section: RenderBlock,
+ line: TTYText & { kind: 'text' },
+)}
+ {#if section.url}
+ {#if typeof section.url === 'string'}
+ {#if section.url.startsWith('newtab:')}
+ {@const url = section.url.substring(7)}
+ <a
+ href={url.startsWith('/') ? base + url : url}
+ target="_blank"
+ rel="noopener noreferrer"
+ class="no-underline text-inherit"
+ download={section.dl}>{@render ttyTextInnerRenderer(section)}</a
+ >
+ {:else if section.url.startsWith('currenttab:')}
+ {@const url = section.url.substring(11)}
+ <a
+ href={url.startsWith('/') ? base + url : url}
+ class="no-underline text-inherit"
+ download={section.dl}>{@render ttyTextInnerRenderer(section)}</a
+ >
+ {:else}
+ ERR: Unknown Link Format
+ {/if}
+ {:else}
+ <span
+ onclick={() =>
+ typeof section.url !== 'function' ? void 0 : section.url(line)}
+ onkeypress={() =>
+ typeof section.url !== 'function' ? void 0 : section.url(line)}
+ role="link"
+ tabindex="0">{@render ttyTextInnerRenderer(section)}</span
+ >
+ {/if}
+ {:else}
+ {@render ttyTextInnerRenderer(section)}
+ {/if}
+{/snippet}
+{#snippet ttyText(line: TTYText)}
+ {#if line.kind === 'text'}
+ {#if (line.renderrestriction ?? 'everywhere') === 'everywhere' || line.renderrestriction === 'noscript' || (line.renderrestriction === 'js-only' && isScripted)}
+ {#if line.renderrestriction === 'noscript'}
+ <noscript class="inline-block max-w-[100%] {line.classes.join(' ')}"
+ >{#each line.value as v}{@render ttyTextMiddleRenderer(
+ v,
+ line,
+ )}{/each}</noscript
+ >
+ {:else}
+ <span class="inline-block max-w-[100%] {line.classes.join(' ')}"
+ >{#each line.value as v}{@render ttyTextMiddleRenderer(
+ v,
+ line,
+ )}{/each}</span
+ >
+ {/if}
+ {/if}
+ {/if}
+{/snippet}
+
+<div class="hidden">
+ {#each ['inline-block', 'block', 'flex', 'inline-flex', 'max-w-full', 'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold', 'font-black'] as c}
+ <span class={c}></span>
+ {/each}
+</div>
+
+<div
+ class="fixed top-0 left-0 w-screen h-screen font-mono"
+ id="app"
+ class:skip-animation={skipAnimation}
+>
+ {#if !skipAnimation}
+ <div
+ id="bios"
+ class="flex flex-col items-center justify-between relative font-bios text-lg"
+ >
+ <div class="top flex items-center justify-center flex-col">
+ <div class="inner text-[#999999]">
+ <p class="leading-4 mt-8 -mb-8 max-w-[36rem]">
+ <span class="bdsdxe-load"
+ >BdsDxe: loading Boot0002 "UEFI Misc Device" from PciRoot
+ (0x0)/Pci(0x2,0x4)/Pci(0x0,0x0)</span
+ ><br />
+ <span class="bdsdxe-start"
+ >BdsDxe: starting Boot0002 "UEFI Misc Device" from PciRoot
+ (0x0)/Pci(0x2,0x4)/Pci(0x0,0x0)</span
+ >
+ </p>
+ </div>
+ </div>
+ <div class="middle">
+ <img
+ src={logo}
+ alt="Bootloader Logo"
+ class="w-32 h-32 flex items-center justify-center"
+ />
+ </div>
+ <div
+ class="bottom flex flex-col items-stretch justify-center text-center gap-0.5 w-full pb-4"
+ >
+ <div class="start-text">Start boot option</div>
+ <div
+ class="bar h-4"
+ style={`background-image: url("data:image/svg+xml,${encodeURIComponent(`<svg width="32" height="32" viewbox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
+ <rect x="1" y="1" width="30" height="30" fill="white" stroke="black" stroke-width="2"/>
+</svg>
+`)}");background-size:contain;`}
+ ></div>
+ </div>
+ </div>
+ <div id="grub" class="font-grub text-sm">
+ <div
+ class="relative p-4 flex items-stretch justify-stretch flex-col w-screen h-screen"
+ >
+ <p class="title text-[#a2a2a2] mb-4 h-4 text-center">
+ GNU GRUB &nbsp;version 2.12
+ </p>
+ <div
+ class="outer-border border-2 border-[#a8a8a8] border-solid h-full px-1 py-2 flex flex-col items-stretch"
+ >
+ <div class="entry bg-[#a8a8a8] text-[#000] text-left">
+ *Alpine Linux {versions.alpine.number}, with {versions.kernel
+ .humanReadable}
+ </div>
+ <div class="entry text-[#a8a8a8] text-left">
+ &nbsp;UEFI Firmware Setup
+ </div>
+ </div>
+ <p class="footer text-[#a2a2a2] my-4 text-left pl-2">
+ <span class="block pl-6">
+ Use the ↑ and ↓ keys to select which entry is highlighted.<br />
+ Press enter to boot the selected OS, `e' to edit the commands before
+ booting or `c' for a command-line.<br />
+ </span>
+ The highlighted entry will be executed automatically in
+ <span class="grub-2s inline-block">2</span><span
+ class="grub-1s inline-block">1</span
+ >s.
+ </p>
+ </div>
+ </div>
+ <div id="grub-term" class="font-grub text-sm text-left text-[#a2a2a2]">
+ <p class="p-8">
+ &nbsp;&nbsp;Booting `Alpine Linux {versions.alpine.number}, with {versions
+ .kernel.humanReadable}'<br /><br /><span class="load-kernel"
+ >Loading {versions.kernel.humanReadable} ...</span
+ ><br /><span class="load-ramdisk">Loading initial ramdisk ...</span>
+ </p>
+ </div>
+ <!--
+ flex col reverse and mb auto are hacks for it to auto scroll to bottom(!)
+ flex col reverse only works due to nested inner (which would be at bottom of screen, if it wasnt for mb-auto div above it)
+ -->
+ <div id="openrc" class="overflow-y-auto flex flex-col-reverse">
+ <div class="mb-auto"></div>
+ <div class="font-mono leading-4 text-[#b9b9b9] flex flex-col p-4">
+ <div class="line">
+ &nbsp;&nbsp;<span class="text-[#51f051]">OpenRC</span>
+ <span class="text-[#4bdfdf]">{versions.openrc}</span>
+ is starting up Linux
+ <span class="text-[#5f5fff]"
+ >{versions.kernel.id} ({architecture})</span
+ >
+ </div>
+ <div class="line">&ZeroWidthSpace;</div>
+ {@render okLine('/proc/ is already mounted')}
+ {@render okLine('Mounting /run ...')}
+ {@render okLine('/run/openrc: creating directory')}
+ {@render okLine('/run/lock: creating directory', 'openrc-boot-step-1')}
+ {@render okLine('/run/lock: correcting owner', 'openrc-boot-step-1')}
+ {@render okLine(
+ 'Caching service dependencies ...',
+ 'openrc-boot-step-1',
+ )}
+ {@render okLine(
+ 'Remounting devtmpfs on /dev ...',
+ 'openrc-boot-step-2',
+ )}
+ {@render okLine('Mounting /dev/mqueue ...', 'openrc-boot-step-3')}
+ {@render okLine(
+ 'Mounting security filesystem ...',
+ 'openrc-boot-step-4',
+ )}
+ {@render okLine('Mounting debug filesystem ...', 'openrc-boot-step-5')}
+ {@render okLine(
+ 'Mounting persistent storage (pstore) filesystem ...',
+ 'openrc-boot-step-5',
+ )}
+ {@render okLine(
+ 'Mounting efivarfs filesystem ...',
+ 'openrc-boot-step-5',
+ )}
+ {@render okLine('Starting busybox mdev ...', 'openrc-boot-step-6')}
+ {@render okLine('Scanning hardware for mdev ...', 'openrc-boot-step-6')}
+ {@render okLine('Loading hardware drivers ...', 'openrc-boot-step-7')}
+ {@render okLine('Loading modules ...', 'openrc-boot-step-8')}
+ {@render okLine(
+ 'Setting system clock using the hardware clock [UTC] ...',
+ 'openrc-boot-step-9',
+ )}
+ {@render okLine(
+ 'Checking local filesystems ...',
+ 'openrc-boot-step-10',
+ )}
+ <div class="line openrc-boot-step-11">
+ /dev/mapper/bepis: clean, {fsckFileCount}/{fsckTotalCount} files, {blocksCount}/{blocksTotal}
+ blocks
+ </div>
+ {@render okLine(
+ 'Remounting root filesystem read/write ...',
+ 'openrc-boot-step-12',
+ )}
+ {@render okLine('Remounting filesystems ...', 'openrc-boot-step-13')}
+ {@render okLine('Activating swap devices ...', 'openrc-boot-step-14')}
+ {@render okLine(
+ 'Mounting local filesystems ...',
+ 'openrc-boot-step-15',
+ )}
+ {@render okLine(
+ 'Configuring kernel parameters ...',
+ 'openrc-boot-step-16',
+ )}
+ {@render okLine(
+ 'Creating user login records ...',
+ 'openrc-boot-step-17',
+ )}
+ {@render okLine('Setting hostname ...', 'openrc-boot-step-18')}
+ {@render okLine('Setting keymap ...', 'openrc-boot-step-19')}
+ {@render okLine('Starting networking ...', 'openrc-boot-step-20')}
+ {@render okLine('\xa0\xa0lo ...', 'openrc-boot-step-21')}
+ {@render okLine('\xa0\xa0eth0 ...', 'openrc-boot-step-22')}
+ <div class="line openrc-boot-step-22">udhcpd: started</div>
+ <div class="line openrc-boot-step-23">
+ udhcpd: broadcasting discover
+ </div>
+ <!-- TODO: configurable subnet uwu -->
+ <div class="line openrc-boot-step-24">
+ udhcpd: broadcasting select for 192.168.1.{lanLeastSignificantIPPart},
+ server 192.168.1.1
+ </div>
+ <div class="line openrc-boot-step-25">
+ udhcpd: lease of 192.168.1.{lanLeastSignificantIPPart} obtained from 192.168.1.1,
+ lease time 3600
+ </div>
+ {@render okLine(
+ 'Seeding random number generator ...',
+ 'openrc-boot-step-26',
+ )}
+ {@render okLine(
+ 'Seeding 256 bits without crediting',
+ 'openrc-boot-step-26',
+ )}
+ {@render okLine(
+ 'Saving 256 bits of creditable seed for next boot',
+ 'openrc-boot-step-26',
+ )}
+ {@render okLine('Starting logbookd ...', 'openrc-boot-step-27')}
+ {@render okLine('Starting busybox acpid ...', 'openrc-boot-step-28')}
+ {@render okLine('Starting busybox crond ...', 'openrc-boot-step-29')}
+ {@render okLine('Starting busybox ntpd ...', 'openrc-boot-step-29')}
+ <div class="line openrc-boot-step-30">&ZeroWidthSpace;</div>
+ <div class="line openrc-boot-step-30">
+ Welcome to Alpine Linux {versions.alpine
+ .number}{#if versions.alpine.isEdge}&nbsp;(edge){/if}
+ </div>
+ <div class="line openrc-boot-step-30">
+ Kernel {versions.kernel.humanReadable} on an {architecture} (/dev/{tty})
+ </div>
+ <div class="line openrc-boot-step-30">&ZeroWidthSpace;</div>
+ <div class="line openrc-boot-step-30">
+ {hostname} login:&nbsp;<span class="openrc-username"
+ >{#each login.username
+ .split('')
+ .map((v, i) => [v, i] as const) as [char, idx]}<span
+ class="openrc-username-char openrc-username-char-{idx} inline-block"
+ >{char}</span
+ >{/each}</span
+ ><span class="openrc-hide-at-login-prompt-username-done"
+ ><span class="openrc-username-anim"
+ ><code class="hidden-after-anim">_</code></span
+ ></span
+ ><span class="openrc-pw-line"
+ ><br />
+ Password:&nbsp;<span class="openrc-password-anim"
+ ><code class="hidden-after-anim">_</code></span
+ ></span
+ >
+ </div>
+ <!-- TODO: Animate login and launching a tui here (for js enjoyers) or a minimal terminal after clearing the screen, and a no js screen alongside a plaintext version of the thing (for schizo noscript lovers) -->
+ <div class="hidden-after-anim">
+ <span class="openrc-hide-at-last-boot-step inline-block">
+ <pre class="flashing-cursor">_</pre>
+ </span>
+ </div>
+ <div
+ class="line ttylines-openrc font-mono text-[#070505] flex flex-col"
+ >
+ {#each ttyLines.filter(((maxidx) => (_, i) => i < (maxidx === -1 ? Infinity : maxidx))(ttyLines.findIndex((v) => v.kind === 'clear'))) as line}
+ {@render ttyText(line)}
+ {/each}
+ </div>
+ </div>
+ </div>
+ {/if}
+ <!-- FIXME: what is this #if? this seems broken -->
+ {#if ttyLines.find((v) => v.kind === 'clear')}
+ <!-- FIXME: this is utterly unreadable and i dont know what this does anymore -->
+ {@const arr = ttyLines
+ .map((v, i) => [v, i] as const)
+ .filter(([v]) => v.kind === 'clear')
+ .map((v, i, a) => [v[1], a[i + 1]?.[1], i] as const)}
+ {#each skipAnimation ? [arr[arr.length - 1]] : arr as [idx, nextIdx, clearIdx]}
+ <div
+ id="tty-{clearIdx}"
+ class="overflow-y-auto max-h-screen flex flex-col-reverse"
+ >
+ <div class="mb-auto"></div>
+ <div class="font-mono leading-4 text-[#070505] flex flex-col p-4">
+ {#each ttyLines.filter((_, i) => i >= idx && i < (nextIdx ?? Infinity)) as line}
+ {@render ttyText(line)}
+ {/each}
+ </div>
+ </div>
+ {/each}
+ {/if}
+</div>
+
+<style>
+ @keyframes flashing-cursor {
+ 0% {
+ opacity: 0;
+ }
+ 49% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 99% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+ }
+ .flashing-cursor {
+ animation-name: flashing-cursor;
+ animation-duration: 0.4s;
+ animation-iteration-count: infinite;
+ animation-timing-function: linear;
+ }
+ .hidden-after-anim {
+ margin-top: -99999vh;
+ margin-bottom: 99999vh;
+ margin-left: -99999vw;
+ margin-right: 99999vw;
+ transform: scaleX(0);
+ opacity: 0;
+ }
+ .ttytext-block {
+ white-space-collapse: preserve-spaces;
+ }
+</style>
diff --git a/src/routes/anim-gen.ts b/src/routes/anim-gen.ts
index 550efb7..b91e088 100644
--- a/src/routes/anim-gen.ts
+++ b/src/routes/anim-gen.ts
@@ -15,6 +15,8 @@ import {
login,
ttyLines,
} from './shared.ts';
+import fs from 'node:fs';
+import esbuild from 'esbuild';
const anim = new Animation();
let ttyCtr = 0;
const stages = [
@@ -22,13 +24,9 @@ const stages = [
anim.selector('#grub'),
anim.selector('#grub-term'),
anim.selector('#openrc'),
- ...(ttyLines.flatMap((v) =>
- v.kind === 'clear'
- ? [
- anim.selector('#tty-' + ttyCtr++),
- ]
- : []
- )),
+ ...ttyLines.flatMap((v) =>
+ v.kind === 'clear' ? [anim.selector('#tty-' + ttyCtr++)] : [],
+ ),
];
const visibleStageStyles = `margin-top: 0;
margin-bottom: 0;
@@ -69,27 +67,19 @@ height:max-content;
margin-left:0;
margin-right:0;`;
stages.forEach((v, i) =>
- v.style(i === 0 ? visibleStageStyles : hiddenStageStyles)
+ v.style(i === 0 ? visibleStageStyles : hiddenStageStyles),
);
-let currentStage: typeof stages[number];
+let currentStage: (typeof stages)[number];
const toStage = (stage: number) => {
const oldStage = currentStage;
currentStage = stages[stage];
if (typeof oldStage !== 'undefined') {
- oldStage.style(
- visibleStageStyles,
- );
- currentStage.style(
- hiddenStageStyles,
- );
+ oldStage.style(visibleStageStyles);
+ currentStage.style(hiddenStageStyles);
anim._internal_timeline.now += 1;
- oldStage.style(
- hiddenStageStyles,
- );
+ oldStage.style(hiddenStageStyles);
}
- currentStage.style(
- visibleStageStyles,
- );
+ currentStage.style(visibleStageStyles);
};
type Step = (next: () => void) => void;
@@ -98,7 +88,7 @@ const biosStepHandlers: Step[] = [
// Show BIOS
(next) => {
toStage(0);
- anim.in(1000 * 3 / 10, next);
+ anim.in((1000 * 3) / 10, next);
},
// Show Start boot option
(next) => {
@@ -109,22 +99,20 @@ const biosStepHandlers: Step[] = [
anim.in(1000, next);
},
// Show bar parts
- ...new Array(
- biosSteps,
- ).fill(
- (quantity: number, lastQuantity: number, index: number) =>
- (next: () => void) => {
- const s = anim.selector(
- `#bios .bar`,
- );
- s.style(`${index === 0 ? hiddenStyles : visibleStyles}
+ ...new Array(biosSteps)
+ .fill(
+ (quantity: number, lastQuantity: number, index: number) =>
+ (next: () => void) => {
+ const s = anim.selector(`#bios .bar`);
+ s.style(`${index === 0 ? hiddenStyles : visibleStyles}
width: ${lastQuantity * 100}vw;`);
- anim._internal_timeline.now += 1;
- s.style(`${visibleStyles}
+ anim._internal_timeline.now += 1;
+ s.style(`${visibleStyles}
width: ${quantity * 100}vw;`);
- anim.in(quantity === 1 ? 50 : biosStepInterval, next);
- },
- ).map((v, i, a) => v((i + 1) / a.length, i / a.length, i)),
+ anim.in(quantity === 1 ? 50 : biosStepInterval, next);
+ },
+ )
+ .map((v, i, a) => v((i + 1) / a.length, i / a.length, i)),
// Show bdsdxe
(next) => {
const s1 = anim.selector('#bios .bdsdxe-load');
@@ -139,7 +127,8 @@ width: ${quantity * 100}vw;`);
});
},
];
-const grubStepHandlers: Step[] = [ // Show Grub
+const grubStepHandlers: Step[] = [
+ // Show Grub
(next) => {
toStage(1);
anim.in(1000, next);
@@ -198,14 +187,10 @@ const idleTypingBar = (
if (flashingTimeCtr === typingIndicatorFlashTime) flashingTimeCtr /= 2;
anim.in(flashingTimeCtr, () => {
isHiddenInCycle = !isHiddenInCycle;
- typingBarSelector.style(
- v(),
- );
+ typingBarSelector.style(v());
});
anim.in(flashingTimeCtr - 1, () => {
- typingBarSelector.style(
- v(),
- );
+ typingBarSelector.style(v());
});
}
return v();
@@ -294,30 +279,32 @@ const openrcStepHandlers = (multi: number): Step[] => [
400,
// Time till typing:
1000,
- ].map((v) => v / multi).map((time, idx, arr) => (next: () => void) => {
- const s = anim.selector('#openrc .openrc-boot-step-' + idx);
- s.style(`${hiddenStyles}
+ ]
+ .map((v) => v / multi)
+ .map((time, idx, arr) => (next: () => void) => {
+ const s = anim.selector('#openrc .openrc-boot-step-' + idx);
+ s.style(`${hiddenStyles}
max-height: 0;`);
- if (idx === arr.length - 1) {
- anim.selector('#openrc .openrc-hide-at-last-boot-step').style(
- 'opacity:1;max-height:90vh;max-width:100vw;',
- );
- }
- anim._internal_timeline.now += 1;
- if (idx === arr.length - 1) {
- anim.selector('#openrc .openrc-hide-at-last-boot-step').style(
- 'opacity:0;max-height:0;max-width:0;',
- );
- }
- s.style(`${visibleStyles}
+ if (idx === arr.length - 1) {
+ anim
+ .selector('#openrc .openrc-hide-at-last-boot-step')
+ .style('opacity:1;max-height:90vh;max-width:100vw;');
+ }
+ anim._internal_timeline.now += 1;
+ if (idx === arr.length - 1) {
+ anim
+ .selector('#openrc .openrc-hide-at-last-boot-step')
+ .style('opacity:0;max-height:0;max-width:0;');
+ }
+ s.style(`${visibleStyles}
max-height: max-content;`);
- if (idx === arr.length - 1) {
- idleTypingBar(anim.selector('#openrc .openrc-username-anim'), time);
- }
- anim.in(time, next);
- }),
+ if (idx === arr.length - 1) {
+ idleTypingBar(anim.selector('#openrc .openrc-username-anim'), time);
+ }
+ anim.in(time, next);
+ }),
// Typing Username
- ...((() => {
+ ...(() => {
const typingBar = anim.selector('#openrc .openrc-username-anim');
return login.username.split('').map((_, i) => {
const character = anim.selector(
@@ -326,7 +313,7 @@ max-height: max-content;`);
return (next: () => void) =>
typeCharacter(typingBar, character, next, getDelay(login));
});
- })()),
+ })(),
// Hide Username Cursor
(next) => {
const s = anim.selector(
@@ -346,13 +333,13 @@ max-height: max-content;`);
next();
},
// Password Typing
- ...(new Array(login.passwordLength).fill(
- (_idx: number) => (next: () => void) => {
+ ...new Array(login.passwordLength)
+ .fill((_idx: number) => (next: () => void) => {
const s = anim.selector('#openrc .openrc-password-anim');
const characterTime = getDelay(login);
typeCharacter(s, null, next, characterTime);
- },
- ).map((v, i) => v(i))),
+ })
+ .map((v, i) => v(i)),
(next) => {
const s = anim.selector('#openrc .openrc-password-anim');
s.style('opacity:0;transform:scaleX(0);');
@@ -362,9 +349,7 @@ max-height: max-content;`);
const ttyStepHandlers: Step[] = [
(next) => anim.in(100, next),
(next) => {
- const s = anim.selector(
- `#openrc .ttylines-openrc`,
- );
+ const s = anim.selector(`#openrc .ttylines-openrc`);
s.style(altHiddenStyles);
anim.in(1, () => {
s.style(altVisibleStyles);
@@ -378,8 +363,9 @@ const ttyStepHandlers: Step[] = [
switch (step.kind) {
case 'text': {
const s = anim.selector(
- `${isBeforeFirstClear ? '#openrc' : '#tty-' + (ttyIdx - 1)} ${step.classes.map((v) => `.${v}`).join('')
- }`,
+ `${isBeforeFirstClear ? '#openrc' : '#tty-' + (ttyIdx - 1)} ${step.classes
+ .map((v) => `.${v}`)
+ .join('')}`,
);
s.style(altHiddenStyles);
anim.in(1, () => {
@@ -390,8 +376,9 @@ const ttyStepHandlers: Step[] = [
}
case 'removeNode': {
const s = anim.selector(
- `${isBeforeFirstClear ? '#openrc' : '#tty-' + (ttyIdx - 1)} ${step.removedItemClassList.map((v) => `.${v}`).join('')
- }`,
+ `${isBeforeFirstClear ? '#openrc' : '#tty-' + (ttyIdx - 1)} ${step.removedItemClassList
+ .map((v) => `.${v}`)
+ .join('')}`,
);
s.style(altVisibleStyles);
anim.in(1, () => {
@@ -412,7 +399,7 @@ const ttyStepHandlers: Step[] = [
if (isBeforeFirstClear) {
isBeforeFirstClear = false;
}
- toStage(3 + (++ttyIdx));
+ toStage(3 + ++ttyIdx);
next();
break;
}
@@ -444,7 +431,7 @@ handleSteps.push(() => {
// we done
});
nextStep();
-const output = `/**
+const comment = `/**
* @generated
* @license AGPL-3.0-OR-LATER
* @copyright 2024 memdmp
@@ -456,10 +443,13 @@ const output = `/**
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-${anim.exportToCSS()}
-${stages.filter((_, i, a) => i !== a.length - 1).map((v) =>
- `${v.selector} {
+ */`;
+const exported = anim.exportToCSS();
+const tail = `${stages
+ .filter((_, i, a) => i !== a.length - 1)
+ .map(
+ (v) =>
+ `${v.selector} {
${hiddenStageStyles}
}
.ttytext-removed-after-done {
@@ -470,35 +460,56 @@ ${hiddenStageStyles}
margin-left: -100vw;
margin-right: 100vw;
}
-${(ttyLines.flatMap((v) =>
+${ttyLines
+ .flatMap((v) =>
v.kind === 'text'
? [
- ...v.value.map((v) => ({
- kind: 'text' as const,
- ...v,
- })),
- ]
- : []
- ).flatMap((v) => [
- ...(
- v.kind === 'text'
- ? [
+ ...v.value.map((v) => ({
+ kind: 'text' as const,
+ ...v,
+ })),
+ ]
+ : [],
+ )
+ .flatMap((v) => [
+ ...(v.kind === 'text'
+ ? [
v.colour
? `.ttytext-block.text-\\[\\${v.colour}\\]{color:${v.colour};}`
: '',
- v.bg
- ? `.ttytext-block.bg-\\[\\${v.bg}\\]{background:${v.bg};}`
- : '',
+ v.bg ? `.ttytext-block.bg-\\[\\${v.bg}\\]{background:${v.bg};}` : '',
]
- : []
- ),
- ]).filter((v, i, a) => v.length !== 0 && a.indexOf(v) === i)).join('\n')
- }
-`
-).join('')
- }
-${[...anim.exportToObject().values()].map(v => `#app.skip-animation ${v.selector} {
+ : []),
+ ])
+ .filter((v, i, a) => v.length !== 0 && a.indexOf(v) === i)
+ .join('\n')}
+`,
+ )
+ .join('')}
+${[...anim.exportToObject().values()]
+ .map((v) => `#app.skip-animation ${v.selector}`)
+ .join(',\n')} {
+ animation-name: none;
animation-duration: 0.01ms;
-}`).join('\n')}
+}
`;
-console.log(output);
+fs.writeFileSync(
+ 'src/routes/anim.css',
+ `${comment}
+${
+ esbuild.buildSync({
+ stdin: {
+ contents: `${exported}
+${tail}`,
+ loader: 'css',
+ },
+ write: false,
+ minify: false,
+ }).outputFiles![0].text
+}`,
+);
+fs.writeFileSync(
+ 'src/routes/no-anim.css',
+ `${comment}
+${tail}`,
+);
diff --git a/src/routes/skip-animation/+page.svelte b/src/routes/skip-animation/+page.svelte
index a252eb6..fdbbfec 100644
--- a/src/routes/skip-animation/+page.svelte
+++ b/src/routes/skip-animation/+page.svelte
@@ -1,5 +1,6 @@
<script lang="ts">
- import Page from '../+page.svelte';
+ import Page from '../IndexPage.svelte';
+ import '../no-anim.css';
</script>
<svelte:head>