From 496ea791d5e294b8011b269511a62c7ba24dde54 Mon Sep 17 00:00:00 2001 From: Justin Flannery Date: Wed, 11 Oct 2023 21:01:16 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20parent=20dir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (#28) --- browsr/_base.py | 9 +- browsr/_cli.py | 7 +- browsr/browsr.py | 17 +- pyproject.toml | 4 +- requirements/requirements-dev.txt | 42 +-- requirements/requirements-prod.txt | 6 +- tests/screenshots/test_github_screenshot.svg | 246 +++++++++--------- .../test_github_screenshot_license.svg | 220 ++++++++-------- 8 files changed, 285 insertions(+), 266 deletions(-) diff --git a/browsr/_base.py b/browsr/_base.py index 9e59708..ce5fa79 100644 --- a/browsr/_base.py +++ b/browsr/_base.py @@ -5,9 +5,10 @@ from __future__ import annotations import math +import os import pathlib from copy import copy -from dataclasses import dataclass +from dataclasses import dataclass, field from os import PathLike from textwrap import dedent from typing import Any, ClassVar, Dict, List, Optional, Union @@ -52,7 +53,7 @@ class TextualAppContext: App Context Object """ - file_path: Optional[str] = None + file_path: str = field(default_factory=os.getcwd) config: Optional[Dict[str, Any]] = None debug: bool = False max_file_size: int = 20 @@ -72,7 +73,7 @@ def path(self) -> pathlib.Path: file_path = file_path[:-4] file_path = handle_github_url(url=str(file_path)) self.file_path = file_path - if str(self.file_path).endswith("/"): + if str(self.file_path).endswith("/") and len(str(self.file_path)) > 1: self.file_path = str(self.file_path)[:-1] kwargs = self.kwargs or {} PathClass = copy(BrowsrPath) @@ -111,7 +112,7 @@ def __init__( like a dictionary to pass into an application """ super().__init__() - self.config_object = config_object + self.config_object = config_object or TextualAppContext() traceback.install(show_locals=True) @staticmethod diff --git a/browsr/_cli.py b/browsr/_cli.py index 9a6b1b7..a74d07a 100644 --- a/browsr/_cli.py +++ b/browsr/_cli.py @@ -2,6 +2,7 @@ browsr command line interface """ +import os from typing import Optional, Tuple import click @@ -186,8 +187,12 @@ def browsr( message=f"Invalid Key/Value pair: `{kwarg}` - must be in the format Key=Value", param_hint="kwargs", ) from ve + file_path = path or os.getcwd() config = TextualAppContext( - file_path=path, debug=debug, max_file_size=max_file_size, kwargs=extra_kwargs + file_path=file_path, + debug=debug, + max_file_size=max_file_size, + kwargs=extra_kwargs, ) app = Browsr(config_object=config) app.run() diff --git a/browsr/browsr.py b/browsr/browsr.py index 805d336..ae77b8f 100644 --- a/browsr/browsr.py +++ b/browsr/browsr.py @@ -6,6 +6,7 @@ """ import json +import os import pathlib import shutil from os import getenv @@ -65,6 +66,7 @@ class Browsr(BrowsrTextualApp): Binding(key="t", action="theme", description="Toggle Theme"), Binding(key="n", action="linenos", description="Toggle Line Numbers"), Binding(key="d", action="toggle_dark", description="Toggle Dark Mode"), + Binding(key=".", action="parent_dir", description="Parent Directory"), ] show_tree = var(True) @@ -189,7 +191,7 @@ def _handle_file_size(self, file_info: FileInfo) -> None: Handle a File Size """ file_size_mb = file_info.size / 1000 / 1000 - too_large = file_size_mb >= self.config_object.max_file_size # type: ignore[union-attr] + too_large = file_size_mb >= self.config_object.max_file_size exception = ( True if is_local_path(file_info.file) and ".csv" in file_info.file.suffixes @@ -388,9 +390,20 @@ def action_download_file(self) -> None: self.table_view.display = False self.confirmation_window.display = True + def action_parent_dir(self) -> None: + """ + Go to the parent directory + """ + new_path = self.config_object.path.parent.resolve() + if new_path != self.config_object.path: + self.config_object.file_path = str(new_path) + self.directory_tree.path = new_path + app = Browsr( - config_object=TextualAppContext(file_path=getenv("BROWSR_PATH"), debug=True) + config_object=TextualAppContext( + file_path=getenv("BROWSR_PATH", os.getcwd()), debug=True + ) ) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 0f78eda..cf27190 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "rich~=13.5.2", "rich-click~=1.5.2", "rich-pixels~=2.1.1", - "textual==0.36.0", + "textual==0.39.0", "textual-universal-directorytree~=1.0.1", "Pillow>=9.1.0", "PyMuPDF~=1.22.3" @@ -75,7 +75,7 @@ dependencies = [ "mkdocs-literate-nav~=0.6.0", "mkdocs-section-index~=0.3.5", "black~=23.3.0", - "ruff~=0.0.261", + "ruff~=0.0.292", "mypy~=1.2.0", "pandas-stubs~=2.0.0.230412", "pip-tools~=6.13.0", diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index b22dae2..3ef46f5 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1521,24 +1521,24 @@ rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 # via google-auth -ruff==0.0.280 \ - --hash=sha256:2dae8f2d9c44c5c49af01733c2f7956f808db682a4193180dedb29dd718d7bbe \ - --hash=sha256:2e7c15828d09f90e97bea8feefcd2907e8c8ce3a1f959c99f9b4b3469679f33c \ - --hash=sha256:37359cd67d2af8e09110a546507c302cbea11c66a52d2a9b6d841d465f9962d4 \ - --hash=sha256:48ed5aca381050a4e2f6d232db912d2e4e98e61648b513c350990c351125aaec \ - --hash=sha256:4a7d52457b5dfcd3ab24b0b38eefaead8e2dca62b4fbf10de4cd0938cf20ce30 \ - --hash=sha256:581c43e4ac5e5a7117ad7da2120d960a4a99e68ec4021ec3cd47fe1cf78f8380 \ - --hash=sha256:5f972567163a20fb8c2d6afc60c2ea5ef8b68d69505760a8bd0377de8984b4f6 \ - --hash=sha256:7008fc6ca1df18b21fa98bdcfc711dad5f94d0fc3c11791f65e460c48ef27c82 \ - --hash=sha256:7784e3606352fcfb193f3cd22b2e2117c444cb879ef6609ec69deabd662b0763 \ - --hash=sha256:7a37dab70114671d273f203268f6c3366c035fe0c8056614069e90a65e614bfc \ - --hash=sha256:83e8f372fa5627eeda5b83b5a9632d2f9c88fc6d78cead7e2a1f6fb05728d137 \ - --hash=sha256:8ffa7347ad11643f29de100977c055e47c988cd6d9f5f5ff83027600b11b9189 \ - --hash=sha256:b7de5b8689575918e130e4384ed9f539ce91d067c0a332aedef6ca7188adac2d \ - --hash=sha256:bd58af46b0221efb95966f1f0f7576df711cb53e50d2fdb0e83c2f33360116a4 \ - --hash=sha256:d878370f7e9463ac40c253724229314ff6ebe4508cdb96cb536e1af4d5a9cd4f \ - --hash=sha256:ef6ee3e429fd29d6a5ceed295809e376e6ece5b0f13c7e703efaf3d3bcb30b96 \ - --hash=sha256:fe7118c1eae3fda17ceb409629c7f3b5a22dffa7caf1f6796776936dca1fe653 +ruff==0.0.292 \ + --hash=sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96 \ + --hash=sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac \ + --hash=sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade \ + --hash=sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205 \ + --hash=sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4 \ + --hash=sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68 \ + --hash=sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0 \ + --hash=sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9 \ + --hash=sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81 \ + --hash=sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8 \ + --hash=sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0 \ + --hash=sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016 \ + --hash=sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7 \ + --hash=sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003 \ + --hash=sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a \ + --hash=sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c \ + --hash=sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4 # via -r requirements.in s3fs==2023.6.0 \ --hash=sha256:63fd8ddf05eb722de784b7b503196107f2a518061298cf005a8a4715b4d49117 \ @@ -1554,9 +1554,9 @@ six==1.16.0 \ # isodate # python-dateutil # vcrpy -textual==0.36.0 \ - --hash=sha256:7d04880bee0274f8cdf05cbe22d9effad3efa458676af2c431997a6d4576005c \ - --hash=sha256:fbfc799a55938cfade6cfbf7c5ae3c3e5fc87ff9deaaed788a6dcefe72245451 +textual==0.39.0 \ + --hash=sha256:4103c0cb4e87bf9f87f4960213d3c1c33fbe7301a1a1c2df0903ecb62212983c \ + --hash=sha256:bf98d7eb5a8a39ed87488640e2a18b2825dd340d36eab6193c9a47af98a3b818 # via # -r requirements.in # textual-dev diff --git a/requirements/requirements-prod.txt b/requirements/requirements-prod.txt index 7869e83..78906a3 100644 --- a/requirements/requirements-prod.txt +++ b/requirements/requirements-prod.txt @@ -988,9 +988,9 @@ six==1.16.0 \ # google-auth # isodate # python-dateutil -textual==0.36.0 \ - --hash=sha256:7d04880bee0274f8cdf05cbe22d9effad3efa458676af2c431997a6d4576005c \ - --hash=sha256:fbfc799a55938cfade6cfbf7c5ae3c3e5fc87ff9deaaed788a6dcefe72245451 +textual==0.39.0 \ + --hash=sha256:4103c0cb4e87bf9f87f4960213d3c1c33fbe7301a1a1c2df0903ecb62212983c \ + --hash=sha256:bf98d7eb5a8a39ed87488640e2a18b2825dd340d36eab6193c9a47af98a3b818 # via # -r requirements.in # textual-universal-directorytree diff --git a/tests/screenshots/test_github_screenshot.svg b/tests/screenshots/test_github_screenshot.svg index 81299d6..3258d21 100644 --- a/tests/screenshots/test_github_screenshot.svg +++ b/tests/screenshots/test_github_screenshot.svg @@ -19,247 +19,247 @@ font-weight: 700; } - .terminal-4201891492-matrix { + .terminal-1049528061-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4201891492-title { + .terminal-1049528061-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4201891492-r1 { fill: #c5c8c6 } -.terminal-4201891492-r2 { fill: #e0e0e0 } -.terminal-4201891492-r3 { fill: #909090 } -.terminal-4201891492-r4 { fill: #e2e3e3 } -.terminal-4201891492-r5 { fill: #211505;font-weight: bold } -.terminal-4201891492-r6 { fill: #dfdfdf } -.terminal-4201891492-r7 { fill: #fea62b;font-weight: bold } -.terminal-4201891492-r8 { fill: #919497;font-weight: bold } -.terminal-4201891492-r9 { fill: #dfdfdf;font-weight: bold } -.terminal-4201891492-r10 { fill: #e2e3e3;font-weight: bold } -.terminal-4201891492-r11 { fill: #608ab1;text-decoration: underline; } -.terminal-4201891492-r12 { fill: #919497;font-style: italic; } -.terminal-4201891492-r13 { fill: #68a0b3;font-weight: bold } -.terminal-4201891492-r14 { fill: #e2e3e3;font-style: italic; } -.terminal-4201891492-r15 { fill: #dfdfdf;font-weight: bold;text-decoration: underline; } -.terminal-4201891492-r16 { fill: #f8f8f2 } -.terminal-4201891492-r17 { fill: #e6db74 } -.terminal-4201891492-r18 { fill: #14191f } -.terminal-4201891492-r19 { fill: #84a4cc } -.terminal-4201891492-r20 { fill: #dde8f3;font-weight: bold } -.terminal-4201891492-r21 { fill: #ddedf9 } + .terminal-1049528061-r1 { fill: #c5c8c6 } +.terminal-1049528061-r2 { fill: #e0e0e0 } +.terminal-1049528061-r3 { fill: #909090 } +.terminal-1049528061-r4 { fill: #e2e3e3 } +.terminal-1049528061-r5 { fill: #211505;font-weight: bold } +.terminal-1049528061-r6 { fill: #dfdfdf } +.terminal-1049528061-r7 { fill: #fea62b;font-weight: bold } +.terminal-1049528061-r8 { fill: #919497;font-weight: bold } +.terminal-1049528061-r9 { fill: #dfdfdf;font-weight: bold } +.terminal-1049528061-r10 { fill: #e2e3e3;font-weight: bold } +.terminal-1049528061-r11 { fill: #608ab1;text-decoration: underline; } +.terminal-1049528061-r12 { fill: #919497;font-style: italic; } +.terminal-1049528061-r13 { fill: #68a0b3;font-weight: bold } +.terminal-1049528061-r14 { fill: #e2e3e3;font-style: italic; } +.terminal-1049528061-r15 { fill: #dfdfdf;font-weight: bold;text-decoration: underline; } +.terminal-1049528061-r16 { fill: #f8f8f2 } +.terminal-1049528061-r17 { fill: #e6db74 } +.terminal-1049528061-r18 { fill: #14191f } +.terminal-1049528061-r19 { fill: #84a4cc } +.terminal-1049528061-r20 { fill: #dde8f3;font-weight: bold } +.terminal-1049528061-r21 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - browsr + browsr - - - - browsr — github://juftin:browsr@v1.6.0/README.md [monokai] -📂 github://juftin:browsr@v1.6.0/┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ -┣━━ 📁 .githubbrowsr -┣━━ 📁 browsr┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -┣━━ 📁 docs -┣━━ 📁 requirements -┣━━ 📁 tests🌆 browsr Version🌆 PyPI🌆 Testing Status🌆 GitHub License -┣━━ 📄 .gitignore -┣━━ 📄 .pre-commit-config.yamlbrowsr is a TUI (text-based user interface) file browser for your terminal. It's a simple way to browse your files and    -┣━━ 📄 .releaserc.jstake a peek at their contents. Plus it works on local and remote file systems.                                            -┣━━ 📄 LICENSE -┣━━ 📄 mkdocs.yaml -┣━━ 📄 pyproject.toml -┗━━ 📄 README.md - -Installation - -The below command recommends pipx instead of pip. pipx installs the package in an isolated environment and makes it easy  -to uninstall. If you'd like to use pip instead, just replace pipx with pip in the below command.                          - - -pipxinstallbrowsr - - - -Extra Installation - -If you're looking to use browsr on remote file systems, like AWS S3, you'll need to install the remote extra. If you'd    -like to browse parquet files, you'll need to install the parquet extra. Or, even simpler, you can install the all extra   -to get all the extras.                                                                                                    - - -pipxinstall"browsr[all]" - - - -Usage - -▄▄ -browsr~/Downloads/ - - -Simply give browsr a path to a file/directory and it will open a browser window with a file browser. You can also give it -a URL to a remote file system, like AWS S3.                                                                               - - -🗄️️️  3KB  💾  README.md  📂  juftin:browsr@v1.6.0 - Q  Quit  F  Toggle Files  T  Toggle Theme  N  Toggle Line Numbers  D  Toggle Dark Mode  X  Download File  + + + + browsr — github://juftin:browsr@v1.6.0/README.md [monokai] +📂 github://juftin:browsr@v1.6.0/┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +┣━━ 📁 .githubbrowsr +┣━━ 📁 browsr┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +┣━━ 📁 docs +┣━━ 📁 requirements +┣━━ 📁 tests🌆 browsr Version🌆 PyPI🌆 Testing Status🌆 GitHub License +┣━━ 📄 .gitignore +┣━━ 📄 .pre-commit-config.yamlbrowsr is a TUI (text-based user interface) file browser for your terminal. It's a simple way to browse your files and    +┣━━ 📄 .releaserc.jstake a peek at their contents. Plus it works on local and remote file systems.                                            +┣━━ 📄 LICENSE +┣━━ 📄 mkdocs.yaml +┣━━ 📄 pyproject.toml +┗━━ 📄 README.md + +Installation + +The below command recommends pipx instead of pip. pipx installs the package in an isolated environment and makes it easy  +to uninstall. If you'd like to use pip instead, just replace pipx with pip in the below command.                          + + +pipxinstallbrowsr + + + +Extra Installation + +If you're looking to use browsr on remote file systems, like AWS S3, you'll need to install the remote extra. If you'd    +like to browse parquet files, you'll need to install the parquet extra. Or, even simpler, you can install the all extra   +to get all the extras.                                                                                                    + + +pipxinstall"browsr[all]" + + + +Usage + +▄▄ +browsr~/Downloads/ + + +Simply give browsr a path to a file/directory and it will open a browser window with a file browser. You can also give it +a URL to a remote file system, like AWS S3.                                                                               + + +🗄️️️  3KB  💾  README.md  📂  juftin:browsr@v1.6.0 + Q  Quit  F  Toggle Files  T  Toggle Theme  N  Toggle Line Numbers  D  Toggle Dark Mode  .  Parent Directory  X  Download File  diff --git a/tests/screenshots/test_github_screenshot_license.svg b/tests/screenshots/test_github_screenshot_license.svg index 2dc9b8e..f55326e 100644 --- a/tests/screenshots/test_github_screenshot_license.svg +++ b/tests/screenshots/test_github_screenshot_license.svg @@ -19,234 +19,234 @@ font-weight: 700; } - .terminal-607049458-matrix { + .terminal-981571403-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-607049458-title { + .terminal-981571403-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-607049458-r1 { fill: #c5c8c6 } -.terminal-607049458-r2 { fill: #e0e0e0 } -.terminal-607049458-r3 { fill: #909090 } -.terminal-607049458-r4 { fill: #dfdfdf } -.terminal-607049458-r5 { fill: #23568b } -.terminal-607049458-r6 { fill: #84a4cc } -.terminal-607049458-r7 { fill: #dde8f3;font-weight: bold } -.terminal-607049458-r8 { fill: #ddedf9 } + .terminal-981571403-r1 { fill: #c5c8c6 } +.terminal-981571403-r2 { fill: #e0e0e0 } +.terminal-981571403-r3 { fill: #909090 } +.terminal-981571403-r4 { fill: #dfdfdf } +.terminal-981571403-r5 { fill: #23568b } +.terminal-981571403-r6 { fill: #84a4cc } +.terminal-981571403-r7 { fill: #dde8f3;font-weight: bold } +.terminal-981571403-r8 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - browsr + browsr - - - - browsr — github://juftin:browsr@v1.6.0/LICENSE [monokai] -MIT License                                                                                                                                                    - -Copyright (c) 2023-present Justin Flannery <justin.flannery@juftin.com>                                                                                        - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in  - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.                                 - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -🗄️️️  1KB  💾  LICENSE  📂  juftin:browsr@v1.6.0 - Q  Quit  F  Toggle Files  T  Toggle Theme  N  Toggle Line Numbers  D  Toggle Dark Mode  X  Download File  + + + + browsr — github://juftin:browsr@v1.6.0/LICENSE [monokai] +MIT License                                                                                                                                                    + +Copyright (c) 2023-present Justin Flannery <justin.flannery@juftin.com>                                                                                        + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in  + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.                                 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +🗄️️️  1KB  💾  LICENSE  📂  juftin:browsr@v1.6.0 + Q  Quit  F  Toggle Files  T  Toggle Theme  N  Toggle Line Numbers  D  Toggle Dark Mode  .  Parent Directory  X  Download File