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.
- nicegui_treebrowser-0.1.0/.github/workflows/release.yml +40 -0
- nicegui_treebrowser-0.1.0/.github/workflows/tests.yml +37 -0
- nicegui_treebrowser-0.1.0/.gitignore +13 -0
- nicegui_treebrowser-0.1.0/.python-version +1 -0
- nicegui_treebrowser-0.1.0/LICENSE +21 -0
- nicegui_treebrowser-0.1.0/PKG-INFO +143 -0
- nicegui_treebrowser-0.1.0/README.md +120 -0
- nicegui_treebrowser-0.1.0/examples/.gitignore +1 -0
- nicegui_treebrowser-0.1.0/examples/demo.py +82 -0
- nicegui_treebrowser-0.1.0/pyproject.toml +66 -0
- nicegui_treebrowser-0.1.0/src/nicegui_treebrowser/__init__.py +7 -0
- nicegui_treebrowser-0.1.0/src/nicegui_treebrowser/browser.py +474 -0
- nicegui_treebrowser-0.1.0/src/nicegui_treebrowser/filetypes.py +169 -0
- nicegui_treebrowser-0.1.0/src/nicegui_treebrowser/fsutil.py +88 -0
- nicegui_treebrowser-0.1.0/src/nicegui_treebrowser/py.typed +0 -0
- nicegui_treebrowser-0.1.0/tests/__init__.py +0 -0
- nicegui_treebrowser-0.1.0/tests/conftest.py +63 -0
- nicegui_treebrowser-0.1.0/tests/test_browser.py +376 -0
- nicegui_treebrowser-0.1.0/tests/test_filetypes.py +115 -0
- nicegui_treebrowser-0.1.0/tests/test_fsutil.py +120 -0
- nicegui_treebrowser-0.1.0/tests/test_security.py +176 -0
- nicegui_treebrowser-0.1.0/uv.lock +1622 -0
|
@@ -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 @@
|
|
|
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
|
+
[](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
|
+
[](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
|