File size: 2,616 Bytes
9fca766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""The CRT boot: one second that tells you whose machine this is.

BootScreen plays a BIOS-flavored startup before the menu; NoiseScreen is
the two-breath static wipe between major scenes. Both dismiss with None,
both skip on any key, and neither exists under SCRYPT_REDUCED_MOTION.
"""

from __future__ import annotations

import random

from rich.text import Text
from textual import events
from textual.app import ComposeResult
from textual.screen import Screen
from textual.widgets import Static

from scrypt.ui import fx as fxmod
from scrypt.ui import palette as pal

_OK = f"{pal.GREEN}"
BOOT_LINES = [
    ("SCRYPT BIOS v0.1 — property of the machine", pal.MUTED),
    ("mem check: 32768 MB ................ OK", _OK),
    ("mounting /home/drifter ............. OK", _OK),
    ("mounting /var/crash ................ OK", _OK),
    ("starting audit-daemon .............. OK", _OK),
    ("starting cron ...................... OK", _OK),
    ("starting warden.service ............ [WATCHING]", f"bold {pal.WARDEN}"),
]


class BootScreen(Screen):
    CSS = """
    #boot { height: 1fr; padding: 2 6; }
    """

    def compose(self) -> ComposeResult:
        yield Static(id="boot")

    def on_mount(self) -> None:
        self._shown = 0
        self.set_interval(0.14, self._tick)

    def _tick(self) -> None:
        if self._shown >= len(BOOT_LINES):
            self.set_timer(0.5, lambda: self._done())
            return
        self._shown += 1
        t = Text()
        for line, style in BOOT_LINES[: self._shown]:
            t.append(line + "\n", style=style)
        self.query_one("#boot", Static).update(t)

    def _done(self) -> None:
        if self.is_current:
            self.dismiss(None)

    def on_key(self, event: events.Key) -> None:
        self.dismiss(None)


class NoiseScreen(Screen):
    """Three frames of static between scenes: the channel changes."""

    CSS = """
    #noise { height: 1fr; }
    """

    def compose(self) -> ComposeResult:
        yield Static(id="noise")

    def on_mount(self) -> None:
        self._frames = 0
        self.set_interval(0.06, self._tick)

    def _tick(self) -> None:
        self._frames += 1
        if self._frames > 3:
            if self.is_current:
                self.dismiss(None)
            return
        w = max(20, self.size.width)
        h = max(5, self.size.height)
        t = Text()
        for _ in range(h):
            t.append(fxmod.noise_line(w, random) + "\n", style=pal.GHOST)
        self.query_one("#noise", Static).update(t)

    def on_key(self, event: events.Key) -> None:
        self.dismiss(None)