Compare commits
6 commits
00d5e51e2f
...
843a88659f
Author | SHA1 | Date | |
---|---|---|---|
dece | 843a88659f | ||
dece | 04c66bad52 | ||
dece | 36d0e6f7b1 | ||
dece | 54aeafd878 | ||
dece | a3c9b10647 | ||
dece | bc8803d86b |
|
@ -99,12 +99,12 @@ class Browser:
|
||||||
return self.dim[1]
|
return self.dim[1]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_url(self):
|
def current_url(self) -> str:
|
||||||
"""Return the current URL."""
|
"""Return the current URL."""
|
||||||
return self._current_url
|
return self._current_url
|
||||||
|
|
||||||
@current_url.setter
|
@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."""
|
"""Set the current URL and show it in the status line."""
|
||||||
self._current_url = url
|
self._current_url = url
|
||||||
self.set_status(url)
|
self.set_status(url)
|
||||||
|
@ -244,7 +244,7 @@ class Browser:
|
||||||
elif char == ord("o"):
|
elif char == ord("o"):
|
||||||
self.quick_command("open")
|
self.quick_command("open")
|
||||||
elif char == ord("O"):
|
elif char == ord("O"):
|
||||||
self.open_last_download()
|
self.quick_command(f"open {self.current_url}")
|
||||||
elif char == ord("p"):
|
elif char == ord("p"):
|
||||||
self.go_back()
|
self.go_back()
|
||||||
elif char == ord("u"):
|
elif char == ord("u"):
|
||||||
|
@ -291,6 +291,8 @@ class Browser:
|
||||||
self.scroll_page_vertically(-1)
|
self.scroll_page_vertically(-1)
|
||||||
elif char == ord("l"):
|
elif char == ord("l"):
|
||||||
self.scroll_page_horizontally(1)
|
self.scroll_page_horizontally(1)
|
||||||
|
elif char == ord("o"):
|
||||||
|
self.open_last_download()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def page_pad_size(self):
|
def page_pad_size(self):
|
||||||
|
@ -386,11 +388,18 @@ class Browser:
|
||||||
elif command == "set-render-mode":
|
elif command == "set-render-mode":
|
||||||
self.set_render_mode(words[1])
|
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."""
|
"""Get user input from the command-line."""
|
||||||
self.set_status(status_text)
|
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()
|
self.reset_status()
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
if strip:
|
if strip:
|
||||||
result = result.strip()
|
result = result.strip()
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -35,7 +35,11 @@ def open_file(browser: Browser, filepath: str, encoding="utf-8"):
|
||||||
except (OSError, ValueError) as exc:
|
except (OSError, ValueError) as exc:
|
||||||
browser.set_status_error(f"Failed to open file: {exc}")
|
browser.set_status_error(f"Failed to open file: {exc}")
|
||||||
return None
|
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():
|
elif path.is_dir():
|
||||||
gemtext = str(path) + "\n\n"
|
gemtext = str(path) + "\n\n"
|
||||||
for entry in sorted(path.iterdir()):
|
for entry in sorted(path.iterdir()):
|
||||||
|
|
|
@ -294,7 +294,7 @@ def _handle_cert_required(
|
||||||
|
|
||||||
url_identities = get_identities_for_url(browser.identities, url)
|
url_identities = get_identities_for_url(browser.identities, url)
|
||||||
if not url_identities:
|
if not url_identities:
|
||||||
identity = create_identity(browser, url)
|
identity = create_identity(browser, url, reason=response.meta)
|
||||||
if not identity:
|
if not identity:
|
||||||
return None
|
return None
|
||||||
browser.identities[url] = [identity]
|
browser.identities[url] = [identity]
|
||||||
|
@ -317,23 +317,27 @@ def select_identity(identities: list):
|
||||||
return identities[0] if identities else None
|
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.
|
"""Walk the user through identity creation.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The created identity on success (already registered in identities
|
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":
|
if key != "y":
|
||||||
browser.reset_status()
|
browser.reset_status()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
common_name = browser.get_user_text_input(
|
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,
|
CommandLine.CHAR_TEXT,
|
||||||
strip=True,
|
strip=True,
|
||||||
|
escape_to_none=True
|
||||||
)
|
)
|
||||||
if not common_name:
|
if common_name is None:
|
||||||
browser.reset_status()
|
browser.reset_status()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ DEFAULT_CONFIG = {
|
||||||
"-nodes",
|
"-nodes",
|
||||||
"-keyform", "PEM",
|
"-keyform", "PEM",
|
||||||
"-keyout", "{key_path}",
|
"-keyout", "{key_path}",
|
||||||
|
"-utf8",
|
||||||
"-x509",
|
"-x509",
|
||||||
"-days", "28140", # https://www.youtube.com/watch?v=F9L4q-0Pi4E
|
"-days", "28140", # https://www.youtube.com/watch?v=F9L4q-0Pi4E
|
||||||
"-outform", "PEM",
|
"-outform", "PEM",
|
||||||
|
|
|
@ -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
|
* gg: go to the top of the page
|
||||||
* G: go to the bottom of the page
|
* G: go to the bottom of the page
|
||||||
* o: open an URL
|
* 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
|
* p: go to the previous page
|
||||||
* u: go to the parent page (up a level in URL)
|
* u: go to the parent page (up a level in URL)
|
||||||
* U: go to the root page (root URL for the current domain)
|
* U: go to the root page (root URL for the current domain)
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
In Bebop we use a list of elements as produced by our parser. These elements are
|
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
|
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.
|
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
|
import string
|
||||||
|
@ -33,6 +36,7 @@ class LineType(IntEnum):
|
||||||
PREFORMATTED = 6
|
PREFORMATTED = 6
|
||||||
BLOCKQUOTE = 7
|
BLOCKQUOTE = 7
|
||||||
LIST_ITEM = 8
|
LIST_ITEM = 8
|
||||||
|
ERROR = 9 # Not part of Gemtext but useful internally.
|
||||||
|
|
||||||
|
|
||||||
def generate_metalines(elements, width, dumb=False):
|
def generate_metalines(elements, width, dumb=False):
|
||||||
|
|
|
@ -70,5 +70,7 @@ def get_base_line_attributes(line_type) -> int:
|
||||||
return curses.color_pair(ColorPair.PREFORMATTED)
|
return curses.color_pair(ColorPair.PREFORMATTED)
|
||||||
elif line_type == LineType.BLOCKQUOTE:
|
elif line_type == LineType.BLOCKQUOTE:
|
||||||
return curses.color_pair(ColorPair.BLOCKQUOTE) | A_ITALIC
|
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
|
else: # includes LineType.PARAGRAPH
|
||||||
return curses.color_pair(ColorPair.NORMAL)
|
return curses.color_pair(ColorPair.NORMAL)
|
||||||
|
|
|
@ -53,7 +53,6 @@ USER_FRIENDLY_TYPES = {t.name.lower(): t for t in ItemType}
|
||||||
ICONS = {
|
ICONS = {
|
||||||
ItemType.FILE: "📄",
|
ItemType.FILE: "📄",
|
||||||
ItemType.DIR: "📂",
|
ItemType.DIR: "📂",
|
||||||
ItemType.ERROR: "❌",
|
|
||||||
ItemType.SEARCH: "✍ ",
|
ItemType.SEARCH: "✍ ",
|
||||||
ItemType.HTML: "🌐",
|
ItemType.HTML: "🌐",
|
||||||
}
|
}
|
||||||
|
@ -256,19 +255,26 @@ def parse_source(source: str, item_type: ItemType):
|
||||||
break
|
break
|
||||||
|
|
||||||
parts = tline.split("\t")
|
parts = tline.split("\t")
|
||||||
if len(parts) != 4:
|
# If the map is poorly formatted and parts are missing, pad with
|
||||||
# TODO move me away
|
# empty parts.
|
||||||
# Does not seem to be split by tabs, may be a file.
|
while len(parts) < 4:
|
||||||
metalines.append(({"type": LineType.PARAGRAPH}, line))
|
parts.append("")
|
||||||
continue
|
|
||||||
|
|
||||||
item_type = ItemType(ltype)
|
item_type = ItemType(ltype)
|
||||||
label, path, host, port = parts
|
label, path, host, port = parts
|
||||||
|
|
||||||
|
# INFO: render as a simple text line.
|
||||||
if item_type == ItemType.INFO:
|
if item_type == ItemType.INFO:
|
||||||
meta = {"type": LineType.PARAGRAPH}
|
metalines.append(({"type": LineType.PARAGRAPH}, label))
|
||||||
metalines.append((meta, label))
|
|
||||||
continue
|
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:":
|
if item_type == ItemType.HTML and path[:4].upper() == "URL:":
|
||||||
link_url = path[4:]
|
link_url = path[4:]
|
||||||
else:
|
else:
|
||||||
|
|
Reference in a new issue