nicegui-treebrowser 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v7
12
+ - uses: astral-sh/setup-uv@v10.2.0
13
+ with:
14
+ enable-cache: true
15
+ - name: Check tag matches project version
16
+ run: |
17
+ tag="${GITHUB_REF_NAME#v}"
18
+ version="$(uv version --short)"
19
+ if [ "$tag" != "$version" ]; then
20
+ echo "Tag $GITHUB_REF_NAME does not match project version $version" >&2
21
+ exit 1
22
+ fi
23
+ - run: uv build
24
+ - uses: actions/upload-artifact@v5
25
+ with:
26
+ name: dist
27
+ path: dist/
28
+
29
+ publish:
30
+ needs: build
31
+ runs-on: ubuntu-latest
32
+ environment: pypi
33
+ permissions:
34
+ id-token: write
35
+ steps:
36
+ - uses: actions/download-artifact@v6
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,37 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ concurrency:
8
+ group: ${{ github.workflow }}-${{ github.ref }}
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.13", "3.14"]
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+ - uses: astral-sh/setup-uv@v10.2.0
21
+ with:
22
+ enable-cache: true
23
+ python-version: ${{ matrix.python-version }}
24
+ - run: uv sync --locked
25
+ - run: uv run pytest
26
+
27
+ lint:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v7
31
+ - uses: astral-sh/setup-uv@v10.2.0
32
+ with:
33
+ enable-cache: true
34
+ - run: uv sync --locked
35
+ - run: uv run ruff check .
36
+ - run: uv run ruff format --check .
37
+ - run: uv run mypy src tests
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ build/
6
+ dist/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
13
+ .DS_Store
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Owen Solberg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.5
2
+ Name: nicegui-treebrowser
3
+ Version: 0.1.0
4
+ Summary: A NiceGUI element that browses, previews and downloads a server-side directory tree
5
+ Project-URL: Homepage, https://github.com/odoublewen/nicegui-treebrowser
6
+ Project-URL: Issues, https://github.com/odoublewen/nicegui-treebrowser/issues
7
+ Author-email: Owen Solberg <owen.solberg@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: directory-tree,file-browser,gui,nicegui,widget
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Software Development :: User Interfaces
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.13
20
+ Requires-Dist: nicegui>=2.14; python_version < '3.14'
21
+ Requires-Dist: nicegui>=3.0.4; python_version >= '3.14'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # nicegui-treebrowser
25
+
26
+ [![tests](https://github.com/odoublewen/nicegui-treebrowser/actions/workflows/tests.yml/badge.svg?branch=develop)](https://github.com/odoublewen/nicegui-treebrowser/actions/workflows/tests.yml)
27
+
28
+ A [NiceGUI](https://nicegui.io) element that renders a server-side directory as a
29
+ browsable tree, previews the files it finds, and lets people download them
30
+ individually or as a `.tar.gz`.
31
+
32
+ It is an ordinary NiceGUI element: it renders where you create it and accepts
33
+ `.classes()`, `.style()`, `.props()` and `.move()` like anything else in `ui.*`.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install nicegui-treebrowser # or: uv add nicegui-treebrowser
39
+ ```
40
+
41
+ ### Supported versions
42
+
43
+ | Python | NiceGUI |
44
+ |---|---|
45
+ | 3.13 | 2.14+ or 3.x |
46
+ | 3.14 | 3.0.4+ |
47
+
48
+ NiceGUI releases before 3.0.4 pull in a `vbuild` that calls `pkgutil.find_loader`,
49
+ removed in Python 3.14, so they cannot be imported there at all. The dependency
50
+ markers pick a workable floor for you.
51
+
52
+ Python 3.15 is not supported yet: `aiohttp`, which NiceGUI requires, has no 3.15
53
+ wheels. Nothing in this package stands in the way — it should work as soon as the
54
+ wheels land.
55
+
56
+ The test suite runs against NiceGUI 2.14.1, 3.0.4 and 3.17.1.
57
+
58
+ ## Quickstart
59
+
60
+ ```python
61
+ from nicegui import ui
62
+ from nicegui_treebrowser import TreeBrowser
63
+
64
+
65
+ @ui.page("/")
66
+ def index() -> None:
67
+ TreeBrowser("/srv/results", title="Results")
68
+
69
+
70
+ ui.run()
71
+ ```
72
+
73
+ ## Options
74
+
75
+ | Parameter | Default | Meaning |
76
+ |---|---|---|
77
+ | `root` | — | Directory to expose. Nothing outside it is ever served. |
78
+ | `focus` | `None` | File or folder to reveal and open on load. |
79
+ | `title` | `"Files"` | Heading shown above the tree. |
80
+ | `show_hidden` | `False` | Include dot-files and dot-directories. |
81
+ | `exclude` | `None` | Predicate returning `True` for paths to leave out. |
82
+ | `max_preview_bytes` | `1_000_000` | Largest text preview loaded into the browser. |
83
+ | `max_table_rows` | `5000` | Row cap for `.csv`/`.tsv` previews. |
84
+ | `allow_archive` | `True` | Show the "Download all" and per-folder archive buttons. |
85
+ | `show_full_path` | `True` | Print the root's absolute path under the title. |
86
+ | `sniff_text` | `False` | Preview unrecognised files that look like UTF-8 text. |
87
+
88
+ Two methods are worth knowing:
89
+
90
+ - `browser.focus(path)` — select `path` and expand only the folders needed to reveal
91
+ it. Returns `False` if the path is outside the root or not in the tree.
92
+ - `browser.refresh()` — re-read the tree from disk.
93
+
94
+ ## What gets previewed
95
+
96
+ | Kind | Extensions |
97
+ |---|---|
98
+ | Image | `.png` `.jpg` `.jpeg` `.gif` `.webp` `.bmp` `.avif` `.ico` |
99
+ | PDF | `.pdf` |
100
+ | Audio | `.mp3` `.wav` `.ogg` `.oga` `.m4a` `.flac` `.aac` |
101
+ | Video | `.mp4` `.webm` `.ogv` `.mov` |
102
+ | Table | `.csv` `.tsv` |
103
+ | Markdown | `.md` `.markdown` |
104
+ | Source | ~40 suffixes (`.py` `.js` `.ts` `.json` `.yaml` `.toml` `.sql` `.go` `.rs` …) plus suffixless `Dockerfile`, `Makefile`, `Gemfile`, `Rakefile`, `justfile`, `LICENSE`, `NOTICE`, `AUTHORS`, `README` and `changelog` |
105
+
106
+ Anything else offers a download. Set `sniff_text=True` to additionally preview
107
+ unrecognised files whose first 8 KiB decode as UTF-8 and contain no NUL bytes.
108
+
109
+ `.svg` and `.html` are shown **as source**, not rendered — see below.
110
+
111
+ The tables are plain module-level dicts, so you can extend them:
112
+
113
+ ```python
114
+ from nicegui_treebrowser import filetypes
115
+
116
+ filetypes.CODE_LANGUAGES[".ino"] = "cpp"
117
+ filetypes.IMAGE_SUFFIXES.add(".jxl")
118
+ ```
119
+
120
+ ## Security
121
+
122
+ Read this before pointing the widget at anything sensitive.
123
+
124
+ - **`root` is the boundary.** Tree keys are resolved back through a dictionary built
125
+ by walking `root`, and the result is re-checked against `root` before anything is
126
+ served. A key naming a path outside the root, or one that was never in the tree,
127
+ resolves to nothing.
128
+ - **Symlinks are skipped entirely**, so a link inside the root cannot be used to read
129
+ or traverse outside it.
130
+ - **`exclude` and `show_hidden` are real filters**, not just display ones. A file they
131
+ keep out of the tree cannot be previewed or downloaded.
132
+ - **Previewing a file publishes it at an app URL** for as long as the preview is on
133
+ screen, and downloading publishes it briefly. Those URLs are unguessable in
134
+ practice but carry no authentication of their own — whatever middleware protects
135
+ your NiceGUI app protects them too, and nothing else does. If your app is public,
136
+ so is anything in `root`.
137
+ - **`.svg` and `.html` are deliberately shown as source.** Both are served from your
138
+ app's own origin, so rendering them would run any script they carry with your
139
+ app's cookies in scope.
140
+
141
+ ## License
142
+
143
+ MIT
@@ -0,0 +1,120 @@
1
+ # nicegui-treebrowser
2
+
3
+ [![tests](https://github.com/odoublewen/nicegui-treebrowser/actions/workflows/tests.yml/badge.svg?branch=develop)](https://github.com/odoublewen/nicegui-treebrowser/actions/workflows/tests.yml)
4
+
5
+ A [NiceGUI](https://nicegui.io) element that renders a server-side directory as a
6
+ browsable tree, previews the files it finds, and lets people download them
7
+ individually or as a `.tar.gz`.
8
+
9
+ It is an ordinary NiceGUI element: it renders where you create it and accepts
10
+ `.classes()`, `.style()`, `.props()` and `.move()` like anything else in `ui.*`.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install nicegui-treebrowser # or: uv add nicegui-treebrowser
16
+ ```
17
+
18
+ ### Supported versions
19
+
20
+ | Python | NiceGUI |
21
+ |---|---|
22
+ | 3.13 | 2.14+ or 3.x |
23
+ | 3.14 | 3.0.4+ |
24
+
25
+ NiceGUI releases before 3.0.4 pull in a `vbuild` that calls `pkgutil.find_loader`,
26
+ removed in Python 3.14, so they cannot be imported there at all. The dependency
27
+ markers pick a workable floor for you.
28
+
29
+ Python 3.15 is not supported yet: `aiohttp`, which NiceGUI requires, has no 3.15
30
+ wheels. Nothing in this package stands in the way — it should work as soon as the
31
+ wheels land.
32
+
33
+ The test suite runs against NiceGUI 2.14.1, 3.0.4 and 3.17.1.
34
+
35
+ ## Quickstart
36
+
37
+ ```python
38
+ from nicegui import ui
39
+ from nicegui_treebrowser import TreeBrowser
40
+
41
+
42
+ @ui.page("/")
43
+ def index() -> None:
44
+ TreeBrowser("/srv/results", title="Results")
45
+
46
+
47
+ ui.run()
48
+ ```
49
+
50
+ ## Options
51
+
52
+ | Parameter | Default | Meaning |
53
+ |---|---|---|
54
+ | `root` | — | Directory to expose. Nothing outside it is ever served. |
55
+ | `focus` | `None` | File or folder to reveal and open on load. |
56
+ | `title` | `"Files"` | Heading shown above the tree. |
57
+ | `show_hidden` | `False` | Include dot-files and dot-directories. |
58
+ | `exclude` | `None` | Predicate returning `True` for paths to leave out. |
59
+ | `max_preview_bytes` | `1_000_000` | Largest text preview loaded into the browser. |
60
+ | `max_table_rows` | `5000` | Row cap for `.csv`/`.tsv` previews. |
61
+ | `allow_archive` | `True` | Show the "Download all" and per-folder archive buttons. |
62
+ | `show_full_path` | `True` | Print the root's absolute path under the title. |
63
+ | `sniff_text` | `False` | Preview unrecognised files that look like UTF-8 text. |
64
+
65
+ Two methods are worth knowing:
66
+
67
+ - `browser.focus(path)` — select `path` and expand only the folders needed to reveal
68
+ it. Returns `False` if the path is outside the root or not in the tree.
69
+ - `browser.refresh()` — re-read the tree from disk.
70
+
71
+ ## What gets previewed
72
+
73
+ | Kind | Extensions |
74
+ |---|---|
75
+ | Image | `.png` `.jpg` `.jpeg` `.gif` `.webp` `.bmp` `.avif` `.ico` |
76
+ | PDF | `.pdf` |
77
+ | Audio | `.mp3` `.wav` `.ogg` `.oga` `.m4a` `.flac` `.aac` |
78
+ | Video | `.mp4` `.webm` `.ogv` `.mov` |
79
+ | Table | `.csv` `.tsv` |
80
+ | Markdown | `.md` `.markdown` |
81
+ | Source | ~40 suffixes (`.py` `.js` `.ts` `.json` `.yaml` `.toml` `.sql` `.go` `.rs` …) plus suffixless `Dockerfile`, `Makefile`, `Gemfile`, `Rakefile`, `justfile`, `LICENSE`, `NOTICE`, `AUTHORS`, `README` and `changelog` |
82
+
83
+ Anything else offers a download. Set `sniff_text=True` to additionally preview
84
+ unrecognised files whose first 8 KiB decode as UTF-8 and contain no NUL bytes.
85
+
86
+ `.svg` and `.html` are shown **as source**, not rendered — see below.
87
+
88
+ The tables are plain module-level dicts, so you can extend them:
89
+
90
+ ```python
91
+ from nicegui_treebrowser import filetypes
92
+
93
+ filetypes.CODE_LANGUAGES[".ino"] = "cpp"
94
+ filetypes.IMAGE_SUFFIXES.add(".jxl")
95
+ ```
96
+
97
+ ## Security
98
+
99
+ Read this before pointing the widget at anything sensitive.
100
+
101
+ - **`root` is the boundary.** Tree keys are resolved back through a dictionary built
102
+ by walking `root`, and the result is re-checked against `root` before anything is
103
+ served. A key naming a path outside the root, or one that was never in the tree,
104
+ resolves to nothing.
105
+ - **Symlinks are skipped entirely**, so a link inside the root cannot be used to read
106
+ or traverse outside it.
107
+ - **`exclude` and `show_hidden` are real filters**, not just display ones. A file they
108
+ keep out of the tree cannot be previewed or downloaded.
109
+ - **Previewing a file publishes it at an app URL** for as long as the preview is on
110
+ screen, and downloading publishes it briefly. Those URLs are unguessable in
111
+ practice but carry no authentication of their own — whatever middleware protects
112
+ your NiceGUI app protects them too, and nothing else does. If your app is public,
113
+ so is anything in `root`.
114
+ - **`.svg` and `.html` are deliberately shown as source.** Both are served from your
115
+ app's own origin, so rendering them would run any script they carry with your
116
+ app's cookies in scope.
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1 @@
1
+ sample/
@@ -0,0 +1,82 @@
1
+ """Run the browser against a generated sample directory.
2
+
3
+ uv run python examples/demo.py # strict allowlist (the default)
4
+ uv run python examples/demo.py --sniff # also preview unrecognised text files
5
+
6
+ Then open http://localhost:8080.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import base64
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from nicegui import ui
16
+
17
+ from nicegui_treebrowser import TreeBrowser
18
+
19
+ SAMPLE = Path(__file__).parent / "sample"
20
+
21
+ PNG = base64.b64decode(
22
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAE"
23
+ "hQGAhKmMIQAAAABJRU5ErkJggg=="
24
+ )
25
+
26
+ PDF = b"""%PDF-1.4
27
+ 1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj
28
+ 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj
29
+ 3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 200 200]>>endobj
30
+ trailer<</Root 1 0 R>>
31
+ %%EOF
32
+ """
33
+
34
+
35
+ def build_sample() -> None:
36
+ """Create a directory with one file of most previewable kinds."""
37
+ (SAMPLE / "reports").mkdir(parents=True, exist_ok=True)
38
+ (SAMPLE / "empty").mkdir(exist_ok=True)
39
+
40
+ (SAMPLE / "README.md").write_text(
41
+ "# Sample tree\n\nPick something on the left.\n\n- images\n- PDFs\n- tables\n"
42
+ )
43
+ (SAMPLE / "settings.yaml").write_text("debug: true\nthreads: 4\n")
44
+ (SAMPLE / "analysis.py").write_text(
45
+ "from pathlib import Path\n\n\ndef main() -> None:\n"
46
+ ' print(sorted(Path(".").glob("*")))\n'
47
+ )
48
+ (SAMPLE / "results.csv").write_text(
49
+ "sample,score,note\n"
50
+ + "".join(f"S{i:03d},{i * 7 % 100},row {i}\n" for i in range(200))
51
+ )
52
+ (SAMPLE / "matrix.tsv").write_text("gene\tcount\nTP53\t120\nBRCA1\t44\n")
53
+ (SAMPLE / "logo.svg").write_text(
54
+ '<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80">'
55
+ '<circle cx="40" cy="40" r="36" fill="teal"/></svg>\n'
56
+ )
57
+ (SAMPLE / "Dockerfile").write_text(
58
+ "FROM python:3.14-slim\nRUN pip install nicegui\n"
59
+ )
60
+ (SAMPLE / "changelog").write_text("0.1.0 - first cut, no suffix on this file\n")
61
+ (SAMPLE / "pixel.png").write_bytes(PNG)
62
+ (SAMPLE / "reports" / "summary.pdf").write_bytes(PDF)
63
+ (SAMPLE / "reports" / "run.log").write_text("started\nworking\nfinished\n")
64
+ (SAMPLE / "opaque.bin").write_bytes(bytes(range(256)) * 8)
65
+
66
+
67
+ def main() -> None:
68
+ build_sample()
69
+ sniff = "--sniff" in sys.argv
70
+
71
+ @ui.page("/")
72
+ def index() -> None:
73
+ ui.label("nicegui-treebrowser demo").classes("text-2xl font-medium px-6 pt-6")
74
+ ui.label(f"sniff_text={sniff} — restart with --sniff to flip it").classes(
75
+ "text-sm text-gray-500 px-6"
76
+ )
77
+ TreeBrowser(SAMPLE, title="Sample tree", focus="README.md", sniff_text=sniff)
78
+
79
+ ui.run(title="TreeBrowser demo", reload=False)
80
+
81
+
82
+ main()
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "nicegui-treebrowser"
7
+ version = "0.1.0"
8
+ description = "A NiceGUI element that browses, previews and downloads a server-side directory tree"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.13"
13
+ authors = [{ name = "Owen Solberg", email = "owen.solberg@gmail.com" }]
14
+ keywords = ["nicegui", "file-browser", "directory-tree", "widget", "gui"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Web Environment",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Topic :: Software Development :: User Interfaces",
23
+ "Typing :: Typed",
24
+ ]
25
+ # NiceGUI before 3.0.4 pulls in a vbuild that calls pkgutil.find_loader,
26
+ # removed in Python 3.14, so those releases cannot even be imported there.
27
+ dependencies = [
28
+ "nicegui>=2.14; python_version < '3.14'",
29
+ "nicegui>=3.0.4; python_version >= '3.14'",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/odoublewen/nicegui-treebrowser"
34
+ Issues = "https://github.com/odoublewen/nicegui-treebrowser/issues"
35
+
36
+ [dependency-groups]
37
+ dev = [
38
+ "pytest>=8.0",
39
+ "pytest-asyncio>=0.23",
40
+ "ruff>=0.6",
41
+ "mypy>=1.11",
42
+ "types-pygments>=2.21.0.20260819",
43
+ ]
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/nicegui_treebrowser"]
47
+
48
+ [tool.pytest.ini_options]
49
+ asyncio_mode = "auto"
50
+ asyncio_default_fixture_loop_scope = "function"
51
+ testpaths = ["tests"]
52
+ # Empty on purpose: NiceGUI 3.x's `user` fixture otherwise runpy's a main.py.
53
+ # With no main file, pages declared with @ui.page inside a test module are used.
54
+ main_file = ""
55
+
56
+ [tool.ruff]
57
+ line-length = 88
58
+ src = ["src", "tests"]
59
+
60
+ [tool.ruff.lint]
61
+ select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM", "RUF"]
62
+
63
+ [tool.mypy]
64
+ python_version = "3.13"
65
+ strict = true
66
+ warn_unused_ignores = false
@@ -0,0 +1,7 @@
1
+ """A NiceGUI element for browsing, previewing and downloading a
2
+ server-side directory."""
3
+
4
+ from .browser import TreeBrowser
5
+
6
+ __all__ = ["TreeBrowser", "__version__"]
7
+ __version__ = "0.1.0"