From 114dc8c8549a4a1271d43c42c7931209b5a9f5b5 Mon Sep 17 00:00:00 2001
From: memdmp <memdmp@estrogen.zone>
Date: Wed, 12 Mar 2025 15:04:31 +0100
Subject: feat: properly handle skip-animation

---
 src/routes/anim-gen.ts | 223 ++++++++++++++++++++++++++-----------------------
 1 file changed, 117 insertions(+), 106 deletions(-)

(limited to 'src/routes/anim-gen.ts')

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}`,
+);
-- 
cgit v1.2.3