aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/shared.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/shared.ts')
-rw-r--r--src/routes/shared.ts485
1 files changed, 485 insertions, 0 deletions
diff --git a/src/routes/shared.ts b/src/routes/shared.ts
new file mode 100644
index 0000000..a5ff683
--- /dev/null
+++ b/src/routes/shared.ts
@@ -0,0 +1,485 @@
+/*
+ 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/>.
+*/
+export const biosSteps = 3;
+export const biosStepInterval = 1000;
+
+const wpmToTypingSpeed = (wpm: number, avgWordLen: number) =>
+ (1 / (wpm * avgWordLen / 60)) * 1000;
+
+const averageWordLength = 4.793;
+const typingInfo = (wpm = 80) => ({
+ typingSpeedAvg: wpmToTypingSpeed(wpm, averageWordLength),
+ typingDeviation: 20, // typing speed deviation is often correlated to the typing speed - TODO: at way lower speeds, the correlation is inverse; the more deviation there is (is this also the case for fast typing? what is the inflection point if not? i forgor i went down a rabbit hole yrs ago)
+});
+export type TypingSpeed = ReturnType<typeof typingInfo>;
+export const getDelay = (typingInfo: TypingSpeed) =>
+ (Math.random() * 2 - 1) * typingInfo.typingDeviation +
+ typingInfo.typingSpeedAvg;
+export const login = {
+ username: 'lain',
+ passwordLength: 12,
+ ...typingInfo(100), // one usually has muscle memory for well-known frequently-typed credentials, hence higher wpm
+};
+export const ttyTypingInfo = typingInfo();
+
+export type RenderBlock = {
+ value: string;
+ colour?: string;
+ weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
+ italic?: boolean;
+ underlined?: boolean;
+ url?:
+ | `newtab:${string}`
+ | `currenttab:${string}`
+ | ((textObj: TTYText & { kind: 'text' }) => void);
+ bg?: string;
+ raw?: boolean;
+ dl?: string;
+};
+export type TTYText = {
+ kind: 'text';
+ renderrestriction?: 'everywhere' | 'js-only' | 'noscript';
+ value: RenderBlock[];
+ id: string;
+ classes: string[];
+} | {
+ kind: 'removeNode';
+ removedId: string;
+ removedItemClassList: string[];
+} | {
+ kind: 'time';
+ delay: number;
+} | {
+ kind: 'cursorVisibility';
+ visible: boolean;
+} | {
+ kind: 'clear';
+};
+export type Only<Obj, Keys extends keyof Obj> = {
+ [k in Keys]: Obj[k];
+};
+export type Diff<
+ T extends {
+ [k: string]: unknown;
+ },
+ U extends keyof T,
+> = ({ [P in keyof T]: P } & { [P in U]: never } & { [x: string]: never })[
+ keyof T
+];
+export type Omit<
+ T extends {
+ [k: string]: unknown;
+ },
+ K extends keyof T,
+> = Pick<
+ T,
+ Diff<T, K>
+>;
+export const ttyLines: TTYText[] = (() => {
+ const lines: TTYText[] = [];
+ let ids: string[] = [];
+ const byId = new Map<string, TTYText>();
+ let i = 69420;
+ let defaultRenderRestriction: Only<
+ TTYText & { kind: 'text' },
+ 'renderrestriction'
+ >['renderrestriction'] = 'everywhere';
+ type LimitedRenderBlock = Omit<RenderBlock, 'value'>;
+ type RenderBlockArg = [
+ text: string,
+ options?: LimitedRenderBlock,
+ ];
+ /** due to hellish css constraints, each text() call creates its own line - however, each block in a text() is joined on the same line. */
+ const text = (
+ globalOptions:
+ | Only<TTYText & { kind: 'text' }, 'renderrestriction'>
+ | RenderBlockArg,
+ ...blocks: RenderBlockArg[]
+ ) => {
+ const id = (++i).toString();
+ ids.push(id);
+ if (Array.isArray(globalOptions)) {
+ blocks.unshift(globalOptions);
+ globalOptions = {};
+ }
+ const txt = {
+ kind: 'text',
+ renderrestriction: globalOptions.renderrestriction ??
+ defaultRenderRestriction,
+ value: blocks.map(([t, o]) => ({
+ value: t,
+ colour: '#b9b9b9',
+ ...o,
+ })),
+ id,
+ classes: [`ttytext-${id}`],
+ } as TTYText;
+ lines.push(txt);
+ byId.set(id, txt);
+ return id;
+ };
+ const wait = (time: number) =>
+ lines.push({
+ kind: 'time',
+ delay: time,
+ });
+ const del = (id: string | number) => {
+ const removedId = typeof id === 'string'
+ ? id
+ : id >= 0
+ ? ids[id]
+ : ids[ids.length + id];
+ const r = byId.get(removedId);
+ if (r?.kind === 'text') {
+ lines.push({
+ kind: 'removeNode',
+ removedId,
+ removedItemClassList: r.classes,
+ });
+ r.classes.push('ttytext-removed-after-done');
+ }
+ ids = ids.filter((v) => v !== removedId);
+ };
+ const delSince = (id: string | number) => {
+ const removedId = typeof id === 'string'
+ ? id
+ : id >= 0
+ ? ids[id]
+ : ids[ids.length + id];
+ const idIndex = ids.indexOf(removedId);
+ for (let i = idIndex; i < ids.length; i++) {
+ const removedId = ids[i];
+ const r = byId.get(removedId);
+ if (r?.kind === 'text') {
+ r.classes.push('ttytext-removed-after-done');
+ lines.push({
+ kind: 'removeNode',
+ removedId,
+ removedItemClassList: r.classes,
+ });
+ }
+ }
+ ids = ids.filter((_, i) => i < idIndex);
+ };
+ const getLast = () => {
+ const v = byId.get(ids[ids.length - 1]);
+ if (v?.kind == 'text') {
+ return v.value;
+ } else return null;
+ };
+ const overwriteLast = (...newValue: RenderBlockArg[]) => {
+ const v = byId.get(ids[ids.length - 1]);
+ const l = v?.kind == 'text' ? v.renderrestriction : 'everywhere';
+ del(-1);
+ return text({
+ renderrestriction: l,
+ }, ...newValue);
+ };
+ const replaceLast = (
+ handler: (newValue: RenderBlockArg[]) => RenderBlockArg[],
+ ) =>
+ overwriteLast(
+ ...handler(
+ getLast()!.map(
+ (v) =>
+ [
+ v.value,
+ Object.fromEntries(
+ Object.entries(v).filter(([k]) => k !== 'value'),
+ ),
+ ] as const,
+ ),
+ ),
+ );
+ const everyTTYClear: (() => void)[] = [];
+ const clear = () => {
+ if (lines.find((v) => v.kind === 'clear')) {
+ delSince(lines.findLastIndex((v) => v.kind === 'clear'));
+ }
+ lines.push({ kind: 'clear' });
+ everyTTYClear.forEach((v) => v());
+ };
+
+ everyTTYClear.push(() => {
+ text({
+ renderrestriction: 'noscript',
+ }, ['This browser does not support JS. Your experience may be degraded.', {
+ bg: '#ff0000',
+ colour: '#dedede',
+ }], ['\n', {}]);
+ });
+ everyTTYClear.forEach((v) => v());
+
+ //
+
+ wait(300);
+ text({ renderrestriction: 'js-only' }, [
+ ((v) => v[Math.floor(Math.random() * v.length)])([
+ 'cool beats are stored in the balls',
+ 'found xml documents in the firmware',
+ 'overhead: "I don\'t consent" "hey thats my line"',
+ 'uwu',
+ 'i regret making this hellhole of a codebase',
+ ]),
+ {
+ colour: '#777777',
+ },
+ ]);
+ wait(1000);
+ clear();
+ text(
+ ['lain', {
+ colour: '#FFFF53',
+ weight: 700,
+ }],
+ [' in '],
+ ['mem.estrogen.zone', {
+ colour: '#17B117',
+ weight: 700,
+ }],
+ [' in '],
+ ['~', {
+ colour: '#53FFFF',
+ weight: 700,
+ }],
+ );
+ text(
+ ['❯ ', { colour: '#53FF53', weight: 600 }],
+ [''],
+ ['', { colour: '#777777' }],
+ );
+ {
+ const targetstr = '/usr/bin/env wel';
+ const completions = [
+ '/bin/sh -c "$(/usr/bin/env curl -fsSL https://blahaj.estrogen.zone/)"',
+ '/usr/local/bin/become-estradiol',
+ '/usr/bin/shellutil ansi cheatsheet 24bit',
+ '/usr/bin/env sh',
+ '/usr/bin/env wc -l src/routes/anim.css',
+ '/usr/bin/env welcome -c ~/.local/share/welcome.toml',
+ ];
+ for (const c of [...targetstr.split(''), 'COMPLETE']) {
+ replaceLast((l) => {
+ const prompt = l[l.length - 2];
+ const suggestions = l[l.length - 1];
+ if (c === 'COMPLETE') {
+ prompt[0] += suggestions[0];
+ suggestions[0] = '';
+ } else {
+ prompt[0] += c;
+ const completion = completions.find((v) => v.startsWith(prompt[0]));
+ if (completion) {
+ suggestions[0] = completion.substring(prompt[0].length);
+ } else suggestions[0] = '';
+ }
+ return l;
+ });
+ if (c === 'COMPLETE') {
+ wait(100);
+ } else wait(50 + Math.random() * 100);
+ }
+ text(['Preparing...']);
+ wait(200);
+ }
+ wait(1000);
+ clear();
+ text(
+ ['Welcome to'],
+ [''],
+ [' the Estrogen Zone!\n\n'],
+ );
+ wait(300);
+ for (const c of ' my corner of'.split('')) {
+ replaceLast((l) => {
+ l[1][0] += c;
+ return l;
+ });
+ wait(70);
+ }
+ text(
+ ['Places you can find me:', {
+ colour: '#7a7a7a',
+ }],
+ );
+ wait(300);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ ['Estradiol SourceHut: ', { colour: '#cdcdcd' }],
+ );
+ wait(100);
+ replaceLast((v) => [...v, ['sh.estrogen.zone/~memdmp', {
+ colour: '#99aaff',
+ underlined: true,
+ weight: 700,
+ url: 'newtab:https://sh.estrogen.zone/~memdmp',
+ }]]);
+ wait(100);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ ['On jsr: ', { colour: '#cdcdcd' }],
+ );
+ wait(600);
+ replaceLast((v) => [...v, ['jsr.io/@memdmp', {
+ colour: '#f7df23',
+ underlined: true,
+ weight: 700,
+ url: 'newtab:https://sh.estrogen.zone/~memdmp',
+ }]]);
+ wait(200);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ ['In random hackerspaces', { colour: '#aaaaaa' }],
+ );
+ wait(600);
+ text(
+ ['\nYou may find these useful:', {
+ colour: '#7a7a7a',
+ }],
+ );
+ wait(400);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ ['GPG Root Key: ', { colour: '#cdcdcd' }],
+ );
+ wait(1000);
+ replaceLast((v) => [...v, ['B546778F06BBCC8EC167DB3CD919706487B8B6DE', {
+ colour: '#58C7F3',
+ underlined: true,
+ weight: 700,
+ url: 'currenttab:/keys/memdmp/primary.pgp',
+ dl: 'memdmp-primary.pgp',
+ }]]);
+ wait(100);
+ replaceLast((v) => [...v, [' (root key)', {
+ colour: '#999999',
+ }]]);
+ wait(400);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ );
+ wait(70);
+ replaceLast(
+ (v) => [...v, ['GPG Git Key: ', { colour: '#cdcdcd' }]],
+ );
+ wait(100);
+ replaceLast((v) => [...v, ['5134F05BD8D9DB8C6C0E1515A9439D346AB6DF4E', {
+ colour: '#F0A3B3',
+ underlined: true,
+ weight: 700,
+ url: 'currenttab:/keys/memdmp/git.pgp',
+ dl: 'memdmp-git.pgp',
+ }]]);
+ wait(100);
+ replaceLast((v) => [...v, [' (', {
+ colour: '#999999',
+ }], ['signed', {
+ colour: '#F0A3B3',
+ underlined: true,
+ weight: 500,
+ url: 'currenttab:/keys/memdmp/git.pgp.sig',
+ dl: 'memdmp-git.pgp.sig',
+ }], [')', { colour: '#999999' }]]);
+ wait(45);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ );
+ wait(70);
+ replaceLast(
+ (
+ v,
+ ) => [...v, ['GPG Release Signing Key: ', {
+ colour: '#cdcdcd',
+ }]],
+ );
+ wait(100);
+ replaceLast((v) => [...v, ['0D93102265071798C7B65A4C9F0739B9E0C8FD60', {
+ colour: '#ffffff',
+ underlined: true,
+ weight: 700,
+ url: 'currenttab:/keys/memdmp/release.pgp',
+ dl: 'memdmp-release.pgp',
+ }]]);
+ wait(100);
+ replaceLast((v) => [...v, [' (', {
+ colour: '#999999',
+ }], ['signed', {
+ colour: '#ffffff',
+ underlined: true,
+ weight: 500,
+ url: 'currenttab:/keys/memdmp/release.pgp.sig',
+ dl: 'memdmp-release.pgp.sig',
+ }], [')', { colour: '#999999' }]]);
+ wait(100);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ );
+ wait(35);
+ replaceLast(
+ (
+ v,
+ ) => [...v, ['Source Code: ', {
+ colour: '#cdcdcd',
+ }]],
+ );
+ wait(100);
+ replaceLast((v) => [...v, ['git.sh.estrogen.zone/~memdmp/mem.estrogen.zone', {
+ colour: '#F0A3B3',
+ underlined: true,
+ weight: 700,
+ url: 'newtab:/upstream/',
+ }]]);
+ wait(100);
+ text(
+ [' - ', {
+ colour: '#7a7a7a',
+ }],
+ );
+ wait(35);
+ replaceLast(
+ (
+ v,
+ ) => [...v, ['Canaries: ', {
+ colour: '#cdcdcd',
+ }]],
+ );
+ wait(100);
+ replaceLast((v) => [...v, ['/canaries/', {
+ colour: '#58C7F3',
+ underlined: true,
+ weight: 700,
+ url: 'currenttab:/canaries/',
+ }]]);
+ wait(5000);
+ text([
+ `<button style="padding: 12px 12px;background: #fff2;margin-top: 0.4rem;border-radius: 0.7rem;opacity:0.1;margin-top:3rem;" data-el="le funny button">have a button :3</button>`,
+ {
+ raw: true,
+ url: () => alert('i abused too much css for this i wanna cry now'),
+ },
+ ]);
+
+ //
+
+ return lines;
+})();