Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ textual update #48

Merged
merged 5 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions browsr/screens/code_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def __init__(
self.file_information.file_info = get_file_info(
file_path=self.code_browser.selected_file_path
)
else:
self.file_information.file_info = get_file_info(self.config_object.path)
self.footer = Footer()

def compose(self) -> Iterable[Widget]:
Expand Down
31 changes: 22 additions & 9 deletions browsr/widgets/double_click_directory_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

from __future__ import annotations

import os
import uuid
from typing import Any
import datetime
from typing import Any, ClassVar

from textual import on
from textual.message import Message
Expand All @@ -19,19 +18,28 @@ class DoubleClickDirectoryTree(DirectoryTree):
A DirectoryTree that can handle any filesystem.
"""

_double_click_time: ClassVar[datetime.timedelta] = datetime.timedelta(
seconds=0.333333
)

def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
Initialize the DirectoryTree
"""
super().__init__(*args, **kwargs)
self._last_clicked_path: os.PathLike[UPath] = UPath(uuid.uuid4().hex)
self._last_clicked_path: UPath = UPath(
"13041530b3174c569e1895fdfc2676fc57af1e02606059e0d2472d04c1bb360f"
)
self._last_clicked_time = datetime.datetime(
1970, 1, 1, tzinfo=datetime.timezone.utc
)

class DoubleClicked(Message):
"""
A message that is emitted when the directory is changed
"""

def __init__(self, path: os.PathLike[Any]) -> None:
def __init__(self, path: UPath) -> None:
"""
Initialize the message
"""
Expand Down Expand Up @@ -69,12 +77,17 @@ def handle_double_click_file(self, message: DirectoryTree.FileSelected) -> None:
message.stop()
self.post_message(self.FileDoubleClicked(path=message.path))

def is_double_click(self, path: os.PathLike[Any]) -> bool:
def is_double_click(self, path: UPath) -> bool:
"""
Check if the path is double clicked
"""
if self._last_clicked_path != path:
self._last_clicked_path = path
click_time = datetime.datetime.now(datetime.timezone.utc)
click_delta = click_time - self._last_clicked_time
self._last_clicked_time = click_time
if self._last_clicked_path == path and click_delta <= self._double_click_time:
return True
elif self._last_clicked_path == path and click_delta > self._double_click_time:
return False
else:
return True
self._last_clicked_path = path
return False
86 changes: 73 additions & 13 deletions browsr/widgets/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
from textual.message import Message
from textual.reactive import reactive
from textual.widget import Widget
from textual_universal_directorytree import (
GitHubTextualPath,
S3TextualPath,
SFTPTextualPath,
is_remote_path,
)

from browsr.utils import FileInfo

Expand Down Expand Up @@ -56,23 +62,77 @@ def render(self) -> RenderableType:
"""
Render the Current File Info Bar
"""
if self.file_info is None or not self.file_info.is_file:
return Text("")
status_string = "🗄️️️ " + self._convert_size(self.file_info.size)
status_string = self.render_file_protocol()
if self.file_info is None:
return Text(status_string)
elif self.file_info.is_file:
file_options = self.render_file_options()
status_string += file_options
if (
self.file_info.last_modified is not None
and self.file_info.last_modified.timestamp() != 0
):
modify_time = self.file_info.last_modified.strftime("%b %d, %Y %I:%M %p")
status_string += " 📅 " + modify_time
parent_name = self.file_info.file.parent.name
if not parent_name:
parent_name = str(self.file_info.file.parent)
parent_name = parent_name.lstrip(f"{self.file_info.file.protocol}://")
parent_name = parent_name.rstrip("/")
status_string += " 💾 " + self.file_info.file.name + " 📂 " + parent_name
status_string += f" 📅 {modify_time}"
directory_options = self.render_directory_options()
status_string += directory_options
return Text(status_string.strip(), style="dim")

def render_file_options(self) -> str:
"""
Render the file options
"""
status_string = ""
if not self.file_info:
return status_string
if self.file_info.is_file:
file_size = self._convert_size(self.file_info.size)
status_string += f" 🗄️️ {file_size}"
if self.file_info.owner not in ["", None]:
status_string += " 👤 " + self.file_info.owner
status_string += f" 👤 {self.file_info.owner}"
if self.file_info.group.strip() not in ["", None]:
status_string += " 🏠 " + self.file_info.group
return Text(status_string, style="dim")
status_string += f" 🏠 {self.file_info.group}"
return status_string

def render_directory_options(self) -> str:
"""
Render the directory options
"""
status_string = ""
if not self.file_info:
return status_string
if self.file_info.is_file:
directory_name = self.file_info.file.parent.name
if not directory_name or (
self.file_info.file.protocol
and f"{self.file_info.file.protocol}://" in directory_name
):
directory_name = str(self.file_info.file.parent)
directory_name = directory_name.lstrip(
f"{self.file_info.file.protocol}://"
)
directory_name = directory_name.rstrip("/")
status_string += f" 📂 {directory_name}"
status_string += f" 💾 {self.file_info.file.name}"
else:
status_string += f" 📂 {self.file_info.file.name}"
return status_string

def render_file_protocol(self) -> str:
"""
Render the file protocol
"""
status_string = ""
if not self.file_info:
return status_string
if is_remote_path(self.file_info.file):
if isinstance(self.file_info.file, GitHubTextualPath):
protocol = "GitHub"
elif isinstance(self.file_info.file, S3TextualPath):
protocol = "S3"
elif isinstance(self.file_info.file, SFTPTextualPath):
protocol = "SFTP"
else:
protocol = self.file_info.file.protocol
status_string += f"🗂️ {protocol}"
return status_string
2 changes: 1 addition & 1 deletion browsr/widgets/universal_directory_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from browsr.widgets.vim import vim_cursor_bindings


class BrowsrDirectoryTree(UniversalDirectoryTree, DoubleClickDirectoryTree):
class BrowsrDirectoryTree(DoubleClickDirectoryTree, UniversalDirectoryTree):
"""
A DirectoryTree that can handle any filesystem.
"""
Expand Down
11 changes: 9 additions & 2 deletions browsr/widgets/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,13 @@ class WindowSwitcher(Container):

show_tree: Reactive[bool] = reactive(True)

datatable_extensions: ClassVar[list[str]] = [".csv", ".parquet", ".feather", ".fea"]
datatable_extensions: ClassVar[list[str]] = [
".csv",
".parquet",
".feather",
".fea",
".csv.gz",
]
image_extensions: ClassVar[list[str]] = image_file_extensions.copy()
markdown_extensions: ClassVar[list[str]] = [".md"]
json_extensions: ClassVar[list[str]] = [".json"]
Expand Down Expand Up @@ -343,7 +349,8 @@ def render_file(self, file_path: UPath, scroll_home: bool = True) -> None:
Render a file
"""
switch_window = self.static_window
if file_path.suffix.lower() in self.datatable_extensions:
joined_suffixes = "".join(file_path.suffixes).lower()
if joined_suffixes in self.datatable_extensions:
self.datatable_window.refresh_from_file(
file_path=file_path, max_lines=self.config_object.max_lines
)
Expand Down
24 changes: 12 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ dependencies = [
"art~=6.1",
"click~=8.1.7",
"pandas>2,<3",
"rich~=13.7.0",
"rich-click~=1.7.3",
"rich~=13.7.1",
"rich-click~=1.7.4",
"rich-pixels~=2.2.0",
"textual==0.48.2",
"textual-universal-directorytree~=1.3.2",
"textual==0.53.1",
"textual-universal-directorytree~=1.5.0",
"universal-pathlib~=0.2.2",
"Pillow>=9.1.0",
"PyMuPDF~=1.23.21",
"Pillow>=10.2.0",
"PyMuPDF~=1.23.26",
"pyperclip~=1.8.2"
]
description = "TUI File Browser App"
Expand All @@ -42,17 +42,17 @@ requires-python = ">=3.8,<4.0"

[project.optional-dependencies]
all = [
"pyarrow~=15.0.0",
"textual-universal-directorytree[remote]~=1.3.2"
"pyarrow~=15.0.2",
"textual-universal-directorytree[remote]~=1.5.0"
]
data = [
"pyarrow~=15.0.0"
"pyarrow~=15.0.2"
]
parquet = [
"pyarrow~=15.0.0"
"pyarrow~=15.0.2"
]
remote = [
"textual-universal-directorytree[remote]~=1.3.2"
"textual-universal-directorytree[remote]~=1.5.0"
]

[project.scripts]
Expand Down Expand Up @@ -129,7 +129,7 @@ release = [

[tool.hatch.envs.lint]
dependencies = [
"mypy>=1.8.0",
"mypy>=1.9.0",
"ruff~=0.1.7"
]
detached = true
Expand Down
Loading