subhashbs36 commited on
Commit
9e2aa25
·
1 Parent(s): 371facd

Refactor code structure for improved readability and maintainability

Browse files
Files changed (9) hide show
  1. .gitignore +140 -0
  2. .python-version +1 -0
  3. README.md +145 -0
  4. dummy.txt +24 -0
  5. fonts/QEJulianDean.ttf +0 -0
  6. fonts/QEKevinKnowles.ttf +0 -0
  7. main.py +245 -0
  8. pyproject.toml +9 -0
  9. uv.lock +0 -0
.gitignore ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98
+ __pypackages__/
99
+
100
+ # Celery stuff
101
+ celerybeat-schedule
102
+ celerybeat.pid
103
+
104
+ # SageMath parsed files
105
+ *.sage.py
106
+
107
+ # Environments
108
+ .env
109
+ .venv
110
+ env/
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
133
+
134
+ # pytype static type analyzer
135
+ .pytype/
136
+
137
+ # Cython debug symbols
138
+ cython_debug/
139
+
140
+ # Ignore changes to dummy.txt when committing
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.11
README.md ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ✍️ Text2Pen
2
+
3
+ A modern, customizable tool to convert your typed text into realistic handwriting on A4 paper.
4
+ Supports multi-column layouts, handwriting font selection, background customization, color, jitter, and more—all with a clean Gradio web UI.
5
+
6
+ ---
7
+
8
+ ## 🚀 Features
9
+
10
+ - **Modern Gradio Web UI**: Easy-to-use, clean, and responsive.
11
+ - **Direct Text Input or File Upload**: Paste text or upload `.txt` files.
12
+ - **Handwriting Fonts**: Choose from built-in fonts or upload your own `.ttf`.
13
+ - **Backgrounds**: Use blank, ruled, or custom backgrounds.
14
+ - **Multi-Column Layout**: Split your page into 1, 2, or 3 columns.
15
+ - **Customizable**: Font size, line spacing, margin, color, jitter, and more.
16
+ - **Preview & Download**: Preview pages as images, download as PDF (generated on demand).
17
+ - **A4 Output**: Perfect for cheat sheets, study notes, or printing.
18
+
19
+ ---
20
+
21
+ ## 📸 Screenshots
22
+
23
+ ### Main UI
24
+
25
+ ![Main UI](sample_ui_1.png)
26
+
27
+ ### Customization Tab
28
+
29
+ ![Customization Tab](sample_ui_2.png)
30
+
31
+ ### Example Output
32
+
33
+ ![PDF Output Example](sample_output_page.png)
34
+
35
+ ---
36
+
37
+ ## 🛠️ Installation
38
+
39
+ 1. **Clone the repository:**
40
+ ```sh
41
+ git clone https://github.com/yourusername/Text2Pen.git
42
+ cd Text2Pen
43
+ ```
44
+
45
+ 2. **Install dependencies:**
46
+ ```sh
47
+ pip install -r requirements.txt
48
+ ```
49
+ *Requirements: `gradio`, `pillow`*
50
+
51
+ 3. **(Optional) Add Fonts/Backgrounds:**
52
+ - Place `.ttf` fonts in the `fonts/` folder.
53
+ - Place background images in the `backgrounds/` folder.
54
+
55
+ ---
56
+
57
+ ## 🏃 Usage
58
+
59
+ ### Start the App
60
+
61
+ ```sh
62
+ python main.py
63
+ ```
64
+
65
+ - The Gradio UI will open in your browser.
66
+ - Enter or upload your text, customize options, and generate handwriting.
67
+
68
+ ### Output
69
+
70
+ - **Preview**: See generated pages as images.
71
+ - **Download PDF**: Click "Generate & Download PDF" to get a PDF (temporary file, not saved).
72
+
73
+ ---
74
+
75
+ ## 📂 Project Structure
76
+
77
+ ```
78
+ Text2Pen/
79
+
80
+ ├── main.py
81
+ ├── README.md
82
+ ├── requirements.txt
83
+ ├── fonts/
84
+ │ ├── QEJulianDean.ttf
85
+ │ └── QEKevinKnowles.ttf
86
+ ├── backgrounds/
87
+ │ └── ruled_a4.png
88
+ ├── generated/
89
+ │ ├── images/
90
+ │ └── pdfs/
91
+ ├── sample_ui_1.png
92
+ ├── sample_ui_2.png
93
+ └── sample_output_page.png
94
+ ```
95
+
96
+ ---
97
+
98
+ ## ✏️ Customization
99
+
100
+ - **Fonts**: Add your own `.ttf` files to the `fonts/` folder or upload via the UI.
101
+ - **Backgrounds**: Add PNG/JPG backgrounds to the `backgrounds/` folder or upload via the UI.
102
+ - **Advanced Options**: Adjust font size, line spacing, margin, jitter, columns, and color in the Customization tab.
103
+
104
+ ---
105
+
106
+ ## 📝 Notes
107
+
108
+ - PDF files are generated as temporary files and are not stored permanently.
109
+ - Images are saved in `generated/images/` for preview purposes.
110
+ - For best results, use high-quality handwriting fonts and A4-sized backgrounds.
111
+
112
+ ---
113
+
114
+ ## 📄 License
115
+
116
+ MIT License
117
+
118
+ ---
119
+
120
+ ## 🙏 Credits
121
+
122
+ - [Gradio](https://gradio.app/)
123
+ - [Pillow](https://python-pillow.org/)
124
+ - Handwriting fonts from [Google Fonts](https://fonts.google.com/) and [DaFont](https://www.dafont.com/)
125
+
126
+ ---
127
+
128
+ ## 💡 Example Use Cases
129
+
130
+ - Micro cheat sheets for exams
131
+ - Study notes that look handwritten
132
+ - Generating realistic handwritten letters
133
+ - Printing custom notes for planners or journals
134
+
135
+ ---
136
+
137
+ ## 🤝 Contributing
138
+
139
+ Pull requests and suggestions are welcome!
140
+
141
+ ---
142
+
143
+ ## 📧 Contact
144
+
145
+ For questions or feedback, open an issue or contact [your-email@example.com](mailto:your-email@example.com).
dummy.txt ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Domain0 in Xen runs all the device drivers for the host machine hardware
2
+ Hardware virtualization extensions do NOT offer binary translation acceleration
3
+ TinyOS does NOT support updating part of the code at runtime
4
+ Grant regions in Tock OS do NOT store kernel states
5
+
6
+ Short QnA--
7
+ Here is pseudocode of a simple spinlock implementation using test-and-set:
8
+ This code achieves mutual exclusion, but what are the two potential problems of this lock implementation?
9
+ Answer:
10
+ No bounded waiting → starvation
11
+ High cache coherence traffic due to frequent test-and-set on the same memory location
12
+
13
+ Consider a hypothetical hard disk drive with 3000 RPM, 100MB/s of data transfer rate, and 10 ms of seek time. If the file system reads data from the disk in a block size of 2MB per access, the latency is [ ] ms and the throughput is [ ] MB/s.
14
+ Latency: 0.04 seconds (or 40 ms)
15
+ Throughput: 50 MB/s
16
+
17
+ Consider a virtualization scenario where the hypervisor and the guest OS use 4-level paging. If nested/extended paging is used, what is the number of memory accesses required to translate a guest virtual address to a host physical address? Also, what is the number for shadow paging?
18
+ Nested/Extended Paging: 21 memory accesses
19
+ Shadow Paging: 5 memory accesses
20
+
21
+ Why event-driven systems can be more memory efficient than multi-threaded systems?
22
+ Event-driven systems use a single stack for scheduling tasks, as opposed to multi-threaded systems which require a separate stack per thread, thus using more memory.
23
+
24
+
fonts/QEJulianDean.ttf ADDED
Binary file (13.8 kB). View file
 
fonts/QEKevinKnowles.ttf ADDED
Binary file (36.8 kB). View file
 
main.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Importing Library
2
+ # import argparse
3
+ import random
4
+ from PIL import Image, ImageDraw, ImageFont
5
+ import gradio as gr
6
+ import io
7
+ import os
8
+ import tempfile
9
+
10
+ A4_WIDTH, A4_HEIGHT = 2480, 3508 # A4 at 300dpi in pixels
11
+
12
+ DEMO_TEXT = """This is a demo of the Text2Pen!
13
+ - You can type or paste your own text here.
14
+ - Or upload a .txt file.
15
+ - Choose a handwriting font, font size, and more.
16
+ - Try different columns and jitters for a natural look.
17
+ Enjoy!"""
18
+
19
+ # Optionally, provide some bundled fonts/backgrounds
20
+ BUILTIN_FONTS = {
21
+ "Julian Hand": "fonts/QEJulianDean.ttf",
22
+ "Kevin Flower": "fonts/QEKevinKnowles.ttf",
23
+ "Upload your own": None
24
+ }
25
+ BUILTIN_BGS = {
26
+ "Blank (White)": None,
27
+ "Ruled Paper": "backgrounds/ruled_a4.png",
28
+ "Upload your own": None
29
+ }
30
+
31
+ def create_new_page(bg_path=None, bg_img=None):
32
+ if bg_img is not None:
33
+ bg = bg_img.convert("RGBA")
34
+ if bg.size != (A4_WIDTH, A4_HEIGHT):
35
+ bg = bg.resize((A4_WIDTH, A4_HEIGHT), Image.LANCZOS)
36
+ return bg
37
+ if bg_path:
38
+ try:
39
+ bg = Image.open(bg_path).convert("RGBA")
40
+ if bg.size != (A4_WIDTH, A4_HEIGHT):
41
+ bg = bg.resize((A4_WIDTH, A4_HEIGHT), Image.LANCZOS)
42
+ return bg
43
+ except Exception:
44
+ pass
45
+ # fallback: blank white A4
46
+ return Image.new("RGBA", (A4_WIDTH, A4_HEIGHT), (255,255,255,255))
47
+
48
+ def get_text_size(text, font):
49
+ if hasattr(font, "getbbox"):
50
+ bbox = font.getbbox(text)
51
+ width = bbox[2] - bbox[0]
52
+ height = bbox[3] - bbox[1]
53
+ return width, height
54
+ else:
55
+ return font.getsize(text)
56
+
57
+ # Ensure output folders exist
58
+ GENERATED_IMG_DIR = "generated/images"
59
+ GENERATED_PDF_DIR = "generated/pdfs"
60
+ os.makedirs(GENERATED_IMG_DIR, exist_ok=True)
61
+ os.makedirs(GENERATED_PDF_DIR, exist_ok=True)
62
+
63
+ def handwriting_render(
64
+ textfile,
65
+ textinput,
66
+ font_choice,
67
+ fontfile,
68
+ fontsize,
69
+ linespacing,
70
+ margin,
71
+ jitter,
72
+ columns,
73
+ bg_choice,
74
+ bgfile,
75
+ color,
76
+ preview_first,
77
+ download_pdf=False # new argument to control PDF generation
78
+ ):
79
+ # Text: file takes priority, else textarea
80
+ if textfile is not None:
81
+ text = textfile.read().decode("utf-8")
82
+ elif textinput and textinput.strip():
83
+ text = textinput
84
+ else:
85
+ return None, None
86
+
87
+ # Font: built-in or uploaded
88
+ font_path = BUILTIN_FONTS.get(font_choice)
89
+ if font_path is None and fontfile is not None:
90
+ font_path = fontfile.name
91
+ try:
92
+ font = ImageFont.truetype(font_path, fontsize)
93
+ except Exception:
94
+ font = ImageFont.load_default()
95
+
96
+ # BG: built-in or uploaded
97
+ bg_path = BUILTIN_BGS.get(bg_choice)
98
+ bg_img = None
99
+ if bg_path is None and bgfile is not None:
100
+ bg_img = Image.open(bgfile).convert("RGBA")
101
+ elif bg_path is not None:
102
+ try:
103
+ bg_img = Image.open(bg_path).convert("RGBA")
104
+ except Exception:
105
+ bg_img = None
106
+
107
+ col_count = int(columns)
108
+ col_width = (A4_WIDTH - (col_count + 1) * margin) // col_count
109
+
110
+ pages = []
111
+ BG = create_new_page(bg_img=bg_img)
112
+ draw = ImageDraw.Draw(BG)
113
+ sheet_width, sheet_height = BG.size
114
+
115
+ col = 0
116
+ gap = margin
117
+ ht = margin
118
+
119
+ words = text.replace("\n", " \n ").split(" ")
120
+
121
+ for word in words:
122
+ if word == "\n":
123
+ gap = margin + col * (col_width + margin)
124
+ ht += fontsize + linespacing
125
+ continue
126
+ word_width, word_height = get_text_size(word, font)
127
+ # Word wrapping in column
128
+ if gap + word_width > margin + (col + 1) * col_width + col * margin:
129
+ gap = margin + col * (col_width + margin)
130
+ ht += fontsize + linespacing
131
+ # If column overflows vertically, move to next column or page
132
+ if ht + word_height > sheet_height - margin:
133
+ if col < col_count - 1:
134
+ col += 1
135
+ gap = margin + col * (col_width + margin)
136
+ ht = margin
137
+ else:
138
+ pages.append(BG)
139
+ BG = create_new_page(bg_img=bg_img)
140
+ draw = ImageDraw.Draw(BG)
141
+ col = 0
142
+ gap = margin
143
+ ht = margin
144
+ # Draw each character with jitter
145
+ for ch in word:
146
+ ch_width, ch_height = get_text_size(ch, font)
147
+ jitter_x = random.randint(-jitter, jitter) if jitter > 0 else 0
148
+ jitter_y = random.randint(-jitter, jitter) if jitter > 0 else 0
149
+ draw.text((gap + jitter_x, ht + jitter_y), ch, font=font, fill=color)
150
+ gap += ch_width
151
+ gap += get_text_size(" ", font)[0]
152
+ pages.append(BG)
153
+ # Prepare outputs
154
+ img_list = []
155
+ if preview_first:
156
+ pages_to_show = [pages[0]]
157
+ else:
158
+ pages_to_show = pages
159
+
160
+ # Save images to disk and collect their filepaths for Gradio Gallery
161
+ img_paths = []
162
+ for idx, page in enumerate(pages_to_show):
163
+ img_filename = os.path.join(GENERATED_IMG_DIR, f"handwriting_page_{idx+1}.png")
164
+ page.save(img_filename)
165
+ img_paths.append(img_filename)
166
+
167
+ # Only generate PDF if requested
168
+ pdf_filename = None
169
+ if download_pdf:
170
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_pdf:
171
+ if len(pages) == 1:
172
+ pages[0].save(tmp_pdf.name, format="PDF", resolution=300)
173
+ else:
174
+ pages[0].save(tmp_pdf.name, format="PDF", save_all=True, append_images=pages[1:], resolution=300)
175
+ pdf_filename = tmp_pdf.name
176
+
177
+ return img_paths, pdf_filename
178
+
179
+ def gradio_ui():
180
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
181
+ gr.Markdown(
182
+ """
183
+ # ✍️ Text2Pen
184
+ Easily convert your text into realistic handwriting on A4 paper.
185
+ """
186
+ )
187
+ with gr.Tab("Text & Output"):
188
+ with gr.Row(equal_height=True):
189
+ with gr.Column(scale=2):
190
+ textinput = gr.Textbox(
191
+ label="Enter Text",
192
+ value=DEMO_TEXT,
193
+ lines=12,
194
+ max_lines=30,
195
+ placeholder="Type or paste your text here..."
196
+ )
197
+ textfile = gr.File(label="Or Upload Text File (.txt)", file_types=[".txt"])
198
+ run_btn = gr.Button("Generate Handwriting", elem_id="generate-btn")
199
+ with gr.Column(scale=1, min_width=180):
200
+ gallery = gr.Gallery(label="Preview Pages", columns=1, height=500)
201
+ pdfout = gr.File(label="Download PDF")
202
+ pdf_btn = gr.Button("Generate & Download PDF", elem_id="pdf-btn")
203
+
204
+ with gr.Tab("Customization"):
205
+ with gr.Row():
206
+ with gr.Column(scale=1):
207
+ gr.Markdown("### Advanced Options")
208
+ fontsize = gr.Slider(24, 120, value=48, step=1, label="Font Size (pt)")
209
+ linespacing = gr.Slider(0, 50, value=10, step=1, label="Line Spacing (px)")
210
+ margin = gr.Slider(20, 200, value=70, step=1, label="Margin (px)")
211
+ jitter = gr.Slider(0, 10, value=0, step=1, label="Jitter (px, for natural look)")
212
+ columns = gr.Radio(choices=["1", "2", "3"], value="1", label="Columns per Page")
213
+ preview_first = gr.Checkbox(label="Preview Only First Page", value=True)
214
+ with gr.Column(scale=1):
215
+ gr.Markdown("### Font Options")
216
+ font_choice = gr.Dropdown(list(BUILTIN_FONTS.keys()), value="Julian Hand", label="Handwriting Font")
217
+ fontfile = gr.File(label="Or Upload TTF Font", file_types=[".ttf"])
218
+ gr.Markdown("### Background Options")
219
+ color = gr.ColorPicker(label="Handwriting Color", value="#000000")
220
+ bg_choice = gr.Dropdown(list(BUILTIN_BGS.keys()), value="Blank (White)", label="Background")
221
+ bgfile = gr.File(label="Or Upload Background Image (A4 PNG/JPG)", file_types=[".png", ".jpg", ".jpeg"])
222
+
223
+ # Link the customization tab values to the main tab
224
+ run_btn.click(
225
+ lambda *args: handwriting_render(*args, download_pdf=False),
226
+ inputs=[
227
+ textfile, textinput, font_choice, fontfile, fontsize, linespacing, margin,
228
+ jitter, columns, bg_choice, bgfile, color, preview_first
229
+ ],
230
+ outputs=[gallery, pdfout] # Always output both, but pdfout will be None unless requested
231
+ )
232
+ # Add a separate button for PDF download
233
+ pdf_btn.click(
234
+ lambda *args: handwriting_render(*args, download_pdf=True),
235
+ inputs=[
236
+ textfile, textinput, font_choice, fontfile, fontsize, linespacing, margin,
237
+ jitter, columns, bg_choice, bgfile, color, preview_first
238
+ ],
239
+ outputs=[gallery, pdfout]
240
+ )
241
+ demo.launch()
242
+
243
+ if __name__ == "__main__":
244
+ gradio_ui()
245
+
pyproject.toml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "Text2Pen"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "gradio>=5.33.0",
9
+ ]
uv.lock ADDED
The diff for this file is too large to render. See raw diff