schenesort 2.2.0__py3-none-any.whl → 2.4.0__py3-none-any.whl

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,291 @@
1
+ """Thumbnail grid widget for gallery view."""
2
+
3
+ from pathlib import Path
4
+
5
+ from textual.app import ComposeResult
6
+ from textual.binding import Binding
7
+ from textual.containers import Container, VerticalScroll
8
+ from textual.message import Message
9
+ from textual.widgets import Static
10
+ from textual_image.widget import Image
11
+
12
+ from schenesort.thumbnails import get_thumbnail_path, thumbnail_exists
13
+
14
+
15
+ class ThumbnailText(Static):
16
+ """Text fallback for when no thumbnail exists."""
17
+
18
+ DEFAULT_CSS = """
19
+ ThumbnailText {
20
+ width: 32;
21
+ height: 14;
22
+ content-align: center middle;
23
+ text-align: center;
24
+ color: $text-muted;
25
+ background: $surface;
26
+ }
27
+
28
+ ThumbnailText.selected {
29
+ border: solid $primary;
30
+ }
31
+ """
32
+
33
+ def __init__(self, image_path: Path, index: int, **kwargs) -> None:
34
+ name = image_path.stem
35
+ if len(name) > 28:
36
+ name = name[:25] + "..."
37
+ super().__init__(name, **kwargs)
38
+ self.image_path = image_path
39
+ self.index = index
40
+
41
+
42
+ # Type alias for cell widgets
43
+ CellWidget = Image | ThumbnailText
44
+
45
+
46
+ def create_thumbnail_cell(image_path: Path, index: int) -> CellWidget:
47
+ """Create a cell widget for the given image using its thumbnail."""
48
+ if thumbnail_exists(image_path):
49
+ thumb_path = get_thumbnail_path(image_path)
50
+ img = Image(thumb_path)
51
+ # Store metadata on the widget
52
+ img.image_path = image_path # type: ignore[attr-defined]
53
+ img.index = index # type: ignore[attr-defined]
54
+ return img
55
+ return ThumbnailText(image_path, index)
56
+
57
+
58
+ class ThumbnailGrid(VerticalScroll, can_focus=True):
59
+ """Scrollable grid of image thumbnails with keyboard navigation."""
60
+
61
+ BINDINGS = [
62
+ Binding("up", "move_up", "Up", show=False),
63
+ Binding("down", "move_down", "Down", show=False),
64
+ Binding("left", "move_left", "Left", show=False),
65
+ Binding("right", "move_right", "Right", show=False),
66
+ Binding("k", "move_up", "Up", show=False),
67
+ Binding("j", "move_down", "Down", show=False),
68
+ Binding("h", "move_left", "Left", show=False),
69
+ Binding("l", "move_right", "Right", show=False),
70
+ Binding("enter", "select", "Select", show=False),
71
+ Binding("home", "first", "First", show=False),
72
+ Binding("end", "last", "Last", show=False),
73
+ Binding("g", "first", "First", show=False),
74
+ Binding("G", "last", "Last", show=False, key_display="shift+g"),
75
+ ]
76
+
77
+ class ImageSelected(Message):
78
+ """Emitted when an image is selected (Enter pressed)."""
79
+
80
+ def __init__(self, image_path: Path, index: int) -> None:
81
+ self.image_path = image_path
82
+ self.index = index
83
+ super().__init__()
84
+
85
+ class SelectionChanged(Message):
86
+ """Emitted when the selection changes."""
87
+
88
+ def __init__(self, image_path: Path, index: int) -> None:
89
+ self.image_path = image_path
90
+ self.index = index
91
+ super().__init__()
92
+
93
+ DEFAULT_CSS = """
94
+ ThumbnailGrid {
95
+ width: 100%;
96
+ height: 100%;
97
+ background: $background;
98
+ }
99
+
100
+ ThumbnailGrid:focus {
101
+ border: solid $accent;
102
+ }
103
+
104
+ ThumbnailGrid #grid-container {
105
+ layout: grid;
106
+ grid-gutter: 0;
107
+ width: 100%;
108
+ }
109
+
110
+ ThumbnailGrid #grid-container Image {
111
+ width: 32;
112
+ height: 14;
113
+ margin: 0;
114
+ padding: 0;
115
+ border: solid $background;
116
+ }
117
+
118
+ ThumbnailGrid #grid-container Image.selected {
119
+ border: solid $primary;
120
+ }
121
+
122
+ ThumbnailGrid .empty-message {
123
+ color: $text-disabled;
124
+ text-style: italic;
125
+ text-align: center;
126
+ width: 100%;
127
+ padding: 4;
128
+ }
129
+ """
130
+
131
+ CELL_WIDTH = 32
132
+ CELL_HEIGHT = 14
133
+
134
+ def __init__(self, images: list[Path] | None = None, **kwargs) -> None:
135
+ super().__init__(**kwargs)
136
+ self._images: list[Path] = images or []
137
+ self._selected_index: int = 0
138
+ self._columns: int = 1
139
+ self._cells: list[CellWidget] = []
140
+ self._grid_container: Container | None = None
141
+
142
+ def compose(self) -> ComposeResult:
143
+ self._grid_container = Container(id="grid-container")
144
+ yield self._grid_container
145
+
146
+ def on_mount(self) -> None:
147
+ """Initialize the grid on mount."""
148
+ # Defer initial build to after layout
149
+ self.call_after_refresh(self._initial_build)
150
+
151
+ def _initial_build(self) -> None:
152
+ """Build grid after initial layout."""
153
+ self._calculate_columns()
154
+ self._rebuild_grid()
155
+
156
+ def on_resize(self) -> None:
157
+ """Recalculate columns when resized."""
158
+ old_columns = self._columns
159
+ self._calculate_columns()
160
+ if old_columns != self._columns:
161
+ self._rebuild_grid()
162
+
163
+ def _calculate_columns(self) -> None:
164
+ """Calculate number of columns based on container width."""
165
+ available_width = max(self.size.width - 2, self.CELL_WIDTH)
166
+ self._columns = max(1, available_width // self.CELL_WIDTH)
167
+
168
+ def _rebuild_grid(self) -> None:
169
+ """Rebuild the grid with current images and column count."""
170
+ if self._grid_container is None:
171
+ return
172
+
173
+ container = self._grid_container
174
+
175
+ # Clear all children from container
176
+ container.remove_children()
177
+ self._cells.clear()
178
+
179
+ if not self._images:
180
+ container.mount(Static("No images to display", classes="empty-message"))
181
+ return
182
+
183
+ # Update grid columns CSS
184
+ container.styles.grid_size_columns = self._columns
185
+
186
+ # Calculate and set container height based on rows
187
+ # (textual-image needs explicit height, not height: auto, to render in scroll containers)
188
+ num_rows = (len(self._images) + self._columns - 1) // self._columns
189
+ container_height = num_rows * self.CELL_HEIGHT
190
+ container.styles.height = container_height
191
+
192
+ # Create cells
193
+ for idx, image_path in enumerate(self._images):
194
+ cell = create_thumbnail_cell(image_path, idx)
195
+ self._cells.append(cell)
196
+ container.mount(cell)
197
+
198
+ # Update selection
199
+ self._update_selection()
200
+
201
+ def set_images(self, images: list[Path]) -> None:
202
+ """Update the images displayed in the grid."""
203
+ self._images = images
204
+ self._selected_index = 0 if images else -1
205
+ self._calculate_columns()
206
+ self._rebuild_grid()
207
+
208
+ def _update_selection(self) -> None:
209
+ """Update the visual selection state."""
210
+ for idx, cell in enumerate(self._cells):
211
+ if idx == self._selected_index:
212
+ cell.add_class("selected")
213
+ else:
214
+ cell.remove_class("selected")
215
+
216
+ # Scroll selected cell into view
217
+ if 0 <= self._selected_index < len(self._cells):
218
+ self._cells[self._selected_index].scroll_visible()
219
+
220
+ def _move_selection(self, delta: int) -> None:
221
+ """Move selection by delta amount."""
222
+ if not self._images:
223
+ return
224
+
225
+ new_index = self._selected_index + delta
226
+ new_index = max(0, min(len(self._images) - 1, new_index))
227
+
228
+ if new_index != self._selected_index:
229
+ self._selected_index = new_index
230
+ self._update_selection()
231
+ self.post_message(
232
+ self.SelectionChanged(self._images[self._selected_index], self._selected_index)
233
+ )
234
+
235
+ def action_move_up(self) -> None:
236
+ """Move selection up by one row."""
237
+ self._move_selection(-self._columns)
238
+
239
+ def action_move_down(self) -> None:
240
+ """Move selection down by one row."""
241
+ self._move_selection(self._columns)
242
+
243
+ def action_move_left(self) -> None:
244
+ """Move selection left by one cell."""
245
+ self._move_selection(-1)
246
+
247
+ def action_move_right(self) -> None:
248
+ """Move selection right by one cell."""
249
+ self._move_selection(1)
250
+
251
+ def action_first(self) -> None:
252
+ """Move selection to first image."""
253
+ if self._images:
254
+ self._selected_index = 0
255
+ self._update_selection()
256
+ self.post_message(
257
+ self.SelectionChanged(self._images[self._selected_index], self._selected_index)
258
+ )
259
+
260
+ def action_last(self) -> None:
261
+ """Move selection to last image."""
262
+ if self._images:
263
+ self._selected_index = len(self._images) - 1
264
+ self._update_selection()
265
+ self.post_message(
266
+ self.SelectionChanged(self._images[self._selected_index], self._selected_index)
267
+ )
268
+
269
+ def action_select(self) -> None:
270
+ """Select the current image (open in detail view)."""
271
+ if 0 <= self._selected_index < len(self._images):
272
+ self.post_message(
273
+ self.ImageSelected(self._images[self._selected_index], self._selected_index)
274
+ )
275
+
276
+ @property
277
+ def selected_image(self) -> Path | None:
278
+ """Get the currently selected image path."""
279
+ if 0 <= self._selected_index < len(self._images):
280
+ return self._images[self._selected_index]
281
+ return None
282
+
283
+ @property
284
+ def selected_index(self) -> int:
285
+ """Get the currently selected index."""
286
+ return self._selected_index
287
+
288
+ @property
289
+ def image_count(self) -> int:
290
+ """Get the number of images in the grid."""
291
+ return len(self._images)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schenesort
3
- Version: 2.2.0
3
+ Version: 2.4.0
4
4
  Summary: Wallpaper collection management CLI tool
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.13
@@ -12,12 +12,14 @@ Requires-Dist: textual>=0.95.0
12
12
  Requires-Dist: typer>=0.21.1
13
13
  Description-Content-Type: text/markdown
14
14
 
15
- # Schenesort v2.2.0
15
+ # Schenesort v2.4.0
16
16
 
17
- A CLI tool for managing wallpaper collections with model generated metadata, terminal UI browsing, and SQLite-based
18
- querying.
17
+ ![Collage example](docs/collage.png)
18
+
19
+ A cli tool for managing wallpaper collections with model generated metadata, sweet tui, and sql metadata querying.
20
+
21
+ schenesort takes a directory of random wallpapers, with random filenames, and uses olama with a decent vision model to:
19
22
 
20
- schenesort takes a directory of random wallpapers, with random filenames, and uses olama with a decent vision model to
21
23
  - look at each wallpaper
22
24
  - rename the wallpaper to something sensible
23
25
  - drop a XMP sidecar with metadata about the file
@@ -26,7 +28,7 @@ Once you have a collection of wallpapers re-named with metadata sidecars, run th
26
28
  that can be queried to retrieve suggestions based on tags, colours, names and the like. Then use `get` to get a
27
29
  wallpaper path `feh $(schenesort get -1 -p)` or `hyprctl hyprpaper wallpaper "eDP-1,$(schenesort get -1 -p)"`
28
30
 
29
- schenesort also provides a bunch of utility commands to satisfy a gooner collection.
31
+ ![Browse example - Autumn](docs/browse-destruction.png)
30
32
 
31
33
  ## Installation
32
34
 
@@ -44,16 +46,25 @@ uv sync
44
46
 
45
47
  ## Quick Start
46
48
 
49
+ To generate metadata you need a olama server serving llava nearby, it takes a minute to set one up see [ollama
50
+ setup](./ollama-setup.md).
51
+
47
52
  ```bash
48
53
  # Generate metadata for images
49
54
  schenesort metadata generate ~/wallpapers -r
50
55
 
51
- # Browse with TUI
52
- schenesort browse ~/wallpapers
53
-
54
56
  # Index the collection
55
57
  schenesort index ~/wallpapers
56
58
 
59
+ # Generate thumbnails for gallery
60
+ schenesort thumbnail ~/wallpapers -r
61
+
62
+ # Browse with gallery (thumbnail grid with filters)
63
+ schenesort gallery
64
+
65
+ # Browse single images with TUI
66
+ schenesort browse ~/wallpapers
67
+
57
68
  # Query wallpapers
58
69
  schenesort get --mood peaceful --screen 4K
59
70
  schenesort get -1 -p | xargs feh # random wallpaper
@@ -64,6 +75,8 @@ schenesort get -1 -p | xargs feh # random wallpaper
64
75
  | Command | Description |
65
76
  |------------------------------|-----------------------------------------------------|
66
77
  | `browse` | Terminal UI browser with image preview and metadata |
78
+ | `gallery` | Thumbnail grid browser with filters |
79
+ | `thumbnail` | Generate thumbnail cache for gallery |
67
80
  | `index` | Build SQLite index for fast querying |
68
81
  | `get` | Query wallpapers by metadata attributes |
69
82
  | `stats` | Show collection statistics from index |
@@ -74,6 +87,7 @@ schenesort get -1 -p | xargs feh # random wallpaper
74
87
  | `info` | Show collection file statistics |
75
88
  | `describe` | AI-rename images based on content (Ollama) |
76
89
  | `models` | List available Ollama models |
90
+ | `collage` | Create a collage grid from matching wallpapers |
77
91
  | `metadata show` | Display XMP sidecar metadata |
78
92
  | `metadata set` | Manually set metadata fields |
79
93
  | `metadata generate` | Generate metadata with AI (Ollama) |
@@ -91,13 +105,12 @@ schenesort browse # uses paths.wallpaper from config
91
105
  schenesort get --mood peaceful -b # browse query results
92
106
  ```
93
107
 
94
- ![Browse example - Greek](docs/browse-greek.png)
95
-
96
108
  ![Browse example - Autumn](docs/browse-autumn.png)
97
109
 
98
110
  ![Browse example - Stallman](docs/browse-stallman.png)
99
111
 
100
112
  **Keyboard shortcuts:**
113
+
101
114
  | Key | Action |
102
115
  |--------------|----------------|
103
116
  | `j` / `Down` | Next image |
@@ -109,6 +122,63 @@ schenesort get --mood peaceful -b # browse query results
109
122
 
110
123
  The TUI uses textual-image for rendering, which auto-detects terminal graphics support (Sixel, iTerm2, Kitty).
111
124
 
125
+ ## Gallery Browser
126
+
127
+ Browse your indexed collection with a thumbnail grid and filter panel:
128
+
129
+ ```bash
130
+ # Open gallery browser
131
+ schenesort gallery
132
+
133
+ # Pre-filter results
134
+ schenesort gallery --mood peaceful
135
+ schenesort gallery --tag nature --style photography
136
+ ```
137
+
138
+ The gallery requires an indexed collection (`schenesort index`) and cached thumbnails for fast loading.
139
+
140
+ ### Generate Thumbnails
141
+
142
+ ```bash
143
+ # Generate thumbnails for a directory
144
+ schenesort thumbnail ~/wallpapers
145
+
146
+ # Recursive with progress bar
147
+ schenesort thumbnail ~/wallpapers -r
148
+
149
+ # Force regenerate all thumbnails
150
+ schenesort thumbnail ~/wallpapers --force
151
+
152
+ # Clear thumbnail cache
153
+ schenesort thumbnail ~/wallpapers --clear
154
+ ```
155
+
156
+ Thumbnails are cached at `~/.cache/schenesort/thumbnails/` (320x200 JPEG).
157
+
158
+ **Gallery keyboard shortcuts:**
159
+
160
+ | Key | Action |
161
+ |------------------|-----------------------|
162
+ | `j` / `Down` | Move down |
163
+ | `k` / `Up` | Move up |
164
+ | `h` / `Left` | Move left |
165
+ | `l` / `Right` | Move right |
166
+ | `g` / `Home` | First image |
167
+ | `G` / `End` | Last image |
168
+ | `Enter` | Open detail view |
169
+ | `Tab` | Switch panel |
170
+ | `Escape` | Clear filters |
171
+ | `r` | Refresh |
172
+ | `q` / `Ctrl+C` | Quit |
173
+
174
+ **Detail view shortcuts:**
175
+
176
+ | Key | Action |
177
+ |------------------|-----------------------|
178
+ | `j` / `Down` | Next image |
179
+ | `k` / `Up` | Previous image |
180
+ | `Escape` / `q` | Back to grid |
181
+
112
182
  ## Collection Indexing and Querying
113
183
 
114
184
  Build a SQLite index for fast querying across your entire collection:
@@ -147,6 +217,38 @@ schenesort stats
147
217
 
148
218
  The database is stored at `$XDG_DATA_HOME/schenesort/index.db` (default: `~/.local/share/schenesort/index.db`).
149
219
 
220
+ ## Collage Generation
221
+
222
+ Create a collage grid (up to 4x4) from wallpapers matching query criteria:
223
+
224
+ ```bash
225
+ # Create a 2x2 collage (default)
226
+ schenesort collage output.png --mood peaceful
227
+
228
+ # Create a 4x4 collage of landscape images
229
+ schenesort collage wall.png --subject landscape --cols 4 --rows 4
230
+
231
+ # Custom tile size (default: 480x270)
232
+ schenesort collage collage.png --tile-width 640 --tile-height 360
233
+
234
+ # Combine filters
235
+ schenesort collage night_cities.png --time night --subject urban --cols 3 --rows 2
236
+ ```
237
+
238
+ `schenesort collage landscape.png --cols 4 --rows 4 --tile-width 640 --tile-height 360 --tag landscape`
239
+
240
+ ![Collage example](docs/landscape.png)
241
+
242
+ | Option | Description | Default |
243
+ |-----------------|--------------------------------------|---------|
244
+ | `--cols` | Number of columns (1-4) | 2 |
245
+ | `--rows` | Number of rows (1-4) | 2 |
246
+ | `--tile-width` | Width of each tile in pixels | 480 |
247
+ | `--tile-height` | Height of each tile in pixels | 270 |
248
+ | `--random` | Select images randomly | True |
249
+
250
+ All `get` query filters are supported: `--tag`, `--mood`, `--color`, `--style`, `--subject`, `--time`, `--screen`, `--min-width`, `--min-height`, `--search`.
251
+
150
252
  ## Metadata Management
151
253
 
152
254
  Store metadata in XMP sidecar files (`.xmp`) alongside images without modifying the original files.
@@ -168,7 +270,7 @@ Store metadata in XMP sidecar files (`.xmp`) alongside images without modifying
168
270
  | `source` | Source URL or info |
169
271
  | `ai_model` | Model used for metadata generation |
170
272
 
171
- ### Generate Metadata with AI
273
+ ### Generate Metadata using ollama
172
274
 
173
275
  ```bash
174
276
  # Preview what would be generated
@@ -245,6 +347,7 @@ schenesort cleanup ~/wallpapers -r
245
347
  ## Configuration
246
348
 
247
349
  Schenesort follows XDG Base Directory spec:
350
+
248
351
  - Config: `$XDG_CONFIG_HOME/schenesort/config.toml` (default: `~/.config/schenesort/config.toml`)
249
352
  - Data: `$XDG_DATA_HOME/schenesort/index.db` (default: `~/.local/share/schenesort/index.db`)
250
353
 
@@ -322,7 +425,7 @@ See [schenesort.yazi/README.md](schenesort.yazi/README.md) for details.
322
425
 
323
426
  ## XMP Sidecar Format
324
427
 
325
- ```
428
+ ```text
326
429
  ~/wallpapers/
327
430
  ├── mountain_sunset.jpg
328
431
  ├── mountain_sunset.jpg.xmp ← metadata stored here
@@ -0,0 +1,19 @@
1
+ schenesort/__init__.py,sha256=Z7bmXIWiFUCKj1j4JRlPZvmfzc4RC10Lh1WCKNsJn20,61
2
+ schenesort/cli.py,sha256=TFq_AjH-PSJYAr7b14rXdXl7xEKovqNnt7SQAZ7psZE,55702
3
+ schenesort/config.py,sha256=8EuYv2nma3QKnLPSAgAsaOHZlORzinHjy-Bj_LKPfJA,2739
4
+ schenesort/db.py,sha256=RbfZN6d5O5MkTRPbu51fA7tQLOJf1huIjxFdoT4sESk,11398
5
+ schenesort/thumbnails.py,sha256=-B4vlh2w_NdUkO0tshcpZPUSs6M8S6G_abiBPp3xVOY,3631
6
+ schenesort/xmp.py,sha256=1VS_I4akY8Dv_KLPOdzPgBCFy0280oSCsMmo-_A9cNE,9749
7
+ schenesort/tui/__init__.py,sha256=BsOc1PedYBguvYBpnny_T9ST-dIa_52xYWlLdNemha4,449
8
+ schenesort/tui/app.py,sha256=HGKBnszTJXtLu9Mo7eJY4wiO2qkB7dFftBoM4GC_js4,6202
9
+ schenesort/tui/grid_app.py,sha256=hrS-dEwQ9wFmcQz2ZT7jy6lYOdSCidEY5Zlfmpv8sI8,10032
10
+ schenesort/tui/widgets/__init__.py,sha256=3cm7vfXG5-xC_UhIbgEtuMxq5I5tXg6okokJ4ecjGIE,202
11
+ schenesort/tui/widgets/filter_panel.py,sha256=BZf8T1H4u4eTUj2bt6ILvQ3m_hwx20DRfc02YVJ4zQk,7273
12
+ schenesort/tui/widgets/image_preview.py,sha256=j_KTQBJk0vbO18FzTo-GYkRlgerBe1K3VGQwMPbEuUw,2846
13
+ schenesort/tui/widgets/metadata_panel.py,sha256=8DnsvDdKf3jzqHOo7dDmxJxL0Mjx5mX7HNwwcMUcJn0,4860
14
+ schenesort/tui/widgets/thumbnail_grid.py,sha256=-81CL4pBI7Q7J_PD2MbeaI-SNckEdfuNZdA14m7q348,9407
15
+ schenesort-2.4.0.dist-info/METADATA,sha256=rIqVFt9JgHlVGSbsG1irhOllNWNzD0zEGMUjduxXhWc,14698
16
+ schenesort-2.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
17
+ schenesort-2.4.0.dist-info/entry_points.txt,sha256=J5lS-N6KgmzjutFi5bG1jv-4Wszbz3MfcOHcbznBVcw,50
18
+ schenesort-2.4.0.dist-info/licenses/LICENSE,sha256=sMw3SMb9ec9dbM2twEMVeunsGwuljza-9kEXg4kSJpg,1070
19
+ schenesort-2.4.0.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- schenesort/__init__.py,sha256=Z7bmXIWiFUCKj1j4JRlPZvmfzc4RC10Lh1WCKNsJn20,61
2
- schenesort/cli.py,sha256=lGbQ5nJ5TjusG7XlZfOVmdaCeef9eRO_a9JL1I-V_Fc,46291
3
- schenesort/config.py,sha256=8EuYv2nma3QKnLPSAgAsaOHZlORzinHjy-Bj_LKPfJA,2739
4
- schenesort/db.py,sha256=RbfZN6d5O5MkTRPbu51fA7tQLOJf1huIjxFdoT4sESk,11398
5
- schenesort/xmp.py,sha256=1VS_I4akY8Dv_KLPOdzPgBCFy0280oSCsMmo-_A9cNE,9749
6
- schenesort/tui/__init__.py,sha256=bqSyRlefPfsYhUxsub4Rltz7yjCQVPXvhzj9n2bn370,141
7
- schenesort/tui/app.py,sha256=HGKBnszTJXtLu9Mo7eJY4wiO2qkB7dFftBoM4GC_js4,6202
8
- schenesort/tui/widgets/__init__.py,sha256=3cm7vfXG5-xC_UhIbgEtuMxq5I5tXg6okokJ4ecjGIE,202
9
- schenesort/tui/widgets/image_preview.py,sha256=j_KTQBJk0vbO18FzTo-GYkRlgerBe1K3VGQwMPbEuUw,2846
10
- schenesort/tui/widgets/metadata_panel.py,sha256=8DnsvDdKf3jzqHOo7dDmxJxL0Mjx5mX7HNwwcMUcJn0,4860
11
- schenesort-2.2.0.dist-info/METADATA,sha256=FOfxFWXroyg2RtZxMCrHL1Bf5qC5d8OPeMb_3cHmK7M,11251
12
- schenesort-2.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
13
- schenesort-2.2.0.dist-info/entry_points.txt,sha256=J5lS-N6KgmzjutFi5bG1jv-4Wszbz3MfcOHcbznBVcw,50
14
- schenesort-2.2.0.dist-info/licenses/LICENSE,sha256=sMw3SMb9ec9dbM2twEMVeunsGwuljza-9kEXg4kSJpg,1070
15
- schenesort-2.2.0.dist-info/RECORD,,