DeckBro implementation

This commit is contained in:
Luxferre
2025-12-31 22:49:24 +02:00
parent bd34cab673
commit e5c6e139d4
3 changed files with 167 additions and 0 deletions
+20
View File
@@ -303,6 +303,26 @@ Supported chat commands:
- `/edcode`: extract code blocks from the most recent message and open `app.ed.edbuf()` on the contents - `/edcode`: extract code blocks from the most recent message and open `app.ed.edbuf()` on the contents
- `/exit` or `/quit`: exit the chat applet - `/exit` or `/quit`: exit the chat applet
### `app.bro`
An implementation of DeckBro, a simple text-only HTTP browser for [DeckText](decktext-spec.md) and simple HTML documents.
Usage: `from app.bro import bro`, then `bro(starting_url)`.
Available commands:
r)ef b)ack f)wd n)ext p)rev l)ink or pg#: q
- `g [url]`: go to a new URL
- `r`: refresh the current document
- `b`: go back in history
- `f`: go forward in history
- `n`: go to the next DeckText page of the document (this is the default action if you just press Enter)
- `p`: go to the previous DeckText page of the document
- `[num]`: jump to the page `[num]` if it exists
- `l [num]`: visit the link number `[num]` on the current page (if it exists)
- `q`: quit the browser
### `app.blog` ### `app.blog`
A simple interface to any POST-based blog API that supports HTTP Basic Auth. A simple interface to any POST-based blog API that supports HTTP Basic Auth.
+146
View File
@@ -0,0 +1,146 @@
# DeckBro: a simple DeckText/HTML browser for T-DeckARD environment
# Usage: from app.bro import DeckBro
# Created by Luxferre in 2025-2026, released into public domain
from deck.pager import print_paged
from deck.input import input, input_multi
from deck.menu import menu
from deck.http import http_fetch, http_set_ua
from deck.text import DeckText
class DeckBro:
def __init__(self, start_url=None):
self.history = []
self.rendered_pages = []
self.history_pos = 0 # always refers to the current slot
self.url = start_url
self.doc = DeckText()
if self.url:
self.go(self.url)
def render(self):
pagenum = 1
pagetotal = len(self.doc.pages)
self.rendered_pages = []
for page in self.doc.pages:
outtext = f'{self.doc.title} ({pagenum}/{pagetotal})\n---\n'
outtext += page['content']
if len(page['links']) > 0:
linknum = 1
outtext += '\n--- Links ---\n'
for l in page['links']:
outtext += f'[{linknum}] {l[1]}: {l[0]}\n'
linknum += 1
self.rendered_pages.append(outtext)
pagenum += 1
return len(self.rendered_pages)
def display(self):
curpage = 1
totalpages = len(self.rendered_pages)
menustring = 'g)o r)ef b)ack f)wd n)ext p)rev l)ink or pg#: '
do_new_display = True
while True:
if do_new_display and curpage > 0 and curpage <= totalpages:
print_paged(self.rendered_pages[curpage - 1])
print(f'[{self.url}]')
user_choice = input(menustring).strip()
pgnum = None
try: # page number input detection
pgnum = int(user_choice)
except: pass
if pgnum is not None:
if pgnum > 0 and pgnum <= totalpages:
curpage = pgnum
do_new_display = True
else:
print('Invalid page number!')
do_new_display = False
continue
user_opt = ''
try:
user_opt = ' '.join(user_choice.split(' ')[1:])
except: pass
if user_choice == '':
user_choice = 'n' # go to the next page by default
ch_letter = user_choice[0].lower()
if ch_letter == 'q': break
elif ch_letter == 'n':
do_new_display = True
curpage += 1
if curpage > totalpages: # wrap around
curpage = 1
elif ch_letter == 'p':
do_new_display = True
curpage -= 1
if curpage < 1: # wrap around
curpage = totalpages
elif ch_letter == 'g':
self.go(user_opt)
break
elif ch_letter == 'l':
addr = None
try:
lnum = int(user_opt)
addr = self.doc.pages[curpage - 1]['links'][lnum - 1][0]
except: pass
if addr is None:
print('Invalid link number entered!')
do_new_display = False
else:
self.go(addr)
break
elif ch_letter == 'r':
self.refresh()
break
elif ch_letter == 'b':
if self.back() is not None:
break
else:
do_new_display = False
elif ch_letter == 'f':
if self.forward() is not None:
break
else:
do_new_display = False
def go(self, url=None, update_history=True):
if url:
self.url = url.strip()
status, pagetext = http_fetch(self.url)
if status != 200:
if pagetext == '':
pagetext = f'Error {status}'
else:
pagetext = f'Error {status}: \n{pagetext}'
self.doc.load(pagetext)
if len(self.doc.pages) > 0 and update_history:
self.history = self.history[0:self.history_pos+1]
self.history.append(self.url)
self.history_pos = len(self.history) - 1
self.render()
self.display()
def refresh(self):
self.go(self.url, update_history=False)
def back(self):
if self.history_pos > 0:
self.history_pos -= 1
new_url = self.history[self.history_pos]
self.go(new_url, update_history=False)
return new_url
return None
def forward(self):
if self.history_pos < len(self.history) - 1:
self.history_pos += 1
new_url = self.history[self.history_pos]
self.go(new_url, update_history=False)
return new_url
return None
# create and start a default DeckBro instance
def bro(url=None):
inst = DeckBro()
inst.go(url)
+1
View File
@@ -15,6 +15,7 @@ from deck.http import auth_header, url_escape, form_encode
from deck.otp import totp, hotp, get_epoch from deck.otp import totp, hotp, get_epoch
from deck import xlat from deck import xlat
from app.ed import edit, view, edbuf, viewbuf, edurl, viewurl from app.ed import edit, view, edbuf, viewbuf, edurl, viewurl
from app.bro import bro
from app.blog import blogpost, blogpost_str, bloged from app.blog import blogpost, blogpost_str, bloged
from app.llmchat import llmchat from app.llmchat import llmchat
try: try: