mirror of
https://github.com/daylinmorgan/viv.git
synced 2024-12-22 10:40:44 -06:00
111 lines
3 KiB
Python
111 lines
3 KiB
Python
|
#!/usr/bin/env python3
|
||
|
"""
|
||
|
Adapted from https://github.com/Textualize/textual/blob/main/examples/dictionary.py
|
||
|
"""
|
||
|
|
||
|
from __future__ import annotations
|
||
|
|
||
|
__import__("viv").use("textual", "httpx") # noqa
|
||
|
|
||
|
try:
|
||
|
import httpx
|
||
|
except ImportError:
|
||
|
raise ImportError("Please install httpx with 'pip install httpx' ")
|
||
|
|
||
|
from textual import work
|
||
|
from textual.app import App, ComposeResult
|
||
|
from textual.containers import VerticalScroll
|
||
|
from textual.widgets import Input, Markdown
|
||
|
|
||
|
|
||
|
class DictionaryApp(App):
|
||
|
"""Searches ab dictionary API as-you-type."""
|
||
|
|
||
|
# CSS_PATH = "dictionary.css"
|
||
|
DEFAULT_CSS = """
|
||
|
Screen {
|
||
|
background: $panel;
|
||
|
}
|
||
|
|
||
|
Input {
|
||
|
dock: top;
|
||
|
margin: 1 0;
|
||
|
}
|
||
|
|
||
|
#results {
|
||
|
width: 100%;
|
||
|
height: auto;
|
||
|
|
||
|
}
|
||
|
|
||
|
#results-container {
|
||
|
background: $background 50%;
|
||
|
margin: 0 0 1 0;
|
||
|
height: 100%;
|
||
|
overflow: hidden auto;
|
||
|
border: tall $background;
|
||
|
}
|
||
|
|
||
|
#results-container:focus {
|
||
|
border: tall $accent;
|
||
|
}
|
||
|
"""
|
||
|
|
||
|
def compose(self) -> ComposeResult:
|
||
|
yield Input(placeholder="Search for a word")
|
||
|
with VerticalScroll(id="results-container"):
|
||
|
yield Markdown(id="results")
|
||
|
|
||
|
def on_mount(self) -> None:
|
||
|
"""Called when app starts."""
|
||
|
# Give the input focus, so we can start typing straight away
|
||
|
self.query_one(Input).focus()
|
||
|
|
||
|
async def on_input_changed(self, message: Input.Changed) -> None:
|
||
|
"""A coroutine to handle a text changed message."""
|
||
|
if message.value:
|
||
|
self.lookup_word(message.value)
|
||
|
else:
|
||
|
# Clear the results
|
||
|
self.query_one("#results", Markdown).update("")
|
||
|
|
||
|
@work(exclusive=True)
|
||
|
async def lookup_word(self, word: str) -> None:
|
||
|
"""Looks up a word."""
|
||
|
url = f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
|
||
|
|
||
|
async with httpx.AsyncClient() as client:
|
||
|
response = await client.get(url)
|
||
|
try:
|
||
|
results = response.json()
|
||
|
except Exception:
|
||
|
self.query_one("#results", Markdown).update(response.text)
|
||
|
|
||
|
if word == self.query_one(Input).value:
|
||
|
markdown = self.make_word_markdown(results)
|
||
|
self.query_one("#results", Markdown).update(markdown)
|
||
|
|
||
|
def make_word_markdown(self, results: object) -> str:
|
||
|
"""Convert the results in to markdown."""
|
||
|
lines = []
|
||
|
if isinstance(results, dict):
|
||
|
lines.append(f"# {results['title']}")
|
||
|
lines.append(results["message"])
|
||
|
elif isinstance(results, list):
|
||
|
for result in results:
|
||
|
lines.append(f"# {result['word']}")
|
||
|
lines.append("")
|
||
|
for meaning in result.get("meanings", []):
|
||
|
lines.append(f"_{meaning['partOfSpeech']}_")
|
||
|
lines.append("")
|
||
|
for definition in meaning.get("definitions", []):
|
||
|
lines.append(f" - {definition['definition']}")
|
||
|
lines.append("---")
|
||
|
|
||
|
return "\n".join(lines)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
app = DictionaryApp()
|
||
|
app.run()
|