Compare commits
4 commits
62619f29fb
...
84764644df
Author | SHA1 | Date | |
---|---|---|---|
dece | 84764644df | ||
dece | d7a94650cb | ||
dece | 524bda5b39 | ||
dece | 72111bce70 |
45
README.md
Normal file
45
README.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
Bebop
|
||||||
|
=====
|
||||||
|
|
||||||
|
Bebop is a [Gemini][gemini] browser for the terminal, focusing on practicality
|
||||||
|
and speed. It is a personal project to learn how to use ncurses and try new
|
||||||
|
ways to explore the Geminispace. It borrows some ideas from [Amfora][amfora],
|
||||||
|
another great terminal browser, Vim for interactivity and tries to support mouse
|
||||||
|
usage decently.
|
||||||
|
|
||||||
|
[gemini]: https://gemini.circumlunar.space/
|
||||||
|
[amfora]: https://github.com/makeworld-the-better-one/amfora
|
||||||
|
|
||||||
|
If you are interested in Gemini and looking for a client, I recommend trying a
|
||||||
|
graphical one like the excellent [Lagrange][lagrange] or [Kristall][kristall],
|
||||||
|
or Amfora if you're feeling more at home in the terminal. Bebop won't attempt
|
||||||
|
to support every feature other clients might have.
|
||||||
|
|
||||||
|
[lagrange]: https://git.skyjake.fi/skyjake/lagrange
|
||||||
|
[kristall]: https://kristall.random-projects.net/
|
||||||
|
|
||||||
|
It passes the Conman's client test but not Egsam's for now.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
### What works
|
||||||
|
|
||||||
|
- Basic browsing: scrolling, follow links, redirections, Web links.
|
||||||
|
|
||||||
|
### What is planned
|
||||||
|
|
||||||
|
- Handle more content types.
|
||||||
|
- Great config options.
|
||||||
|
- Identity management with temporary and managed certificates.
|
||||||
|
- Buffers (or tabs if you prefer).
|
||||||
|
- Home page.
|
||||||
|
- Bookmarks.
|
||||||
|
|
||||||
|
### What is not planned for now
|
||||||
|
|
||||||
|
- 256-colors mode and themes.
|
||||||
|
- Subscriptions. I have no need for them as I prefer to use a browser-agnostic
|
||||||
|
aggregator at the moment.
|
|
@ -222,7 +222,11 @@ class Browser:
|
||||||
else:
|
else:
|
||||||
pass # TODO
|
pass # TODO
|
||||||
|
|
||||||
response = Response.parse(req.proceed())
|
data = req.proceed()
|
||||||
|
if not data:
|
||||||
|
self.set_status_error(f"Server did not respond in time ({url}).")
|
||||||
|
return
|
||||||
|
response = Response.parse(data)
|
||||||
if not response:
|
if not response:
|
||||||
self.set_status_error(f"Server response parsing failed ({url}).")
|
self.set_status_error(f"Server response parsing failed ({url}).")
|
||||||
return
|
return
|
||||||
|
|
|
@ -81,7 +81,7 @@ class Request:
|
||||||
self.payload += LINE_TERM
|
self.payload += LINE_TERM
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sock = socket.create_connection((hostname, port))
|
sock = socket.create_connection((hostname, port), timeout=10)
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
self.state = Request.STATE_CONNECTION_FAILED
|
self.state = Request.STATE_CONNECTION_FAILED
|
||||||
self.error = exc.strerror
|
self.error = exc.strerror
|
||||||
|
@ -124,7 +124,10 @@ class Request:
|
||||||
self.ssock.sendall(self.payload)
|
self.ssock.sendall(self.payload)
|
||||||
response = b""
|
response = b""
|
||||||
while True:
|
while True:
|
||||||
|
try:
|
||||||
buf = self.ssock.recv(4096)
|
buf = self.ssock.recv(4096)
|
||||||
|
except socket.timeout:
|
||||||
|
buf = None
|
||||||
if not buf:
|
if not buf:
|
||||||
return response
|
return response
|
||||||
response += buf
|
response += buf
|
||||||
|
@ -172,6 +175,7 @@ class Response:
|
||||||
content: bytes = b""
|
content: bytes = b""
|
||||||
|
|
||||||
HEADER_RE = re.compile(r"(\d{2}) (.*)")
|
HEADER_RE = re.compile(r"(\d{2}) (.*)")
|
||||||
|
MAX_META_LEN = 1024
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def generic_code(self):
|
def generic_code(self):
|
||||||
|
@ -189,6 +193,8 @@ class Response:
|
||||||
if not match:
|
if not match:
|
||||||
return None
|
return None
|
||||||
code, meta = match.groups()
|
code, meta = match.groups()
|
||||||
|
if len(meta) > Response.MAX_META_LEN:
|
||||||
|
return None
|
||||||
response = Response(StatusCode(int(code)), meta=meta)
|
response = Response(StatusCode(int(code)), meta=meta)
|
||||||
if response.generic_code == StatusCode.SUCCESS:
|
if response.generic_code == StatusCode.SUCCESS:
|
||||||
content_offset = response_header_len + len(LINE_TERM)
|
content_offset = response_header_len + len(LINE_TERM)
|
||||||
|
|
|
@ -215,10 +215,13 @@ def render_lines(metalines, window, max_width):
|
||||||
- max_width: line length limit for the pad.
|
- max_width: line length limit for the pad.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
The tuple (height, width) of the resized window.
|
The tuple of integers (error, height, width), error being a non-zero value
|
||||||
|
if an error occured during rendering, and height and width being the new
|
||||||
|
dimensions of the resized window.
|
||||||
"""
|
"""
|
||||||
num_lines = len(metalines)
|
num_lines = len(metalines)
|
||||||
window.resize(num_lines, max_width)
|
new_dimensions = num_lines, max_width
|
||||||
|
window.resize(*new_dimensions)
|
||||||
for line_index, metaline in enumerate(metalines):
|
for line_index, metaline in enumerate(metalines):
|
||||||
meta, line = metaline
|
meta, line = metaline
|
||||||
line = line[:max_width - 1]
|
line = line[:max_width - 1]
|
||||||
|
@ -237,7 +240,10 @@ def render_lines(metalines, window, max_width):
|
||||||
attr = curses.color_pair(ColorPair.BLOCKQUOTE) | curses.A_ITALIC
|
attr = curses.color_pair(ColorPair.BLOCKQUOTE) | curses.A_ITALIC
|
||||||
else: # includes LineType.PARAGRAPH
|
else: # includes LineType.PARAGRAPH
|
||||||
attr = curses.color_pair(ColorPair.NORMAL)
|
attr = curses.color_pair(ColorPair.NORMAL)
|
||||||
|
try:
|
||||||
window.addstr(line, attr)
|
window.addstr(line, attr)
|
||||||
|
except ValueError:
|
||||||
|
return new_dimensions
|
||||||
if line_index < num_lines - 1:
|
if line_index < num_lines - 1:
|
||||||
window.addstr("\n")
|
window.addstr("\n")
|
||||||
return num_lines, max_width
|
return new_dimensions
|
||||||
|
|
Reference in a new issue