Compare commits

..

6 commits

Author SHA1 Message Date
dece 843a88659f file: parse .gmi files as gemtext 2021-06-18 01:44:48 +02:00
dece 04c66bad52 gemini: make it clear that empty CN is OK
Will it be OK for all servers though? We might never know…
2021-06-18 01:44:17 +02:00
dece 36d0e6f7b1 gopher: properly render error lines 2021-06-13 01:55:48 +02:00
dece 54aeafd878 config: use -utf8 for default openssl cert gen 2021-06-13 01:55:15 +02:00
dece a3c9b10647 gemini: show reason for certificate creation 2021-06-13 01:54:56 +02:00
dece bc8803d86b browser: edit current URL with "O"
Move "open last download" to M-o, as it is not that often used it seems.
2021-06-11 22:52:18 +02:00
8 changed files with 51 additions and 20 deletions

View file

@ -99,12 +99,12 @@ class Browser:
return self.dim[1]
@property
def current_url(self):
def current_url(self) -> str:
"""Return the current URL."""
return self._current_url
@current_url.setter
def current_url(self, url):
def current_url(self, url: str):
"""Set the current URL and show it in the status line."""
self._current_url = url
self.set_status(url)
@ -244,7 +244,7 @@ class Browser:
elif char == ord("o"):
self.quick_command("open")
elif char == ord("O"):
self.open_last_download()
self.quick_command(f"open {self.current_url}")
elif char == ord("p"):
self.go_back()
elif char == ord("u"):
@ -291,6 +291,8 @@ class Browser:
self.scroll_page_vertically(-1)
elif char == ord("l"):
self.scroll_page_horizontally(1)
elif char == ord("o"):
self.open_last_download()
@property
def page_pad_size(self):
@ -386,11 +388,18 @@ class Browser:
elif command == "set-render-mode":
self.set_render_mode(words[1])
def get_user_text_input(self, status_text, char, prefix="", strip=False):
def get_user_text_input(self, status_text, char, prefix="", strip=False,
escape_to_none=False):
"""Get user input from the command-line."""
self.set_status(status_text)
result = self.command_line.focus(char, prefix=prefix)
result = self.command_line.focus(
char,
prefix=prefix,
escape_to_none=escape_to_none
)
self.reset_status()
if result is None:
return None
if strip:
result = result.strip()
return result

View file

@ -35,7 +35,11 @@ def open_file(browser: Browser, filepath: str, encoding="utf-8"):
except (OSError, ValueError) as exc:
browser.set_status_error(f"Failed to open file: {exc}")
return None
browser.load_page(Page.from_text(text))
if path.suffix == ".gmi":
page = Page.from_gemtext(text, browser.config["text_width"])
else:
page = Page.from_text(text)
browser.load_page(page)
elif path.is_dir():
gemtext = str(path) + "\n\n"
for entry in sorted(path.iterdir()):

View file

@ -294,7 +294,7 @@ def _handle_cert_required(
url_identities = get_identities_for_url(browser.identities, url)
if not url_identities:
identity = create_identity(browser, url)
identity = create_identity(browser, url, reason=response.meta)
if not identity:
return None
browser.identities[url] = [identity]
@ -317,23 +317,27 @@ def select_identity(identities: list):
return identities[0] if identities else None
def create_identity(browser: Browser, url: str):
def create_identity(browser: Browser, url: str, reason: Optional[str] =None):
"""Walk the user through identity creation.
Returns:
The created identity on success (already registered in identities
"""
key = browser.prompt("Create client certificate?")
prompt_text = "Create client certificate?"
if reason:
prompt_text += f" Reason: {reason}"
key = browser.prompt(prompt_text)
if key != "y":
browser.reset_status()
return None
common_name = browser.get_user_text_input(
"Name? The server will see this, you can leave it empty.",
"Name? The server may use it as your username.",
CommandLine.CHAR_TEXT,
strip=True,
escape_to_none=True
)
if not common_name:
if common_name is None:
browser.reset_status()
return None

View file

@ -22,6 +22,7 @@ DEFAULT_CONFIG = {
"-nodes",
"-keyform", "PEM",
"-keyout", "{key_path}",
"-utf8",
"-x509",
"-days", "28140", # https://www.youtube.com/watch?v=F9L4q-0Pi4E
"-outform", "PEM",

View file

@ -27,7 +27,8 @@ Keybinds using the SHIFT key are written uppercase. Keybinds using the ALT (or M
* gg: go to the top of the page
* G: go to the bottom of the page
* o: open an URL
* O: open last download with an external command
* O: edit current URL
* M-o: open last download with an external command
* p: go to the previous page
* u: go to the parent page (up a level in URL)
* U: go to the root page (root URL for the current domain)

View file

@ -3,6 +3,9 @@
In Bebop we use a list of elements as produced by our parser. These elements are
converted into so-called "metalines", which are the text lines as they will be
displayed, along with associated meta-data such as its type or a link's URL.
Note that metalines can be generated by custom functions without relying on the
elements classes as they are quite coupled to Gemtext parsing/rendering.
"""
import string
@ -33,6 +36,7 @@ class LineType(IntEnum):
PREFORMATTED = 6
BLOCKQUOTE = 7
LIST_ITEM = 8
ERROR = 9 # Not part of Gemtext but useful internally.
def generate_metalines(elements, width, dumb=False):

View file

@ -70,5 +70,7 @@ def get_base_line_attributes(line_type) -> int:
return curses.color_pair(ColorPair.PREFORMATTED)
elif line_type == LineType.BLOCKQUOTE:
return curses.color_pair(ColorPair.BLOCKQUOTE) | A_ITALIC
elif line_type == LineType.ERROR:
return curses.color_pair(ColorPair.ERROR) | curses.A_BOLD
else: # includes LineType.PARAGRAPH
return curses.color_pair(ColorPair.NORMAL)

View file

@ -53,7 +53,6 @@ USER_FRIENDLY_TYPES = {t.name.lower(): t for t in ItemType}
ICONS = {
ItemType.FILE: "📄",
ItemType.DIR: "📂",
ItemType.ERROR: "",
ItemType.SEARCH: "",
ItemType.HTML: "🌐",
}
@ -256,19 +255,26 @@ def parse_source(source: str, item_type: ItemType):
break
parts = tline.split("\t")
if len(parts) != 4:
# TODO move me away
# Does not seem to be split by tabs, may be a file.
metalines.append(({"type": LineType.PARAGRAPH}, line))
continue
# If the map is poorly formatted and parts are missing, pad with
# empty parts.
while len(parts) < 4:
parts.append("")
item_type = ItemType(ltype)
label, path, host, port = parts
# INFO: render as a simple text line.
if item_type == ItemType.INFO:
meta = {"type": LineType.PARAGRAPH}
metalines.append((meta, label))
metalines.append(({"type": LineType.PARAGRAPH}, label))
continue
# ERROR: render as an error line.
if item_type == ItemType.ERROR:
metalines.append(({"type": LineType.ERROR}, label))
continue
# Other item types are rendered as links, with a special case for
# "URL:"-type selectors in HTML items.
if item_type == ItemType.HTML and path[:4].upper() == "URL:":
link_url = path[4:]
else: