create browser class
This commit is contained in:
parent
e65d430aac
commit
e7dd7a1629
229
nex.py
229
nex.py
|
@ -12,47 +12,12 @@ logger.setLevel(logging.INFO)
|
|||
logger.addHandler(logging.FileHandler('nex.log'))
|
||||
|
||||
|
||||
class Session:
|
||||
def __init__(self):
|
||||
self.history = [Page('home')]
|
||||
self.curr_page_idx = 0
|
||||
|
||||
def back(self):
|
||||
if self.curr_page_idx > 0:
|
||||
self.curr_page_idx -= 1
|
||||
self.render()
|
||||
|
||||
def forward(self):
|
||||
if self.curr_page_idx < len(self.history) - 1:
|
||||
self.curr_page_idx += 1
|
||||
self.render()
|
||||
|
||||
def load(self, url):
|
||||
self.curr_page_idx += 1
|
||||
if self.curr_page_idx >= len(self.history):
|
||||
self.history.append(Page(url))
|
||||
else:
|
||||
self.history[self.curr_page_idx] = Page(url)
|
||||
self.history[self.curr_page_idx].request()
|
||||
|
||||
def render(self):
|
||||
return self.history[self.curr_page_idx].content
|
||||
|
||||
def reload(self):
|
||||
self.history[self.curr_page_idx].request()
|
||||
|
||||
@property
|
||||
def current_page(self):
|
||||
return self.history[self.curr_page_idx]
|
||||
|
||||
def follow_link(self, link_index: int):
|
||||
logger.info(link_index)
|
||||
url = self.current_page.links[link_index]
|
||||
if url.startswith('nex://'):
|
||||
self.load(url)
|
||||
else:
|
||||
url = '/'.join([self.current_page.url.rstrip('/'), url])
|
||||
self.load(url)
|
||||
def parse_url(url: str) -> Tuple[str, str]:
|
||||
clean_url = url.replace('nex://', '')
|
||||
split_url = clean_url.split('/', 1)
|
||||
if len(split_url) == 1:
|
||||
return clean_url, ''
|
||||
return split_url[0], split_url[1]
|
||||
|
||||
|
||||
class Page:
|
||||
|
@ -69,7 +34,7 @@ class Page:
|
|||
sock.connect((self.host, PORT))
|
||||
except ConnectionError as e:
|
||||
logger.error(e)
|
||||
self.content = f'Error connecting to {self.host}'
|
||||
self.content = f'[error]Could not connect to {self.host}'
|
||||
return
|
||||
selector = self.selector + '\r\n'
|
||||
sock.sendall(selector.encode('ascii'))
|
||||
|
@ -83,12 +48,12 @@ class Page:
|
|||
self.raw_content = buf.decode('ascii')
|
||||
except UnicodeDecodeError as e:
|
||||
logger.error(e)
|
||||
self.content = 'Content could not be decoded'
|
||||
self.content = '[warning]Content could not be decoded'
|
||||
return
|
||||
self.content = self.raw_content
|
||||
self._parse_links()
|
||||
for i, l in enumerate(self.links):
|
||||
self.content = re.sub(l, f'[bold cyan]\[{i}][/] {l}',
|
||||
self.content = re.sub(l, f'[bold primary]\[{i}][/] {l}',
|
||||
self.content, 1)
|
||||
|
||||
@property
|
||||
|
@ -98,103 +63,127 @@ class Page:
|
|||
def _parse_links(self):
|
||||
pattern = re.compile(r'=>\s*([\w/:\.~-]*)\s*')
|
||||
self.links = pattern.findall(self.raw_content)
|
||||
# Find links
|
||||
# Determine if absolute or relative
|
||||
# Parse out relative directory navigation
|
||||
# Store parsed URLs in array
|
||||
# Add numbers to page content corresponding to array index
|
||||
|
||||
|
||||
def parse_url(url: str) -> Tuple[str, str]:
|
||||
clean_url = url.replace('nex://', '')
|
||||
split_url = clean_url.split('/', 1)
|
||||
if len(split_url) == 1:
|
||||
return clean_url, ''
|
||||
return split_url[0], split_url[1]
|
||||
landing_page = Page('')
|
||||
landing_page.raw_content = 'Welcome to PyNex'
|
||||
landing_page.content = landing_page.raw_content
|
||||
|
||||
|
||||
class BrowserWindow(ptg.Window):
|
||||
class Session:
|
||||
def __init__(self):
|
||||
self.pages = {'': landing_page}
|
||||
self.history = ['']
|
||||
self.curr_index = 0
|
||||
|
||||
overflow = ptg.Overflow.SCROLL
|
||||
vertical_align = ptg.VerticalAlignment.TOP
|
||||
@property
|
||||
def current_page(self):
|
||||
return self.pages[self.history[self.curr_index]]
|
||||
|
||||
def __init__(self, session: Session):
|
||||
def prev(self):
|
||||
if self.curr_index > 0:
|
||||
self.curr_index -= 1
|
||||
return self.current_page
|
||||
|
||||
def next(self):
|
||||
if self.curr_index < len(self.history) - 1:
|
||||
self.curr_index += 1
|
||||
return self.current_page
|
||||
|
||||
def new_page(self, url):
|
||||
self.curr_index += 1
|
||||
if self.curr_index >= len(self.history):
|
||||
self.history.append(url)
|
||||
else:
|
||||
self.history[self.curr_index] = url
|
||||
page = Page(url)
|
||||
page.request()
|
||||
self.pages[url] = page
|
||||
return self.current_page
|
||||
|
||||
|
||||
class Browser(ptg.WindowManager):
|
||||
def __init__(self, session=Session(), *args):
|
||||
super().__init__()
|
||||
self.layout = ptg.Layout()
|
||||
self.session = session
|
||||
self.set_widgets([session.render()])
|
||||
|
||||
self.page_container = ptg.Container(self.session.current_page.content)
|
||||
self.body = ptg.Window(self.page_container,
|
||||
overflow=ptg.Overflow.SCROLL,
|
||||
vertical_align=ptg.VerticalAlignment.TOP)
|
||||
self.footer = ptg.Window(
|
||||
ptg.Splitter(
|
||||
ptg.KeyboardButton("Back", self.back, bound='b'),
|
||||
ptg.KeyboardButton("Forward", self.forward, bound='f'),
|
||||
ptg.KeyboardButton("Reload", self.reload, bound='r'),
|
||||
ptg.KeyboardButton("Quit", self.stop, bound='q')
|
||||
),
|
||||
box="EMPTY",
|
||||
is_persistant=True
|
||||
)
|
||||
|
||||
def update(self) -> None:
|
||||
self.set_widgets([session.render()])
|
||||
|
||||
def window_back(self, *args):
|
||||
self.session.back()
|
||||
self._create_layout()
|
||||
self._create_key_bindings()
|
||||
self.update()
|
||||
|
||||
def window_forward(self, *args):
|
||||
self.session.forward()
|
||||
def _create_key_bindings(self):
|
||||
self.bind('j', lambda *_: self.body.scroll(5))
|
||||
self.bind(ptg.keys.DOWN, lambda *_: self.body.scroll(5))
|
||||
self.bind('k', lambda *_: self.body.scroll(-5))
|
||||
self.bind(ptg.keys.UP, lambda *_: self.body.scroll(-5))
|
||||
self.bind('q', lambda *_: self.stop())
|
||||
|
||||
for i in range(10):
|
||||
self.bind(f'{i}', lambda widget, key: self.follow_link(key))
|
||||
|
||||
def _create_layout(self):
|
||||
self.layout.add_slot("Body")
|
||||
self.layout.add_break()
|
||||
self.layout.add_slot("Footer", height=1)
|
||||
|
||||
# self.add(self.header)
|
||||
self.add(self.body)
|
||||
self.add(self.footer)
|
||||
|
||||
def update(self):
|
||||
self.page_container.set_widgets([self.session.current_page.content])
|
||||
self.body.set_title(f'[bold tertiary]{self.session.current_page.title}', 1)
|
||||
|
||||
def back(self, *args):
|
||||
self.session.prev()
|
||||
self.update()
|
||||
|
||||
def window_reload(self, *args):
|
||||
self.session.reload()
|
||||
def forward(self, *args):
|
||||
self.session.next()
|
||||
self.update()
|
||||
|
||||
def follow_link(self, key: str):
|
||||
def reload(self, *args):
|
||||
self.session.current_page.request()
|
||||
self.update()
|
||||
|
||||
def go(self, url):
|
||||
self.session.new_page(url)
|
||||
self.update()
|
||||
|
||||
def follow_link(self, key):
|
||||
index = int(key)
|
||||
self.session.follow_link(index)
|
||||
url = self.session.current_page.links[index]
|
||||
if url.startswith('nex://'):
|
||||
self.session.new_page(url)
|
||||
else:
|
||||
url = '/'.join([self.session.current_page.url.rstrip('/'), url])
|
||||
self.session.new_page(url)
|
||||
self.update()
|
||||
|
||||
|
||||
def _define_layout() -> ptg.Layout:
|
||||
layout = ptg.Layout()
|
||||
layout.add_slot("Header", height=1)
|
||||
layout.add_break()
|
||||
layout.add_slot("Body")
|
||||
layout.add_break()
|
||||
layout.add_slot("Footer", height=1)
|
||||
return layout
|
||||
|
||||
|
||||
def _create_footer(browser: BrowserWindow, manager: ptg.WindowManager):
|
||||
|
||||
return ptg.Window(
|
||||
ptg.Splitter(
|
||||
ptg.KeyboardButton("Back", browser.window_back, bound='b'),
|
||||
ptg.KeyboardButton("Forward", browser.window_forward, bound='f'),
|
||||
ptg.KeyboardButton("Reload", browser.window_reload, bound='r'),
|
||||
ptg.KeyboardButton("Quit", lambda *_: manager.stop(), bound='q')
|
||||
),
|
||||
box="EMPTY",
|
||||
is_persistant=True
|
||||
)
|
||||
def main(url=None):
|
||||
with Browser() as browser:
|
||||
if url is not None:
|
||||
browser.go(url)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
session = Session()
|
||||
if len(sys.argv) > 1:
|
||||
url = sys.argv[1]
|
||||
if len(sys.argv) > 2 and sys.argv[2] == '--notui':
|
||||
url = sys.argv[1]
|
||||
session.load(url)
|
||||
print(session.render())
|
||||
session.follow_link(0)
|
||||
print(session.render())
|
||||
sys.exit(0)
|
||||
session.load(url)
|
||||
browser = BrowserWindow(session)
|
||||
with ptg.WindowManager() as manager:
|
||||
manager.layout = _define_layout()
|
||||
header = ptg.Window(f"[bold accent]{session.current_page.title}",
|
||||
box="EMPTY",
|
||||
is_persistant=True)
|
||||
manager.add(header)
|
||||
manager.add(browser, assign='body')
|
||||
manager.add(_create_footer(browser, manager), assign='footer')
|
||||
manager.bind('j', lambda *_: browser.scroll(5))
|
||||
manager.bind(ptg.keys.DOWN, lambda *_: browser.scroll(5))
|
||||
manager.bind('k', lambda *_: browser.scroll(-5))
|
||||
manager.bind(ptg.keys.UP, lambda *_: browser.scroll(-5))
|
||||
manager.bind('q', lambda *_: manager.stop())
|
||||
|
||||
for i in range(10):
|
||||
manager.bind(f'{i}', lambda widget, key: browser.follow_link(key))
|
||||
logger.info(manager.bindings)
|
||||
main(url)
|
||||
|
|
Loading…
Reference in New Issue