Upload boredatwork.py
This commit is contained in:
parent
781181ebe1
commit
71c88b4c42
1 changed files with 592 additions and 0 deletions
592
boredatwork.py
Normal file
592
boredatwork.py
Normal file
|
|
@ -0,0 +1,592 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
boredatwork — a pomodoro timer with a pool of harmless break-time diversions.
|
||||
|
||||
Run: python3 boredatwork.py
|
||||
|
||||
Keys (from the main screen):
|
||||
s start / pause
|
||||
r reset current session
|
||||
o options (adjust focus / break length)
|
||||
b surprise me (boredatwork diversion, any time)
|
||||
q quit
|
||||
"""
|
||||
|
||||
import json
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.screen import Screen, ModalScreen
|
||||
from textual.containers import Vertical, Horizontal, Center, Middle
|
||||
from textual.widgets import Static, Button, Input, Label
|
||||
from textual.reactive import reactive
|
||||
from textual.binding import Binding
|
||||
|
||||
CONFIG_DIR = Path.home() / ".config" / "boredatwork"
|
||||
CONFIG_PATH = CONFIG_DIR / "config.json"
|
||||
|
||||
DEFAULT_CONFIG = {"focus_minutes": 25, "break_minutes": 5}
|
||||
|
||||
|
||||
def load_config() -> dict:
|
||||
try:
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
cfg = dict(DEFAULT_CONFIG)
|
||||
cfg.update(data)
|
||||
return cfg
|
||||
except Exception:
|
||||
return dict(DEFAULT_CONFIG)
|
||||
|
||||
|
||||
def save_config(cfg: dict) -> None:
|
||||
try:
|
||||
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(cfg, f)
|
||||
except Exception:
|
||||
pass # settings persistence is a nicety, never a blocker
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Diversion content — all local, all static, nothing fetched or generated.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
JOKES = [
|
||||
"Why do programmers prefer dark mode? Because light attracts bugs.",
|
||||
"I told my computer I needed a break. It said no problem, it'll go to sleep too.",
|
||||
"Why did the Raspberry Pi apply for a loan? It wanted a bigger case.",
|
||||
"A SQL query walks into a bar, walks up to two tables and asks: mind if I join you?",
|
||||
"I'd tell you a UDP joke, but you might not get it.",
|
||||
"There are 10 kinds of people: those who understand binary, and those who don't.",
|
||||
"Why was the network administrator always calm? Great subnet management.",
|
||||
"I changed my password to 'incorrect' — now when I forget it, it tells me.",
|
||||
"Debugging: being the detective in a crime movie where you are also the murderer.",
|
||||
"Why do Java developers wear glasses? Because they don't see sharp.",
|
||||
"My code doesn't have bugs, it has undocumented features.",
|
||||
"Why did the Wi-Fi break up with the router? It felt no connection.",
|
||||
"An optimist and a pessimist walk into a bar. It's actually half a bar.",
|
||||
"I've got a great joke about UDP, but I'm not sure you'll get it.",
|
||||
"Real programmers count from zero, and occasionally lose an argument about it.",
|
||||
]
|
||||
|
||||
TRIVIA = [
|
||||
"The first computer mouse, built in 1964, was carved out of wood.",
|
||||
"The Basilica di San Gaudenzio in Novara has one of the tallest domes in Italy.",
|
||||
"A single bolt of lightning contains enough energy to toast about 100,000 slices of bread.",
|
||||
"Brianza has been a recognised centre of Italian furniture-making for over a century.",
|
||||
"The QWERTY keyboard layout was originally designed to slow typists down, to stop typewriter jams.",
|
||||
"Honey never spoils — edible honey has been found in tombs thousands of years old.",
|
||||
"The first 1GB hard drive, released in 1980, weighed about 250 kilograms.",
|
||||
"Octopuses have three hearts, and two of them stop beating when they swim.",
|
||||
"The word 'robot' comes from the Czech word 'robota', meaning forced labour.",
|
||||
"Milan's Duomo took nearly six centuries to complete, from 1386 to 1965.",
|
||||
"A group of crows is called a murder; a group of ravens is called an unkindness.",
|
||||
"The first email was sent in 1971, by an engineer emailing the desk next to him.",
|
||||
"Bamboo can grow up to 91 cm in a single day under the right conditions.",
|
||||
"The shortest war in recorded history lasted about 38 minutes, in 1896.",
|
||||
"There are more possible chess games than atoms in the observable universe.",
|
||||
]
|
||||
|
||||
RIDDLES = [
|
||||
("What has keys but no locks, space but no room, and you can enter but not go in?",
|
||||
"A keyboard."),
|
||||
("The more you take, the more you leave behind. What am I?",
|
||||
"Footsteps."),
|
||||
("What has a heart that doesn't beat?",
|
||||
"An artichoke."),
|
||||
("I'm light as a feather, yet the strongest person can't hold me for long. What am I?",
|
||||
"Your breath."),
|
||||
("What gets wetter the more it dries?",
|
||||
"A towel."),
|
||||
("What has a neck but no head?",
|
||||
"A bottle."),
|
||||
("The person who makes it, sells it. The person who buys it never uses it. What is it?",
|
||||
"A coffin."),
|
||||
("What can travel around the world while staying in a corner?",
|
||||
"A stamp."),
|
||||
("I have branches, but no fruit, trunk, or leaves. What am I?",
|
||||
"A bank."),
|
||||
("What five-letter word becomes shorter when you add two letters to it?",
|
||||
"Short."),
|
||||
]
|
||||
|
||||
COMPLIMENTS = [
|
||||
"You've read this far into a break-timer's silly text bank. Genuine dedication.",
|
||||
"Your solder joints are probably cleaner than most people's handwriting.",
|
||||
"Somewhere, a client's ancient PC is grateful you exist.",
|
||||
"You debug things at 11pm so other people don't have to think about it at all.",
|
||||
"Your documentation habit alone puts you ahead of most of the industry.",
|
||||
"You've earned this break. The Ryzen isn't going anywhere.",
|
||||
"Whoever built your workbench setup clearly knows what they're doing. Oh, that was you.",
|
||||
"You've kept a system running for longer than some startups have existed.",
|
||||
"The next person who inherits your notes is going to be pleasantly surprised.",
|
||||
"Not everyone air-gaps a Raspberry Pi for fun. Respect.",
|
||||
]
|
||||
|
||||
MADLIBS = [
|
||||
{
|
||||
"blanks": ["a noun", "a verb ending in -ing", "an adjective", "a place",
|
||||
"a plural noun", "an adverb"],
|
||||
"template": (
|
||||
"The client called about their {0}. Apparently it had been {1} all "
|
||||
"morning, which the client described as '{2}'. I drove out to {3}, "
|
||||
"opened the case, and found three {4} inside — clearly the real "
|
||||
"problem. I fixed it {5} and left before they could ask how."
|
||||
),
|
||||
},
|
||||
{
|
||||
"blanks": ["a number", "an animal", "a noun", "an adjective",
|
||||
"a body part", "a verb (past tense)"],
|
||||
"template": (
|
||||
"The server room had been running fine for {0} days when a {1} "
|
||||
"got into the ventilation and knocked loose the {2}. The whole "
|
||||
"rack went {3}, the fans started sounding like a distressed {4}, "
|
||||
"and the whole thing only {5} after I unplugged it and counted to ten."
|
||||
),
|
||||
},
|
||||
{
|
||||
"blanks": ["a colour", "a kitchen appliance", "a verb ending in -ing",
|
||||
"a Brianza town", "an adjective", "a small object"],
|
||||
"template": (
|
||||
"The new client's office was entirely {0}, including — "
|
||||
"inexplicably — the {1}. I spent the afternoon {2} cables while "
|
||||
"someone from {3} called three times about a {4} invoice. "
|
||||
"In the end the whole fix came down to a single {5} taped to the router."
|
||||
),
|
||||
},
|
||||
{
|
||||
"blanks": ["a liquid", "a piece of furniture", "an emotion",
|
||||
"a type of weather", "a plural noun", "a verb ending in -ing"],
|
||||
"template": (
|
||||
"Someone spilled {0} directly onto the {1} during the demo. "
|
||||
"The reaction in the room was pure {2} — outside it had started "
|
||||
"doing that thing where the {3} makes all the {4} act strange. "
|
||||
"I kept {5} and pretended this was a completely normal Tuesday."
|
||||
),
|
||||
},
|
||||
{
|
||||
"blanks": ["a number", "a tool", "an adjective", "a sound effect",
|
||||
"a plural noun", "a place"],
|
||||
"template": (
|
||||
"It took exactly {0} attempts with a {1} before the case finally "
|
||||
"came apart. The screws were {2}, the fan made a sound like "
|
||||
"'{3}', and inside were nothing but loose {4} and a receipt "
|
||||
"from {5}. Some builds keep their secrets better than others."
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def pick_diversion(exclude_kind: str | None = None) -> tuple[str, object]:
|
||||
kinds = ["joke", "trivia", "riddle", "compliment", "madlib"]
|
||||
if exclude_kind and len(kinds) > 1:
|
||||
kinds = [k for k in kinds if k != exclude_kind]
|
||||
kind = random.choice(kinds)
|
||||
if kind == "joke":
|
||||
return kind, random.choice(JOKES)
|
||||
if kind == "trivia":
|
||||
return kind, random.choice(TRIVIA)
|
||||
if kind == "riddle":
|
||||
return kind, random.choice(RIDDLES)
|
||||
if kind == "compliment":
|
||||
return kind, random.choice(COMPLIMENTS)
|
||||
return kind, random.choice(MADLIBS)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Screens
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class SettingsScreen(ModalScreen[None]):
|
||||
BINDINGS = [Binding("escape", "dismiss_screen", "Close")]
|
||||
|
||||
def __init__(self, cfg: dict) -> None:
|
||||
super().__init__()
|
||||
self.cfg = cfg
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Center():
|
||||
with Middle():
|
||||
with Vertical(id="settings-box"):
|
||||
yield Label("Options", id="settings-title")
|
||||
yield Label("Focus length (minutes)")
|
||||
yield Input(value=str(self.cfg["focus_minutes"]), id="focus-input")
|
||||
yield Label("Break length (minutes)")
|
||||
yield Input(value=str(self.cfg["break_minutes"]), id="break-input")
|
||||
with Horizontal(id="settings-buttons"):
|
||||
yield Button("Save", id="save-btn", variant="success")
|
||||
yield Button("Cancel", id="cancel-btn")
|
||||
|
||||
def action_dismiss_screen(self) -> None:
|
||||
self.dismiss(None)
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "cancel-btn":
|
||||
self.dismiss(None)
|
||||
return
|
||||
if event.button.id == "save-btn":
|
||||
focus_in = self.query_one("#focus-input", Input)
|
||||
break_in = self.query_one("#break-input", Input)
|
||||
try:
|
||||
focus_val = max(1, int(focus_in.value))
|
||||
except ValueError:
|
||||
focus_val = self.cfg["focus_minutes"]
|
||||
try:
|
||||
break_val = max(1, int(break_in.value))
|
||||
except ValueError:
|
||||
break_val = self.cfg["break_minutes"]
|
||||
self.cfg["focus_minutes"] = focus_val
|
||||
self.cfg["break_minutes"] = break_val
|
||||
save_config(self.cfg)
|
||||
self.dismiss(None)
|
||||
|
||||
|
||||
class MadlibScreen(ModalScreen[None]):
|
||||
"""Collects blanks one at a time, then reveals the finished story."""
|
||||
|
||||
BINDINGS = [Binding("escape", "dismiss_screen", "Close")]
|
||||
|
||||
def __init__(self, madlib: dict) -> None:
|
||||
super().__init__()
|
||||
self.madlib = madlib
|
||||
self.answers: list[str] = []
|
||||
self.index = 0
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Center():
|
||||
with Middle():
|
||||
with Vertical(id="madlib-box"):
|
||||
yield Label("Madlib", id="madlib-title")
|
||||
yield Static(id="madlib-prompt")
|
||||
yield Input(placeholder="type here", id="madlib-input")
|
||||
with Horizontal(id="madlib-buttons"):
|
||||
yield Button("Next", id="next-btn", variant="success")
|
||||
yield Button("Close", id="close-btn")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.run_worker(self._show_prompt(), exclusive=True)
|
||||
|
||||
async def _show_prompt(self) -> None:
|
||||
blanks = self.madlib["blanks"]
|
||||
if self.index < len(blanks):
|
||||
prompt = self.query_one("#madlib-prompt", Static)
|
||||
prompt.update(f"Give me {blanks[self.index]}:")
|
||||
box = self.query_one("#madlib-input", Input)
|
||||
box.value = ""
|
||||
box.focus()
|
||||
else:
|
||||
await self._reveal()
|
||||
|
||||
async def _reveal(self) -> None:
|
||||
story = self.madlib["template"].format(*self.answers)
|
||||
box = self.query_one("#madlib-box", Vertical)
|
||||
await box.remove_children()
|
||||
await box.mount(Label("Your story", id="madlib-title"))
|
||||
await box.mount(Static(story, id="madlib-story"))
|
||||
await box.mount(Horizontal(Button("Close", id="close-btn"), id="madlib-buttons"))
|
||||
|
||||
async def _submit(self) -> None:
|
||||
box = self.query_one("#madlib-input", Input)
|
||||
value = box.value.strip() or "(blank)"
|
||||
self.answers.append(value)
|
||||
self.index += 1
|
||||
await self._show_prompt()
|
||||
|
||||
async def on_input_submitted(self, event: Input.Submitted) -> None:
|
||||
if event.input.id == "madlib-input":
|
||||
await self._submit()
|
||||
|
||||
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "close-btn":
|
||||
self.dismiss(None)
|
||||
elif event.button.id == "next-btn":
|
||||
await self._submit()
|
||||
|
||||
def action_dismiss_screen(self) -> None:
|
||||
self.dismiss(None)
|
||||
|
||||
|
||||
class DiversionScreen(ModalScreen[None]):
|
||||
BINDINGS = [Binding("escape", "dismiss_screen", "Close")]
|
||||
|
||||
KIND_LABELS = {
|
||||
"joke": "Joke",
|
||||
"trivia": "Trivia",
|
||||
"riddle": "Riddle",
|
||||
"compliment": "Just because",
|
||||
"madlib": "Madlib",
|
||||
}
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.kind, self.content = pick_diversion()
|
||||
self.revealed = False
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Center():
|
||||
with Middle():
|
||||
with Vertical(id="diversion-box"):
|
||||
yield Label(self.KIND_LABELS[self.kind], id="diversion-tag")
|
||||
yield Static(id="diversion-body")
|
||||
with Horizontal(id="diversion-buttons"):
|
||||
yield Button("Another", id="another-btn")
|
||||
if self.kind == "riddle":
|
||||
yield Button("Reveal answer", id="reveal-btn")
|
||||
if self.kind == "madlib":
|
||||
yield Button("Play this one", id="play-madlib-btn",
|
||||
variant="success")
|
||||
yield Button("Back to work", id="close-btn", variant="success")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self._render_body()
|
||||
|
||||
def _render_body(self) -> None:
|
||||
body = self.query_one("#diversion-body", Static)
|
||||
if self.kind == "riddle":
|
||||
question, _ = self.content
|
||||
body.update(question)
|
||||
elif self.kind == "madlib":
|
||||
body.update(
|
||||
"A fill-in-the-blank story is ready. Press 'Play this one' "
|
||||
"to supply the words."
|
||||
)
|
||||
else:
|
||||
body.update(self.content)
|
||||
|
||||
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "close-btn":
|
||||
self.dismiss(None)
|
||||
elif event.button.id == "another-btn":
|
||||
self.kind, self.content = pick_diversion(exclude_kind=self.kind)
|
||||
self.query_one("#diversion-tag", Label).update(
|
||||
self.KIND_LABELS[self.kind]
|
||||
)
|
||||
await self._rebuild_buttons()
|
||||
self._render_body()
|
||||
elif event.button.id == "reveal-btn":
|
||||
_, answer = self.content
|
||||
self.query_one("#diversion-body", Static).update(answer)
|
||||
elif event.button.id == "play-madlib-btn":
|
||||
self.app.push_screen(MadlibScreen(self.content))
|
||||
|
||||
async def _rebuild_buttons(self) -> None:
|
||||
row = self.query_one("#diversion-buttons", Horizontal)
|
||||
await row.remove_children()
|
||||
await row.mount(Button("Another", id="another-btn"))
|
||||
if self.kind == "riddle":
|
||||
await row.mount(Button("Reveal answer", id="reveal-btn"))
|
||||
if self.kind == "madlib":
|
||||
await row.mount(
|
||||
Button("Play this one", id="play-madlib-btn", variant="success")
|
||||
)
|
||||
await row.mount(Button("Back to work", id="close-btn", variant="success"))
|
||||
|
||||
def action_dismiss_screen(self) -> None:
|
||||
self.dismiss(None)
|
||||
|
||||
|
||||
class MainScreen(Screen):
|
||||
BINDINGS = [
|
||||
Binding("s", "toggle_run", "Start/pause"),
|
||||
Binding("r", "reset_session", "Reset"),
|
||||
Binding("o", "open_settings", "Options"),
|
||||
Binding("b", "open_diversion", "Surprise me"),
|
||||
Binding("q", "quit", "Quit"),
|
||||
]
|
||||
|
||||
remaining = reactive(0)
|
||||
running = reactive(False)
|
||||
mode = reactive("focus") # "focus" or "break"
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.cfg = load_config()
|
||||
self.remaining = self.cfg["focus_minutes"] * 60
|
||||
self._timer = None
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Center():
|
||||
with Middle():
|
||||
with Vertical(id="console"):
|
||||
yield Label("CERVELLO ELETTRICO", id="brand-label")
|
||||
yield Label("boredatwork", id="app-label")
|
||||
yield Static("FOCUS", id="mode-label")
|
||||
yield Static("25:00", id="clock")
|
||||
with Horizontal(id="controls"):
|
||||
yield Button("Start", id="start-btn", variant="success")
|
||||
yield Button("Reset", id="reset-btn")
|
||||
yield Button("Options", id="options-btn")
|
||||
with Horizontal(id="controls2"):
|
||||
yield Button("Surprise me", id="surprise-btn", variant="primary")
|
||||
yield Static(
|
||||
"s start/pause r reset o options b surprise me q quit",
|
||||
id="hint-bar",
|
||||
)
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self._update_clock()
|
||||
|
||||
def _update_clock(self) -> None:
|
||||
mins, secs = divmod(max(0, self.remaining), 60)
|
||||
self.query_one("#clock", Static).update(f"{mins:02d}:{secs:02d}")
|
||||
self.query_one("#mode-label", Static).update(
|
||||
"FOCUS" if self.mode == "focus" else "BREAK"
|
||||
)
|
||||
console = self.query_one("#console", Vertical)
|
||||
console.set_class(self.mode == "break", "break-mode")
|
||||
|
||||
def action_toggle_run(self) -> None:
|
||||
self.running = not self.running
|
||||
start_btn = self.query_one("#start-btn", Button)
|
||||
if self.running:
|
||||
start_btn.label = "Pause"
|
||||
self._timer = self.set_interval(1, self._tick)
|
||||
else:
|
||||
start_btn.label = "Start"
|
||||
if self._timer:
|
||||
self._timer.stop()
|
||||
self._timer = None
|
||||
|
||||
def action_reset_session(self) -> None:
|
||||
if self._timer:
|
||||
self._timer.stop()
|
||||
self._timer = None
|
||||
self.running = False
|
||||
self.query_one("#start-btn", Button).label = "Start"
|
||||
minutes = (
|
||||
self.cfg["focus_minutes"] if self.mode == "focus"
|
||||
else self.cfg["break_minutes"]
|
||||
)
|
||||
self.remaining = minutes * 60
|
||||
self._update_clock()
|
||||
|
||||
def action_open_settings(self) -> None:
|
||||
self.app.push_screen(SettingsScreen(self.cfg))
|
||||
|
||||
def action_open_diversion(self) -> None:
|
||||
self.app.push_screen(DiversionScreen())
|
||||
|
||||
def _tick(self) -> None:
|
||||
self.remaining -= 1
|
||||
if self.remaining <= 0:
|
||||
self._switch_mode()
|
||||
self._update_clock()
|
||||
|
||||
def _switch_mode(self) -> None:
|
||||
self.mode = "break" if self.mode == "focus" else "focus"
|
||||
minutes = (
|
||||
self.cfg["focus_minutes"] if self.mode == "focus"
|
||||
else self.cfg["break_minutes"]
|
||||
)
|
||||
self.remaining = minutes * 60
|
||||
if self.mode == "break":
|
||||
self.app.push_screen(DiversionScreen())
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "start-btn":
|
||||
self.action_toggle_run()
|
||||
elif event.button.id == "reset-btn":
|
||||
self.action_reset_session()
|
||||
elif event.button.id == "options-btn":
|
||||
self.action_open_settings()
|
||||
elif event.button.id == "surprise-btn":
|
||||
self.action_open_diversion()
|
||||
|
||||
|
||||
class BoredAtWorkApp(App):
|
||||
TITLE = "boredatwork"
|
||||
CSS = """
|
||||
Screen {
|
||||
background: #1b1917;
|
||||
}
|
||||
|
||||
#console {
|
||||
width: 56;
|
||||
border: round #6b6560;
|
||||
padding: 1 3;
|
||||
background: #201d1a;
|
||||
}
|
||||
|
||||
#console.break-mode {
|
||||
border: round #7fbf4d;
|
||||
}
|
||||
|
||||
#brand-label {
|
||||
color: #6b6560;
|
||||
text-style: bold;
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#app-label {
|
||||
color: #c1793d;
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
margin-bottom: 1;
|
||||
}
|
||||
|
||||
#mode-label {
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
color: #c1793d;
|
||||
text-style: bold;
|
||||
}
|
||||
|
||||
#clock {
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
text-style: bold;
|
||||
color: #efe7da;
|
||||
margin: 1 0;
|
||||
}
|
||||
|
||||
#controls, #controls2 {
|
||||
align: center middle;
|
||||
margin-bottom: 1;
|
||||
}
|
||||
|
||||
#hint-bar {
|
||||
color: #6b6560;
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
margin-top: 1;
|
||||
}
|
||||
|
||||
#settings-box, #diversion-box, #madlib-box {
|
||||
width: 60;
|
||||
border: round #c1793d;
|
||||
padding: 1 3;
|
||||
background: #201d1a;
|
||||
}
|
||||
|
||||
#settings-title, #diversion-tag, #madlib-title {
|
||||
text-style: bold;
|
||||
color: #c1793d;
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
margin-bottom: 1;
|
||||
}
|
||||
|
||||
#diversion-body, #madlib-story, #madlib-prompt {
|
||||
color: #efe7da;
|
||||
margin-bottom: 1;
|
||||
}
|
||||
|
||||
#settings-buttons, #diversion-buttons, #madlib-buttons {
|
||||
align: center middle;
|
||||
margin-top: 1;
|
||||
}
|
||||
"""
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.push_screen(MainScreen())
|
||||
|
||||
|
||||
def main() -> None:
|
||||
BoredAtWorkApp().run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue