streamlit-image-board 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Streamlit Image Board Contributors
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,2 @@
1
+ recursive-include streamlit_image_board/frontend/build *
2
+ include streamlit_image_board/pyproject.toml
@@ -0,0 +1,362 @@
1
+ Metadata-Version: 2.4
2
+ Name: streamlit-image-board
3
+ Version: 0.1.0
4
+ Summary: Snappy React selectable image board for Streamlit (Components v2)
5
+ Author-email: George Siogkas <gsiogkas@cvrlab.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/gsiogkas/streamlit-image-board
8
+ Project-URL: Repository, https://github.com/gsiogkas/streamlit-image-board
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: streamlit>=1.51
13
+ Requires-Dist: pillow
14
+ Provides-Extra: aggrid
15
+ Requires-Dist: pandas; extra == "aggrid"
16
+ Requires-Dist: streamlit-aggrid>=0.3.4; extra == "aggrid"
17
+ Provides-Extra: aggrid-v2
18
+ Requires-Dist: pandas; extra == "aggrid-v2"
19
+ Requires-Dist: streamlit-aggrid-v2>=0.3.4; extra == "aggrid-v2"
20
+ Provides-Extra: devel
21
+ Requires-Dist: wheel; extra == "devel"
22
+ Requires-Dist: pytest; extra == "devel"
23
+ Requires-Dist: pandas; extra == "devel"
24
+ Dynamic: license-file
25
+
26
+ # streamlit-image-board
27
+
28
+ ![Usage demo — select, hover, select-all, styled cards](docs/assets/usage.gif)
29
+
30
+ Snappy React **selectable image board** for Streamlit, built on **Components v2**
31
+ (same stack as [`streamlit-drawable-konva`](https://github.com/gsiogkas/streamlit-drawable-konva)).
32
+
33
+ Named **image-board** (not gallery) to avoid clashing with existing
34
+ `image-gallery` packages. Checkboxes, image-frame selection, hover text, styled
35
+ cards, and toolbar actions update **inside the widget** — no full Streamlit
36
+ rerun for every toggle.
37
+
38
+ ## Features
39
+
40
+ - CSS grid of image cards (configurable columns / gap)
41
+ - Configurable **image click**: `select` | `open` | `none` (per item or board-wide)
42
+ - Optional **checkboxes** kept in sync with the same selection
43
+ - **Select all / Deselect all** + master **All** control
44
+ - **Hover text** overlay on thumbnails
45
+ - Stylable **label / caption / text chips / buttons**
46
+ - Per-card (and board-default) **background, text color, font**
47
+ - **JSON** items / full board config (file path, string, or dict)
48
+ - Toolbar **selection counter**: file count + total size
49
+ - Optional **paging** (`page_size`) — overflow goes to the next page; checklist spans all pages
50
+ - **Image-only cards** via `show_card_info=False` (hide labels / captions / chips / card buttons)
51
+ - Configurable **button aesthetics** (`button_style`, per-button `variant` / `size` / `style`)
52
+ - Shared checklist contract for **AgGrid** (classic + v2)
53
+ - Returns `selected_ids: list[str]`, `selected_csv`, and size stats
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ pip install streamlit-image-board
59
+ # or editable:
60
+ pip install -e .
61
+ ```
62
+
63
+ Frontend rebuild (developers):
64
+
65
+ ```bash
66
+ cd streamlit_image_board/frontend
67
+ npm install
68
+ npm run build
69
+ ```
70
+
71
+ Demo:
72
+
73
+ ```bash
74
+ streamlit run app.py
75
+ ```
76
+
77
+ Bundled demo photos live in [`examples/images/`](examples/images/) (public domain;
78
+ see [`ATTRIBUTION.md`](examples/images/ATTRIBUTION.md)). Refresh with
79
+ `uv run python scripts/fetch_demo_images.py`.
80
+
81
+ ## Quick start
82
+
83
+ ```python
84
+ from streamlit_image_board import st_image_board
85
+
86
+ result = st_image_board(
87
+ [
88
+ {
89
+ "id": "a",
90
+ "src": "https://…",
91
+ "label": "Alpha",
92
+ "hover_text": "Alpha details on hover",
93
+ "card_style": {
94
+ "background": "#0f172a",
95
+ "color": "#e2e8f0",
96
+ "fontFamily": "Georgia, serif",
97
+ },
98
+ "texts": [
99
+ {"text": "NEW", "variant": "badge"},
100
+ {"text": "12 MB", "variant": "muted"},
101
+ ],
102
+ },
103
+ {
104
+ "id": "b",
105
+ "image": pil_image,
106
+ "label": "Beta",
107
+ "caption": "from PIL",
108
+ "background": "#fff7ed",
109
+ "color": "#9a3412",
110
+ },
111
+ ],
112
+ columns=4,
113
+ selectable=True,
114
+ image_click="select", # or "open" / "none"
115
+ show_select_all=True,
116
+ selection_color="#0f766e",
117
+ card_style={"background": "#ffffff", "color": "#0f172a"}, # board default
118
+ buttons=[
119
+ {"id": "approve", "label": "Approve", "variant": "primary"},
120
+ {"id": "delete", "label": "Delete", "variant": "danger"},
121
+ ],
122
+ key="board",
123
+ )
124
+
125
+ ids = result.selected_ids # ["a", "b"]
126
+ csv = result.selected_csv # "a,b"
127
+ n = result.selected_count # 2
128
+ size = result.selected_size # e.g. "240.0 KB" when sizes are known
129
+ ```
130
+
131
+ ### Button aesthetics
132
+
133
+ Board-wide theme (toolbar, pager, card buttons):
134
+
135
+ ```python
136
+ result = st_image_board(
137
+ items,
138
+ button_style={
139
+ "background": "#ffffff",
140
+ "color": "#0f172a",
141
+ "borderRadius": "999px",
142
+ "padding": "6px 14px",
143
+ "fontSize": "13px",
144
+ "primaryBackground": "#0f766e",
145
+ "primaryColor": "#ffffff",
146
+ "dangerBackground": "#fee2e2",
147
+ "dangerColor": "#b91c1c",
148
+ },
149
+ buttons=[
150
+ {"id": "ok", "label": "Approve", "variant": "primary", "size": "lg"},
151
+ {"id": "no", "label": "Skip", "variant": "ghost", "size": "sm"},
152
+ {
153
+ "id": "del",
154
+ "label": "Delete",
155
+ "variant": "danger",
156
+ "style": {"fontWeight": "700"},
157
+ },
158
+ ],
159
+ )
160
+ ```
161
+
162
+ | Knob | Values |
163
+ | --- | --- |
164
+ | `variant` | `default` · `primary` · `danger` · `ghost` |
165
+ | `size` | `sm` · `md` (default) · `lg` |
166
+ | `style` | per-button sanitized CSS override |
167
+ | `button_style` | board theme → CSS variables for all `.sig-btn` |
168
+
169
+ ### Image-only cards
170
+
171
+ Hide everything under the thumbnail (label, caption, chips, per-card buttons):
172
+
173
+ ```python
174
+ result = st_image_board(items, show_card_info=False)
175
+ ```
176
+
177
+ Checkboxes, hover overlay, selection frame, and toolbar actions still work.
178
+
179
+ ### Paging
180
+
181
+ Set `page_size` to a positive integer to paginate instead of scrolling.
182
+ Selection, select-all, and the size counter stay **global** across pages.
183
+
184
+ ```python
185
+ result = st_image_board(
186
+ items,
187
+ columns=4,
188
+ page_size=8,
189
+ pager_position="top", # or "bottom"
190
+ )
191
+ # page_size=0 (default) → classic scroll via max_height
192
+ # Pager controls: First / Prev / Next / Last
193
+ ```
194
+
195
+ ### Selection size counter
196
+
197
+ Pass `size` / `size_bytes` on items (int bytes, or strings like `"1.5 MB"`).
198
+ Pillow images and `data:` URLs get a size inferred automatically.
199
+
200
+ The toolbar shows e.g. `3 selected / 8 · 1.2 MB` (toggle with
201
+ `show_selection_stats=False`). Python side:
202
+
203
+ | Field | Meaning |
204
+ | --- | --- |
205
+ | `selected_count` | Number of selected files |
206
+ | `selected_bytes` | Sum of known sizes (bytes) |
207
+ | `selected_size` | Human string (`""` if none known) |
208
+ | `result.stats_label(total_count=…)` | Same text as the toolbar |
209
+
210
+ ```python
211
+ result = st_image_board(
212
+ [
213
+ {"id": "a", "src": "https://…/a.jpg", "size_bytes": 120_000},
214
+ {"id": "b", "src": "https://…/b.jpg", "size": "2.1 MB"},
215
+ ],
216
+ )
217
+ st.write(result.selected_count, result.selected_size)
218
+ ```
219
+
220
+ ### Image click behavior
221
+
222
+ | `image_click` | Thumbnail click |
223
+ | --- | --- |
224
+ | `"select"` (default) | Toggle selection frame (same checklist as checkboxes) |
225
+ | `"open"` | Emit `clicked_button="open"` / `clicked_item`, and optionally `window.open` |
226
+ | `"none"` | No thumbnail action (checkboxes / buttons still work) |
227
+
228
+ Set board-wide via `image_click=…`, or per item with `"image_click": "open"`.
229
+ Open target: item `open_url` / `href` / `link`, else `src`. Use `open_in_new_tab=False` to skip the browser tab and only emit the Streamlit event.
230
+
231
+ ```python
232
+ result = st_image_board(
233
+ [
234
+ {
235
+ "id": "shot",
236
+ "src": "https://…/thumb.jpg",
237
+ "label": "Full size",
238
+ "open_url": "https://…/full.jpg",
239
+ "image_click": "open",
240
+ }
241
+ ],
242
+ image_click="open",
243
+ open_in_new_tab=True,
244
+ selectable=True, # checkboxes still select
245
+ show_checkboxes=True,
246
+ )
247
+ if result.clicked_button == "open":
248
+ st.write("Opened", result.clicked_item)
249
+ ```
250
+
251
+ ### JSON input
252
+
253
+ Pass a path, JSON string, or dict — items only, or a full board config:
254
+
255
+ ```python
256
+ from streamlit_image_board import load_board_config, st_image_board
257
+
258
+ # Items array file (see examples/items.json)
259
+ result = st_image_board("examples/items.json", columns=3, image_click="select")
260
+
261
+ # Full config: items + options (kwargs override file fields)
262
+ result = st_image_board(config="examples/board_config.json")
263
+
264
+ # Or load yourself
265
+ cfg = load_board_config("examples/board_config.json")
266
+ result = st_image_board(**cfg, key="from_json")
267
+ ```
268
+
269
+ Helpers: `load_board_items`, `load_board_config`.
270
+
271
+ ### Card chrome (font / background)
272
+
273
+ | Source | Keys |
274
+ | --- | --- |
275
+ | Board default | `card_style={...}` on `st_image_board` |
276
+ | Per item | `card_style` / `style`, or shortcuts `background`, `color`, `font_family` |
277
+ | Label / caption | `label_style`, `caption_style` |
278
+
279
+ Allowed style keys are sanitized (color, background, fontFamily, padding, …).
280
+
281
+ ### Selection UX
282
+
283
+ - Click the **image** → depends on `image_click` (`select` / `open` / `none`)
284
+ - Optional **checkbox** uses the same checklist (independent of open mode)
285
+ - **All** / **Select all** / **Deselect all** in the toolbar
286
+ - Hover shows `hover_text` (falls back to `label`)
287
+
288
+ ### `BoardResult`
289
+
290
+ | Field | Meaning |
291
+ | --- | --- |
292
+ | `selected_ids` | `list[str]` — checkbox **and** image clicks |
293
+ | `selected_csv` | Same ids as `"id1,id2"` |
294
+ | `selected_count` | Number of selected files |
295
+ | `selected_bytes` | Sum of known `size_bytes` |
296
+ | `selected_size` | Human-readable total (or `""`) |
297
+ | `clicked_button` | Last toolbar/card button id |
298
+ | `clicked_item` | Item id for card button / activation |
299
+ | `action_nonce` | Increments on each action so repeats are detectable |
300
+
301
+ Helpers: `result.as_list()`, `result.as_set()`, `result.stats_label()`,
302
+ `result.to_aggrid_preselected(df)`, `format_bytes`.
303
+
304
+ `st_image_gallery` / `GalleryResult` remain as thin aliases.
305
+
306
+ ## AgGrid checklist bridge (classic + v2)
307
+
308
+ Shared contract: **`list[str]` of stable ids** (plus optional CSV). Both the
309
+ image board and AgGrid read/write that checklist.
310
+
311
+ ```python
312
+ from st_aggrid import AgGrid, GridOptionsBuilder, GridUpdateMode
313
+ from streamlit_image_board import (
314
+ configure_aggrid_selection,
315
+ st_image_board,
316
+ sync_checklist,
317
+ )
318
+
319
+ board = st_image_board(items, selected_ids=st.session_state.get("shared_selection", []))
320
+
321
+ gb = GridOptionsBuilder.from_dataframe(df) # df must include an "id" column
322
+ configure_aggrid_selection(gb, st.session_state.get("shared_selection", []), data=df)
323
+ grid = AgGrid(df, gridOptions=gb.build(), update_mode=GridUpdateMode.SELECTION_CHANGED)
324
+
325
+ sel = sync_checklist(
326
+ session_state=st.session_state,
327
+ key="shared_selection",
328
+ board=board,
329
+ aggrid=grid,
330
+ id_column="id",
331
+ )
332
+ ```
333
+
334
+ | Helper | Role |
335
+ | --- | --- |
336
+ | `normalize_selected_ids` | Coerce any payload → `list[str]` |
337
+ | `selected_ids_from_aggrid` | Read AgGrid `selected_rows` |
338
+ | `aggrid_preselected_rows` | Map ids → `pre_selected_rows` indices |
339
+ | `configure_aggrid_selection` | Wire `GridOptionsBuilder.configure_selection` |
340
+ | `sync_checklist` | Bidirectional session sync board ↔ AgGrid |
341
+ | `filter_rows_by_ids` | Slice a DataFrame / row list by the checklist |
342
+
343
+ Optional extras: `pip install 'streamlit-image-board[aggrid]'` or `[aggrid-v2]`.
344
+
345
+ ## Publishing
346
+
347
+ Step-by-step GitHub / PyPI / Streamlit Cloud / gallery:
348
+ **[PUBLISHING.md](PUBLISHING.md)**.
349
+
350
+ Refresh the usage GIF:
351
+
352
+ ```bash
353
+ uv run python scripts/make_usage_gif.py
354
+ # or record from the live demo — see PUBLISHING.md
355
+ ```
356
+
357
+ ## Requirements
358
+
359
+ - Python ≥ 3.10
360
+ - Streamlit ≥ 1.51 (Components v2)
361
+ - Pillow
362
+ - Node ≥ 18 (frontend build only)