PageSourceSearch

https://tinylytics.app/assets/controllers/crt_controller-cce07c64.js

js tinylytics.app collected 2026-10-02 05:41:49 UTC 2,675 bytes, 95 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3const POWER_ON = [
4  { transform: "scale(0.02, 0.004)", filter: "brightness(8)" },
5  { transform: "scale(1, 0.004)", filter: "brightness(8)", offset: 0.3 },
6  { transform: "scale(1, 1.02)", filter: "brightness(1.6)", offset: 0.7 },
7  { transform: "scale(1, 1)", filter: "brightness(1)" }
8]
9
10const POWER_OFF = [
11  { transform: "scale(1, 1)", filter: "brightness(1)", opacity: 1 },
12  { transform: "scale(1, 0.004)", filter: "brightness(6)", opacity: 1, offset: 0.45 },
13  { transform: "scale(0.02, 0.004)", filter: "brightness(10)", opacity: 1, offset: 0.8 },
14  { transform: "scale(0, 0)", filter: "brightness(10)", opacity: 0 }
15]
16
17export default class extends Controller {
18  static targets = [ "power", "phosphor" ]
19
20  connect() {
21    this.#screen = sessionStorage.getItem("crt")
22  }
23
24  disconnect() {
25    delete document.documentElement.dataset.crt
26  }
27
28  toggle() {
29    if (this.powering) return
30
31    if (this.#screen) {
32      this.#powerOff()
33    } else {
34      this.#powerOn()
35    }
36  }
37
38  tune({ params: { phosphor } }) {
39    this.#screen = this.#screen === phosphor ? "color" : phosphor
40  }
41
42  #powerOn() {
43    this.#screen = "color"
44    this.#animate(POWER_ON, 550)
45  }
46
47  async #powerOff() {
48    await this.#animate(POWER_OFF, 450)
49    this.#screen = null
50  }
51
52  async #animate(keyframes, duration) {
53    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return
54
55    const root = document.documentElement
56    const tint = getComputedStyle(root).filter.replace("none", "")
57    this.powering = true
58    root.classList.add("crt-powering")
59    root.style.transformOrigin = `50% ${window.scrollY + window.innerHeight / 2}px`
60
61    try {
62      await root.animate(keyframes.map((frame) => ({ ...frame, filter: `${tint} ${frame.filter}` })), { duration, easing: "ease-out" }).finished
63    } finally {
64      this.powering = false
65      root.classList.remove("crt-powering")
66      root.style.removeProperty("transform-origin")
67    }
68  }
69
70  #render() {
71    const screen = this.#screen
72
73    this.powerTargets.forEach((button) => button.setAttribute("aria-pressed", Boolean(screen)))
74    this.phosphorTargets.forEach((button) => {
75      button.classList.toggle("hidden", !screen)
76      button.setAttribute("aria-pressed", button.dataset.crtPhosphorParam === screen)
77    })
78  }
79
80  get #screen() {
81    return document.documentElement.dataset.crt
82  }
83
84  set #screen(value) {
85    if (value) {
86      document.documentElement.dataset.crt = value
87      sessionStorage.setItem("crt", value)
88    } else {
89      delete document.documentElement.dataset.crt
90      sessionStorage.removeItem("crt")
91    }
92
93    this.#render()
94  }
95}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.