streamlit-drawable-konva 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 Drawable Konva 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_drawable_konva/frontend/build *
2
+ include streamlit_drawable_konva/pyproject.toml
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: streamlit-drawable-konva
3
+ Version: 0.1.0
4
+ Summary: Streamlit drawable canvas component using Konva.js (Components v2)
5
+ Author-email: George Siogkas <gsiogkas@cvrlab.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/gsiogkas/streamlit-drawable-konva
8
+ Project-URL: Documentation, https://github.com/gsiogkas/streamlit-drawable-konva#readme
9
+ Project-URL: Repository, https://github.com/gsiogkas/streamlit-drawable-konva
10
+ Project-URL: Issues, https://github.com/gsiogkas/streamlit-drawable-konva/issues
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: streamlit>=1.51
15
+ Requires-Dist: numpy
16
+ Requires-Dist: pillow
17
+ Provides-Extra: demo
18
+ Requires-Dist: pandas; extra == "demo"
19
+ Provides-Extra: devel
20
+ Requires-Dist: wheel; extra == "devel"
21
+ Requires-Dist: pandas; extra == "devel"
22
+ Dynamic: license-file
23
+
24
+ # Streamlit Drawable Konva
25
+
26
+ Streamlit custom component (Components **v2**) that provides a sketching canvas
27
+ using [Konva.js](https://konvajs.org/) / `react-konva`.
28
+
29
+ The public Python API is intentionally compatible with
30
+ [`streamlit-drawable-canvas-fix`](https://pypi.org/project/streamlit-drawable-canvas-fix/)
31
+ (`st_canvas` → `CanvasResult(image_data, json_data)`). Object JSON uses
32
+ Konva-oriented fields (`x`/`y`/`points`/…), not Fabric.js schemas.
33
+
34
+ ## Features
35
+
36
+ - Freehand, line, rect, circle, point, polygon drawing
37
+ - Transform mode (move / scale / rotate); double-click to delete
38
+ - Background color or image
39
+ - Realtime or on-demand updates to Streamlit
40
+ - Undo / redo / clear toolbar
41
+ - Returns RGBA `image_data` and scene `json_data`
42
+
43
+ ## Why Konva.js (vs Fabric.js)?
44
+
45
+ Konva is not universally “better” than Fabric — both are mature 2D canvas
46
+ libraries. For **this** Streamlit component, Konva is the better fit.
47
+
48
+ | Concern | Konva | Fabric |
49
+ | --- | --- | --- |
50
+ | React integration | First-class via `react-konva` (declarative scene tree) | Imperative `fabric.Canvas`; React wrappers are thinner / more brittle |
51
+ | Mental model | Stage → Layer → Shape (clear export boundaries) | Single canvas object model; background vs drawing export needs extra care |
52
+ | Bundle / focus | Leaner, scene-graph oriented | Heavier; strong textile/image-editing heritage |
53
+ | Transforms | Built-in `Transformer` for select / scale / rotate | Excellent selection UX; historically the gold standard for editors |
54
+ | Ecosystem | Strong for interactive UIs and annotation tools | Huge installed base; many Fabric JSON examples in the wild |
55
+
56
+ **Why we chose Konva here**
57
+
58
+ 1. **React + Streamlit Components v2** — the official CCv2 React template maps
59
+ cleanly onto `react-konva`. Drawing state stays in React; updates go out via
60
+ `setStateValue` without fighting an imperative canvas singleton.
61
+ 2. **Layered export** — background images can live on a non-exported layer while
62
+ strokes/shapes export cleanly to `image_data`, matching the upstream
63
+ “background is not round-tripped” behavior with less glue code.
64
+ 3. **Maintainability** — freerdraw / shapes / polygon / transform are ordinary
65
+ React event handlers and Konva nodes, which is easier to evolve next to a
66
+ modern Vite + TypeScript frontend than a Fabric-centric CRA-era stack.
67
+ 4. **Performance for annotation UIs** — Konva’s layer/stage model scales well for
68
+ ROI / sketching workloads typical in Streamlit apps.
69
+
70
+ **When Fabric may still win**
71
+
72
+ - You need **byte-compatible Fabric JSON** (existing pipelines, demos, or
73
+ tutorials that assume `left`/`top`/`path` Fabric schemas).
74
+ - You want Fabric’s mature free-transform / skew / complex text editing out of
75
+ the box without reimplementing edge cases.
76
+
77
+ This package keeps a **compatible Python API** (`st_canvas` → `CanvasResult`) so
78
+ apps can migrate from `streamlit-drawable-canvas-fix`, while accepting that
79
+ `json_data` is Konva-oriented rather than a Fabric dump.
80
+
81
+ ## Install (uv)
82
+
83
+ ```bash
84
+ uv sync
85
+ ```
86
+
87
+ Build the frontend once (required before first Streamlit run):
88
+
89
+ ```bash
90
+ cd streamlit_drawable_konva/frontend
91
+ npm install
92
+ npm run build
93
+ cd ../../
94
+ ```
95
+
96
+ Editable install is handled by `uv sync` via the local `pyproject.toml`.
97
+
98
+ ## Demo
99
+
100
+ ```bash
101
+ uv run streamlit run app.py
102
+ ```
103
+
104
+ ## Development
105
+
106
+ CCv2 watches a production-style build into `frontend/build` (no separate webpack
107
+ dev server URL). Use two terminals:
108
+
109
+ **Terminal A — frontend watch build**
110
+
111
+ ```bash
112
+ cd streamlit_drawable_konva/frontend
113
+ npm install
114
+ npm run dev
115
+ ```
116
+
117
+ **Terminal B — Streamlit**
118
+
119
+ ```bash
120
+ uv sync
121
+ uv run streamlit run app.py
122
+ ```
123
+
124
+ Refresh the browser after frontend rebuilds.
125
+
126
+ ## API
127
+
128
+ ```python
129
+ from streamlit_drawable_konva import st_canvas
130
+
131
+ result = st_canvas(
132
+ fill_color="rgba(255, 165, 0, 0.3)",
133
+ stroke_width=3,
134
+ stroke_color="#000000",
135
+ background_color="#eee",
136
+ background_image=None,
137
+ update_streamlit=True,
138
+ height=400,
139
+ width=600,
140
+ drawing_mode="freedraw", # freedraw|line|rect|circle|point|polygon|transform
141
+ initial_drawing=None,
142
+ display_toolbar=True,
143
+ point_display_radius=3,
144
+ key="canvas",
145
+ )
146
+
147
+ # result.image_data -> np.ndarray | None
148
+ # result.json_data -> dict | None
149
+ ```
150
+
151
+ ### Polygon controls
152
+
153
+ - Left-click: add point
154
+ - Right-click: close polygon
155
+ - Double-click: remove latest point
156
+
157
+ ### Transform controls
158
+
159
+ - Click object to select
160
+ - Drag / resize / rotate with the transformer
161
+ - Double-click selected object to delete
162
+
163
+ ## Packaging notes
164
+
165
+ - Registered as `streamlit-drawable-konva.st_canvas` via the in-package
166
+ `streamlit_drawable_konva/pyproject.toml` manifest (`asset_dir = frontend/build`).
167
+ - Requires Streamlit `>= 1.51` (Components v2).
168
+ - Frontend toolchain targets Node `>= 18` (Vite 5).
169
+
170
+ ## Publishing (GitHub / PyPI / Streamlit Cloud / gallery)
171
+
172
+ Step-by-step instructions: [`PUBLISHING.md`](PUBLISHING.md).
173
+
174
+ Order: push to GitHub → publish to PyPI → deploy `app.py` on Community Cloud →
175
+ submit [`gallery/streamlit-drawable-konva.json`](gallery/streamlit-drawable-konva.json)
176
+ as a PR to [streamlit/gallery](https://github.com/streamlit/gallery/tree/main/components/registry).
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,157 @@
1
+ # Streamlit Drawable Konva
2
+
3
+ Streamlit custom component (Components **v2**) that provides a sketching canvas
4
+ using [Konva.js](https://konvajs.org/) / `react-konva`.
5
+
6
+ The public Python API is intentionally compatible with
7
+ [`streamlit-drawable-canvas-fix`](https://pypi.org/project/streamlit-drawable-canvas-fix/)
8
+ (`st_canvas` → `CanvasResult(image_data, json_data)`). Object JSON uses
9
+ Konva-oriented fields (`x`/`y`/`points`/…), not Fabric.js schemas.
10
+
11
+ ## Features
12
+
13
+ - Freehand, line, rect, circle, point, polygon drawing
14
+ - Transform mode (move / scale / rotate); double-click to delete
15
+ - Background color or image
16
+ - Realtime or on-demand updates to Streamlit
17
+ - Undo / redo / clear toolbar
18
+ - Returns RGBA `image_data` and scene `json_data`
19
+
20
+ ## Why Konva.js (vs Fabric.js)?
21
+
22
+ Konva is not universally “better” than Fabric — both are mature 2D canvas
23
+ libraries. For **this** Streamlit component, Konva is the better fit.
24
+
25
+ | Concern | Konva | Fabric |
26
+ | --- | --- | --- |
27
+ | React integration | First-class via `react-konva` (declarative scene tree) | Imperative `fabric.Canvas`; React wrappers are thinner / more brittle |
28
+ | Mental model | Stage → Layer → Shape (clear export boundaries) | Single canvas object model; background vs drawing export needs extra care |
29
+ | Bundle / focus | Leaner, scene-graph oriented | Heavier; strong textile/image-editing heritage |
30
+ | Transforms | Built-in `Transformer` for select / scale / rotate | Excellent selection UX; historically the gold standard for editors |
31
+ | Ecosystem | Strong for interactive UIs and annotation tools | Huge installed base; many Fabric JSON examples in the wild |
32
+
33
+ **Why we chose Konva here**
34
+
35
+ 1. **React + Streamlit Components v2** — the official CCv2 React template maps
36
+ cleanly onto `react-konva`. Drawing state stays in React; updates go out via
37
+ `setStateValue` without fighting an imperative canvas singleton.
38
+ 2. **Layered export** — background images can live on a non-exported layer while
39
+ strokes/shapes export cleanly to `image_data`, matching the upstream
40
+ “background is not round-tripped” behavior with less glue code.
41
+ 3. **Maintainability** — freerdraw / shapes / polygon / transform are ordinary
42
+ React event handlers and Konva nodes, which is easier to evolve next to a
43
+ modern Vite + TypeScript frontend than a Fabric-centric CRA-era stack.
44
+ 4. **Performance for annotation UIs** — Konva’s layer/stage model scales well for
45
+ ROI / sketching workloads typical in Streamlit apps.
46
+
47
+ **When Fabric may still win**
48
+
49
+ - You need **byte-compatible Fabric JSON** (existing pipelines, demos, or
50
+ tutorials that assume `left`/`top`/`path` Fabric schemas).
51
+ - You want Fabric’s mature free-transform / skew / complex text editing out of
52
+ the box without reimplementing edge cases.
53
+
54
+ This package keeps a **compatible Python API** (`st_canvas` → `CanvasResult`) so
55
+ apps can migrate from `streamlit-drawable-canvas-fix`, while accepting that
56
+ `json_data` is Konva-oriented rather than a Fabric dump.
57
+
58
+ ## Install (uv)
59
+
60
+ ```bash
61
+ uv sync
62
+ ```
63
+
64
+ Build the frontend once (required before first Streamlit run):
65
+
66
+ ```bash
67
+ cd streamlit_drawable_konva/frontend
68
+ npm install
69
+ npm run build
70
+ cd ../../
71
+ ```
72
+
73
+ Editable install is handled by `uv sync` via the local `pyproject.toml`.
74
+
75
+ ## Demo
76
+
77
+ ```bash
78
+ uv run streamlit run app.py
79
+ ```
80
+
81
+ ## Development
82
+
83
+ CCv2 watches a production-style build into `frontend/build` (no separate webpack
84
+ dev server URL). Use two terminals:
85
+
86
+ **Terminal A — frontend watch build**
87
+
88
+ ```bash
89
+ cd streamlit_drawable_konva/frontend
90
+ npm install
91
+ npm run dev
92
+ ```
93
+
94
+ **Terminal B — Streamlit**
95
+
96
+ ```bash
97
+ uv sync
98
+ uv run streamlit run app.py
99
+ ```
100
+
101
+ Refresh the browser after frontend rebuilds.
102
+
103
+ ## API
104
+
105
+ ```python
106
+ from streamlit_drawable_konva import st_canvas
107
+
108
+ result = st_canvas(
109
+ fill_color="rgba(255, 165, 0, 0.3)",
110
+ stroke_width=3,
111
+ stroke_color="#000000",
112
+ background_color="#eee",
113
+ background_image=None,
114
+ update_streamlit=True,
115
+ height=400,
116
+ width=600,
117
+ drawing_mode="freedraw", # freedraw|line|rect|circle|point|polygon|transform
118
+ initial_drawing=None,
119
+ display_toolbar=True,
120
+ point_display_radius=3,
121
+ key="canvas",
122
+ )
123
+
124
+ # result.image_data -> np.ndarray | None
125
+ # result.json_data -> dict | None
126
+ ```
127
+
128
+ ### Polygon controls
129
+
130
+ - Left-click: add point
131
+ - Right-click: close polygon
132
+ - Double-click: remove latest point
133
+
134
+ ### Transform controls
135
+
136
+ - Click object to select
137
+ - Drag / resize / rotate with the transformer
138
+ - Double-click selected object to delete
139
+
140
+ ## Packaging notes
141
+
142
+ - Registered as `streamlit-drawable-konva.st_canvas` via the in-package
143
+ `streamlit_drawable_konva/pyproject.toml` manifest (`asset_dir = frontend/build`).
144
+ - Requires Streamlit `>= 1.51` (Components v2).
145
+ - Frontend toolchain targets Node `>= 18` (Vite 5).
146
+
147
+ ## Publishing (GitHub / PyPI / Streamlit Cloud / gallery)
148
+
149
+ Step-by-step instructions: [`PUBLISHING.md`](PUBLISHING.md).
150
+
151
+ Order: push to GitHub → publish to PyPI → deploy `app.py` on Community Cloud →
152
+ submit [`gallery/streamlit-drawable-konva.json`](gallery/streamlit-drawable-konva.json)
153
+ as a PR to [streamlit/gallery](https://github.com/streamlit/gallery/tree/main/components/registry).
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "streamlit-drawable-konva"
7
+ version = "0.1.0"
8
+ description = "Streamlit drawable canvas component using Konva.js (Components v2)"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [
14
+ { name = "George Siogkas", email = "gsiogkas@cvrlab.com" },
15
+ ]
16
+ dependencies = [
17
+ "streamlit>=1.51",
18
+ "numpy",
19
+ "pillow",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/gsiogkas/streamlit-drawable-konva"
24
+ Documentation = "https://github.com/gsiogkas/streamlit-drawable-konva#readme"
25
+ Repository = "https://github.com/gsiogkas/streamlit-drawable-konva"
26
+ Issues = "https://github.com/gsiogkas/streamlit-drawable-konva/issues"
27
+ # Demo = "https://YOUR_APP_SLUG.streamlit.app" # uncomment after Cloud deploy
28
+
29
+
30
+ [project.optional-dependencies]
31
+ demo = ["pandas"]
32
+ devel = [
33
+ "wheel",
34
+ "pandas",
35
+ ]
36
+
37
+ [dependency-groups]
38
+ demo = ["pandas"]
39
+ dev = ["pandas", "wheel"]
40
+
41
+ [tool.setuptools.packages.find]
42
+ include = ["streamlit_drawable_konva*"]
43
+
44
+ [tool.setuptools]
45
+ include-package-data = true
46
+
47
+ [tool.setuptools.package-data]
48
+ streamlit_drawable_konva = ["frontend/build/**/*", "pyproject.toml"]
49
+
50
+ [tool.uv]
51
+ default-groups = ["demo"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,163 @@
1
+ """Streamlit Drawable Konva — Components v2 canvas with a compatible st_canvas API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import io
7
+ from dataclasses import dataclass
8
+ from typing import Any, Optional
9
+
10
+ import numpy as np
11
+ import streamlit as st
12
+ from PIL import Image
13
+
14
+ _COMPONENT = st.components.v2.component(
15
+ "streamlit-drawable-konva.st_canvas",
16
+ js="index.js",
17
+ html='<div class="react-root"></div>',
18
+ )
19
+
20
+
21
+ @dataclass
22
+ class CanvasResult:
23
+ """Output of the drawable canvas component.
24
+
25
+ Attributes
26
+ ----------
27
+ image_data:
28
+ RGBA image as a NumPy array, or None before the first update.
29
+ json_data:
30
+ JSON scene dict (Konva-oriented objects), or None before the first update.
31
+ """
32
+
33
+ image_data: Optional[np.ndarray] = None
34
+ json_data: Optional[dict] = None
35
+
36
+
37
+ def _data_url_to_image(data_url: str) -> Image.Image:
38
+ _, encoded = data_url.split(";base64,", 1)
39
+ return Image.open(io.BytesIO(base64.b64decode(encoded)))
40
+
41
+
42
+ def _image_to_data_url(img: Image.Image) -> str:
43
+ buf = io.BytesIO()
44
+ img.convert("RGBA").save(buf, format="PNG")
45
+ b64 = base64.b64encode(buf.getvalue()).decode("ascii")
46
+ return f"data:image/png;base64,{b64}"
47
+
48
+
49
+ def _resize_img(img: Image.Image, new_height: int, new_width: int) -> Image.Image:
50
+ return img.resize((new_width, new_height))
51
+
52
+
53
+ def _noop() -> None:
54
+ """Required CCv2 on_change stub so default state keys are always present."""
55
+
56
+
57
+ def st_canvas(
58
+ fill_color: str = "#eee",
59
+ stroke_width: int = 20,
60
+ stroke_color: str = "black",
61
+ background_color: str = "",
62
+ background_image: Optional[Image.Image] = None,
63
+ update_streamlit: bool = True,
64
+ height: int = 400,
65
+ width: int = 600,
66
+ drawing_mode: str = "freedraw",
67
+ initial_drawing: Optional[dict] = None,
68
+ display_toolbar: bool = True,
69
+ point_display_radius: int = 3,
70
+ key: Optional[str] = None,
71
+ ) -> CanvasResult:
72
+ """Create a Konva drawing canvas in a Streamlit app.
73
+
74
+ Compatible with the core API of ``streamlit-drawable-canvas`` /
75
+ ``streamlit-drawable-canvas-fix``, implemented on Streamlit Components v2.
76
+
77
+ Parameters
78
+ ----------
79
+ fill_color:
80
+ Fill color for shapes (CSS color). Defaults to ``"#eee"``.
81
+ stroke_width:
82
+ Stroke / brush width in pixels. Defaults to ``20``.
83
+ stroke_color:
84
+ Stroke color. Defaults to ``"black"``.
85
+ background_color:
86
+ Canvas background color. Empty string means transparent.
87
+ Overridden by ``background_image``. Changing this resets the drawing.
88
+ background_image:
89
+ Pillow image displayed behind drawings (resized to canvas size).
90
+ Not included in returned ``image_data``.
91
+ update_streamlit:
92
+ When True, send canvas data to Streamlit on mouse-up / object updates.
93
+ height:
94
+ Canvas height in pixels. Defaults to ``400``.
95
+ width:
96
+ Canvas width in pixels. Defaults to ``600``.
97
+ drawing_mode:
98
+ One of ``freedraw``, ``transform``, ``line``, ``rect``, ``circle``,
99
+ ``point``, ``polygon``. Defaults to ``freedraw``.
100
+ initial_drawing:
101
+ JSON scene to load (typically a previous ``json_data``).
102
+ display_toolbar:
103
+ Show undo / redo / clear toolbar.
104
+ point_display_radius:
105
+ Radius used when drawing points as circles.
106
+ key:
107
+ Optional Streamlit widget key.
108
+
109
+ Returns
110
+ -------
111
+ CanvasResult
112
+ ``image_data`` is an RGBA NumPy array; ``json_data`` is the scene dict.
113
+ """
114
+ background_image_url: Optional[str] = None
115
+ bg_color = background_color
116
+ if background_image is not None:
117
+ resized = _resize_img(background_image, height, width)
118
+ background_image_url = _image_to_data_url(resized)
119
+ bg_color = ""
120
+
121
+ scene: dict[str, Any] = (
122
+ {"version": "konva-1", "objects": []}
123
+ if initial_drawing is None
124
+ else dict(initial_drawing)
125
+ )
126
+ scene["background"] = bg_color
127
+
128
+ toolbar_h = 40 if display_toolbar else 0
129
+ component_height = height + toolbar_h + 8
130
+
131
+ raw = _COMPONENT(
132
+ key=key,
133
+ data={
134
+ "fillColor": fill_color,
135
+ "strokeWidth": stroke_width,
136
+ "strokeColor": stroke_color,
137
+ "backgroundColor": bg_color,
138
+ "backgroundImageURL": background_image_url,
139
+ "realtimeUpdateStreamlit": update_streamlit
140
+ and (drawing_mode != "polygon"),
141
+ "canvasHeight": height,
142
+ "canvasWidth": width,
143
+ "drawingMode": drawing_mode,
144
+ "initialDrawing": scene,
145
+ "displayToolbar": display_toolbar,
146
+ "displayRadius": point_display_radius,
147
+ },
148
+ default={"image_data_url": None, "json_data": None},
149
+ on_image_data_url_change=_noop,
150
+ on_json_data_change=_noop,
151
+ height=component_height,
152
+ )
153
+
154
+ image_data_url = getattr(raw, "image_data_url", None)
155
+ json_data = getattr(raw, "json_data", None)
156
+
157
+ if image_data_url is None:
158
+ return CanvasResult(image_data=None, json_data=json_data)
159
+
160
+ return CanvasResult(
161
+ image_data=np.asarray(_data_url_to_image(image_data_url)),
162
+ json_data=json_data,
163
+ )