Skip to content

Instantly share code, notes, and snippets.

@qexat
Created December 27, 2023 14:47
Show Gist options
  • Select an option

  • Save qexat/6ba1b359b2bd5e921d316aebb1472cfb to your computer and use it in GitHub Desktop.

Select an option

Save qexat/6ba1b359b2bd5e921d316aebb1472cfb to your computer and use it in GitHub Desktop.
terminal slides lol
import abc
import collections.abc
import os
import shutil
import sys
import time
import typing
class RGBColor(typing.NamedTuple):
red: int
green: int
blue: int
def to_printable(self) -> str:
return "\x1b[38;2;%d;%d;%dm{}\x1b[39m" % self
class RGBAColor(typing.NamedTuple):
red: int
green: int
blue: int
alpha: float = 1.0
def remove_alpha(self) -> RGBColor:
return RGBColor(*self[:-1])
def flatten_component(fgc: int, bgc: int, opacity: float) -> int:
return int(((fgc - bgc) * opacity) + bgc)
def flatten_foreground(
fg: RGBAColor,
bg: RGBColor,
strategy: collections.abc.Callable[[int, int, float], int] | None = None,
) -> RGBColor:
_strategy = strategy or flatten_component
return RGBColor(
*(_strategy(c, bg[i], fg.alpha) for i, c in enumerate(fg.remove_alpha()))
)
T_contra = typing.TypeVar("T_contra", contravariant=True)
class SupportsWrite(typing.Protocol[T_contra]):
@abc.abstractmethod
def write(self, __s: T_contra, /) -> typing.Any:
pass
def set_bg(color: RGBColor, *, file: SupportsWrite[str] | None = None) -> None:
_file = file or sys.stdout
_file.write("\x1b[48;2;%d;%d;%dm" % color)
def reset_bg(*, file: SupportsWrite[str] | None = None) -> None:
_file = file or sys.stdout
_file.write("\x1b[49m")
def init_seq() -> None:
sys.stdout.write("\x1b[?1049h")
sys.stdout.write("\x1b[2J\x1b[H")
sys.stdout.write("\x1b[?25l")
def end_seq() -> None:
print("\x1b[?1049l\x1b[?25h")
def write_string(
message: str,
x: int,
y: int,
*,
delay: float = 0.0,
release: float = 0.0,
) -> None:
time.sleep(delay)
sys.stdout.write(f"\x1b[{x};{y}H")
sys.stdout.write(message)
sys.stdout.flush()
time.sleep(release)
def write_strings(
messages: collections.abc.Iterable[str],
x: int,
y: int,
*,
delay: float = 0.0,
release: float = 0.0,
postdelay: float = 0.0,
) -> None:
for message in messages:
write_string(message, x, y, delay=delay, release=release)
time.sleep(postdelay)
def print_seq(
message: str,
message_length: int | None = None,
/,
fg: RGBColor = RGBColor(255, 255, 255),
bg: RGBColor = RGBColor(0, 0, 0),
framerate: int = 25,
sustain: float = 1.0,
release: float = 0.0,
) -> None:
width, height = shutil.get_terminal_size()
_message_length = len(message) if message_length is None else message_length
top = (height - 1) // 2
left = (width - _message_length) // 2
fadein_frames = [
flatten_foreground(RGBAColor(*fg, (a / framerate)), bg)
for a in range(0, framerate)
]
fadeout_frames = list(reversed(fadein_frames))
fadein = list(map(lambda a: a.to_printable().format(message), fadein_frames))
fadeout = list(map(lambda a: a.to_printable().format(message), fadeout_frames))
set_bg(bg)
for _ in range(height):
print(" " * width)
write_strings(fadein, top, left, release=1 / framerate, postdelay=sustain)
write_strings(fadeout, top, left, release=1 / framerate, postdelay=release)
def main() -> int:
FRAMERATE = 100
fg = RGBColor(255, 255, 255)
bg = RGBColor(0, 0, 0)
mom_bg = RGBColor(48, 24, 0)
son_bg = RGBColor(0, 24, 48)
try:
init_seq()
print_seq(
"© 2007 The Pokémon Company Inc.",
fg=fg,
bg=bg,
framerate=FRAMERATE,
release=0.5,
)
print_seq("Can you come out?", bg=mom_bg, framerate=FRAMERATE)
print_seq("Mom, I am gay.", bg=son_bg, framerate=FRAMERATE)
print_seq(
"I know that, silly. Come out to the car.", bg=mom_bg, framerate=FRAMERATE
)
print_seq("Car, I am gay.", bg=son_bg, framerate=FRAMERATE)
finally:
end_seq()
return os.EX_OK
if __name__ == "__main__":
raise SystemExit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment