Compare commits
3 commits
80ff6b2219
...
6f5610bff9
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f5610bff9 | |||
| 69da2a4df9 | |||
| 5015d9b700 |
4 changed files with 40 additions and 6 deletions
|
|
@ -32,3 +32,10 @@ repos:
|
|||
args: [ --fix, --preview ]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: Run the mypy type linter
|
||||
language: system
|
||||
types: [python]
|
||||
entry: 'make mypy'
|
||||
|
|
|
|||
|
|
@ -75,9 +75,16 @@ Your Operating System may report that `ttotp` "pasted from the clipboard".
|
|||
This is because `ttotp` tries to only clear values that it set,
|
||||
by checking that the current clipboard value is equal to the value it pasted earlier.
|
||||
|
||||
Search for a key by pressing "/" and then entering a case insensitive regular expression.
|
||||
Search for a key by pressing "/" and then entering a modified case insensitive regular expression.
|
||||
Press Ctrl+A to show all keys again.
|
||||
|
||||
In this type of regular expression, a space ` ` stands for "zero or more characters, followed by whitespace, followed by zero or more characters"; the sequence backslash-space stands for a literal space.
|
||||
|
||||
This makes it easy to search for e.g., "Jay Doe / example.com" by entering "ja d ex", while not requiring any sophisticated fuzzy search technology.
|
||||
|
||||
Due to the simple way this is implemented, a space character inside a character class does not function as expected.
|
||||
Since complicated regular expressions are likely seldom used, this is not likely to be a huge limitation.
|
||||
|
||||
Exit the app with Ctrl+C.
|
||||
|
||||
# In-memory storage of TOTPs
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
textual
|
||||
pyperclip
|
||||
pyotp
|
||||
click
|
||||
platformdirs
|
||||
pyotp
|
||||
pyperclip
|
||||
textual
|
||||
|
|
|
|||
|
|
@ -176,11 +176,12 @@ class SearchInput(Input, can_focus=False):
|
|||
]
|
||||
|
||||
def on_focus(self) -> None:
|
||||
self.placeholder = "Enter regular expression"
|
||||
self.placeholder = "Enter search expression"
|
||||
|
||||
def on_blur(self) -> None:
|
||||
self.placeholder = "Type / to search"
|
||||
self.can_focus = False
|
||||
self.remove_class("error")
|
||||
|
||||
|
||||
class TOTPButton(Button, can_focus=False):
|
||||
|
|
@ -243,6 +244,18 @@ class TOTPData:
|
|||
)
|
||||
|
||||
|
||||
def search_preprocess(s: str) -> str:
|
||||
def replace_escape_sequence(m: re.Match[str]) -> str:
|
||||
s = m.group(0)
|
||||
if s == "\\ ":
|
||||
return " "
|
||||
if s == " ":
|
||||
return r".*\s+.*"
|
||||
return s
|
||||
|
||||
return re.sub(r"\\.| |[^\\ ]+", replace_escape_sequence, s)
|
||||
|
||||
|
||||
class TTOTP(App[None]):
|
||||
CSS = """
|
||||
VerticalScroll { min-height: 1; }
|
||||
|
|
@ -256,6 +269,7 @@ class TTOTP(App[None]):
|
|||
Button { border: none; height: 1; width: 3; min-width: 4 }
|
||||
Horizontal { height: 1; }
|
||||
Input { border: none; height: 1; width: 1fr; }
|
||||
Input.error { background: $error; }
|
||||
"""
|
||||
|
||||
BINDINGS = [
|
||||
|
|
@ -339,7 +353,13 @@ class TTOTP(App[None]):
|
|||
self.screen.focus_next()
|
||||
|
||||
def on_input_changed(self, event: Input.Changed) -> None:
|
||||
rx = re.compile(event.value or ".", re.I)
|
||||
haystack = event.value.replace(" ", ".* .*")
|
||||
try:
|
||||
rx = re.compile(haystack, re.I)
|
||||
except re.error:
|
||||
self.search.add_class("error")
|
||||
return
|
||||
self.search.remove_class("error")
|
||||
for otp in self.otp_data:
|
||||
parent = otp.name_widget.parent
|
||||
assert parent is not None
|
||||
|
|
|
|||
Loading…
Reference in a new issue