From e7dd7a16299c60ec02f66adcffbe960bd215148d Mon Sep 17 00:00:00 2001 From: yequari Date: Mon, 3 Jul 2023 01:52:14 -0700 Subject: [PATCH] create browser class --- nex.py | 229 +++++++++++++++++++++++++++------------------------------ 1 file changed, 109 insertions(+), 120 deletions(-) diff --git a/nex.py b/nex.py index 95d50b9..fe83ae3 100644 --- a/nex.py +++ b/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)