streamlit-mini-toolkit 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.
Files changed (43) hide show
  1. streamlit_mini_toolkit-0.1.0/.gitignore +21 -0
  2. streamlit_mini_toolkit-0.1.0/.streamlit/config.toml +16 -0
  3. streamlit_mini_toolkit-0.1.0/LICENSE +21 -0
  4. streamlit_mini_toolkit-0.1.0/PKG-INFO +151 -0
  5. streamlit_mini_toolkit-0.1.0/README.md +108 -0
  6. streamlit_mini_toolkit-0.1.0/app.py +111 -0
  7. streamlit_mini_toolkit-0.1.0/core/__init__.py +1 -0
  8. streamlit_mini_toolkit-0.1.0/core/net.py +56 -0
  9. streamlit_mini_toolkit-0.1.0/core/pdf.py +12 -0
  10. streamlit_mini_toolkit-0.1.0/core/registry.py +75 -0
  11. streamlit_mini_toolkit-0.1.0/core/ui.py +90 -0
  12. streamlit_mini_toolkit-0.1.0/mini_toolkit/__init__.py +3 -0
  13. streamlit_mini_toolkit-0.1.0/mini_toolkit/__main__.py +3 -0
  14. streamlit_mini_toolkit-0.1.0/mini_toolkit/cli.py +40 -0
  15. streamlit_mini_toolkit-0.1.0/pyproject.toml +65 -0
  16. streamlit_mini_toolkit-0.1.0/requirements.txt +26 -0
  17. streamlit_mini_toolkit-0.1.0/style/style.css +171 -0
  18. streamlit_mini_toolkit-0.1.0/tools/__init__.py +1 -0
  19. streamlit_mini_toolkit-0.1.0/tools/background_remover.py +65 -0
  20. streamlit_mini_toolkit-0.1.0/tools/change_tracker.py +90 -0
  21. streamlit_mini_toolkit-0.1.0/tools/chat_alert.py +42 -0
  22. streamlit_mini_toolkit-0.1.0/tools/color_picker.py +40 -0
  23. streamlit_mini_toolkit-0.1.0/tools/document_converter.py +121 -0
  24. streamlit_mini_toolkit-0.1.0/tools/file_compressor.py +78 -0
  25. streamlit_mini_toolkit-0.1.0/tools/github_trending.py +53 -0
  26. streamlit_mini_toolkit-0.1.0/tools/hash_generator.py +43 -0
  27. streamlit_mini_toolkit-0.1.0/tools/image_converter.py +63 -0
  28. streamlit_mini_toolkit-0.1.0/tools/image_downloader.py +67 -0
  29. streamlit_mini_toolkit-0.1.0/tools/image_enhancer.py +54 -0
  30. streamlit_mini_toolkit-0.1.0/tools/image_watermarker.py +63 -0
  31. streamlit_mini_toolkit-0.1.0/tools/ocr.py +93 -0
  32. streamlit_mini_toolkit-0.1.0/tools/page_speed.py +45 -0
  33. streamlit_mini_toolkit-0.1.0/tools/password_checker.py +53 -0
  34. streamlit_mini_toolkit-0.1.0/tools/pdf_tools.py +83 -0
  35. streamlit_mini_toolkit-0.1.0/tools/qr_code.py +83 -0
  36. streamlit_mini_toolkit-0.1.0/tools/resume_builder.py +385 -0
  37. streamlit_mini_toolkit-0.1.0/tools/seo_health.py +64 -0
  38. streamlit_mini_toolkit-0.1.0/tools/signature.py +72 -0
  39. streamlit_mini_toolkit-0.1.0/tools/ssl_checker.py +68 -0
  40. streamlit_mini_toolkit-0.1.0/tools/text_to_speech.py +34 -0
  41. streamlit_mini_toolkit-0.1.0/tools/text_utilities.py +36 -0
  42. streamlit_mini_toolkit-0.1.0/tools/url_shortener.py +29 -0
  43. streamlit_mini_toolkit-0.1.0/tools/website_status.py +39 -0
@@ -0,0 +1,21 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ venv/
6
+ *.egg-info/
7
+
8
+ # Streamlit
9
+ .streamlit/secrets.toml
10
+
11
+ # OS / editor
12
+ .DS_Store
13
+ .idea/
14
+ .vscode/
15
+
16
+ # Vercel
17
+ .vercel
18
+
19
+ # Packaging
20
+ dist/
21
+ build/
@@ -0,0 +1,16 @@
1
+ [theme]
2
+ base = "dark"
3
+ primaryColor = "#5B8CFF"
4
+ backgroundColor = "#0D1017"
5
+ secondaryBackgroundColor = "#161A23"
6
+ textColor = "#E7E9EE"
7
+ font = "sans serif"
8
+
9
+ [server]
10
+ maxUploadSize = 50
11
+
12
+ [browser]
13
+ gatherUsageStats = false
14
+
15
+ [client]
16
+ toolbarMode = "minimal"
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sarif Mia
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,151 @@
1
+ Metadata-Version: 2.5
2
+ Name: streamlit-mini-toolkit
3
+ Version: 0.1.0
4
+ Summary: 25 everyday utilities (image, document, text and web tools) in one Streamlit app.
5
+ Project-URL: Homepage, https://github.com/sarif-mia/mini-toolkit
6
+ Project-URL: Source, https://github.com/sarif-mia/mini-toolkit
7
+ Project-URL: Tracker, https://github.com/sarif-mia/mini-toolkit/issues
8
+ Author: Sarif Mia
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: image,pdf,qr-code,seo,streamlit,toolkit,utilities
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Utilities
15
+ Requires-Python: >=3.9
16
+ Requires-Dist: beautifulsoup4>=4.12
17
+ Requires-Dist: fpdf2>=2.7.8
18
+ Requires-Dist: gtts>=2.5
19
+ Requires-Dist: pillow>=10.1
20
+ Requires-Dist: pypdf>=4.0
21
+ Requires-Dist: python-docx>=1.1
22
+ Requires-Dist: qrcode>=7.4
23
+ Requires-Dist: requests>=2.31
24
+ Requires-Dist: streamlit-image-coordinates>=0.1.9
25
+ Requires-Dist: streamlit>=1.50
26
+ Provides-Extra: all
27
+ Requires-Dist: docx2pdf>=0.1.8; (sys_platform != 'linux') and extra == 'all'
28
+ Requires-Dist: opencv-python-headless>=4.8; extra == 'all'
29
+ Requires-Dist: pdf2docx>=0.5.8; extra == 'all'
30
+ Requires-Dist: pytesseract>=0.3.10; extra == 'all'
31
+ Requires-Dist: pywin32; (sys_platform == 'win32') and extra == 'all'
32
+ Requires-Dist: rembg[cpu]>=2.0.50; extra == 'all'
33
+ Provides-Extra: bg
34
+ Requires-Dist: rembg[cpu]>=2.0.50; extra == 'bg'
35
+ Provides-Extra: docs
36
+ Requires-Dist: docx2pdf>=0.1.8; (sys_platform != 'linux') and extra == 'docs'
37
+ Requires-Dist: pdf2docx>=0.5.8; extra == 'docs'
38
+ Requires-Dist: pywin32; (sys_platform == 'win32') and extra == 'docs'
39
+ Provides-Extra: ocr
40
+ Requires-Dist: opencv-python-headless>=4.8; extra == 'ocr'
41
+ Requires-Dist: pytesseract>=0.3.10; extra == 'ocr'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # ๐Ÿงฐ Mini Toolkit
45
+
46
+ A collection of 25 everyday utilities โ€” image editing, document conversion, text tools and website checks โ€” in one fast, clean Streamlit app. No sign-up, no clutter.
47
+
48
+ ## Tools
49
+
50
+ | Category | Tools |
51
+ | --- | --- |
52
+ | **Image** | Background Remover ยท Image Converter ยท Image Enhancer ยท Image Watermarker ยท Image Color Picker ยท QR Code Generator ยท OCR Text Extractor |
53
+ | **Documents** | Document Converter (DOCX โ†” PDF โ†” TXT) ยท PDF Merge & Split ยท File Compressor ยท Resume Builder ยท Email Signature |
54
+ | **Text & Security** | Text Utilities ยท Text to Speech ยท Hash Generator ยท Password Checker & Generator |
55
+ | **Web & Developer** | Website Status ยท Page Speed Checker ยท SSL Certificate Checker ยท SEO Health Check ยท Website Image Downloader ยท URL Shortener ยท Website Change Tracker ยท GitHub Trending ยท Chat Alert Bot (Slack / Teams / Discord) |
56
+
57
+ Every tool has its own shareable URL, e.g. `/?tool=qr-code`.
58
+
59
+ ## Install from PyPI
60
+
61
+ ```sh
62
+ pip install streamlit-mini-toolkit # core tools
63
+ pip install "streamlit-mini-toolkit[all]" # + background remover, OCR, DOCX/PDF conversion
64
+ mini-toolkit # opens the app at http://localhost:8501
65
+ ```
66
+
67
+ Pick extras individually if you prefer: `[bg]` (Background Remover), `[ocr]` (OCR Text Extractor), `[docs]` (DOCX โ†” PDF). Streamlit flags pass through, e.g. `mini-toolkit --server.port 8080`.
68
+
69
+ ## Quick start (from source)
70
+
71
+ Requires Python 3.9+.
72
+
73
+ ```sh
74
+ git clone https://github.com/sarif-mia/mini-toolkit.git
75
+ cd mini-toolkit
76
+ python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
77
+ pip install -r requirements.txt
78
+ streamlit run app.py
79
+ ```
80
+
81
+ The app opens at <http://localhost:8501>.
82
+
83
+ ### System dependencies (optional)
84
+
85
+ A few tools call external programs. Everything else works without them.
86
+
87
+ | Feature | Needs |
88
+ | --- | --- |
89
+ | OCR Text Extractor | [Tesseract](https://tesseract-ocr.github.io/tessdoc/Installation.html) |
90
+ | DOCX โ†’ PDF | [LibreOffice](https://www.libreoffice.org/) (or Microsoft Word on Windows/macOS) |
91
+
92
+ On Debian/Ubuntu: `sudo xargs apt-get install -y < packages.txt`
93
+
94
+ ## Deployment
95
+
96
+ ### Streamlit Community Cloud (recommended, free)
97
+
98
+ 1. Go to [share.streamlit.io](https://share.streamlit.io) and sign in with GitHub.
99
+ 2. **Create app** โ†’ pick this repository, branch `master`, main file `app.py`.
100
+ 3. Deploy. `requirements.txt` and `packages.txt` are picked up automatically.
101
+
102
+ ### Docker (Render, Railway, Fly.io, Hugging Face Spaces, โ€ฆ)
103
+
104
+ ```sh
105
+ docker build -t mini-toolkit .
106
+ docker run -p 8501:8501 mini-toolkit
107
+ ```
108
+
109
+ > **Why not Vercel?** Vercel runs short-lived serverless functions, while Streamlit needs a long-running server with a persistent WebSocket connection. Use one of the options above instead.
110
+
111
+ ### Optional secrets
112
+
113
+ The Website Change Tracker can email change summaries. Add these to `.streamlit/secrets.toml` locally, or to your host's secrets settings:
114
+
115
+ ```toml
116
+ smtp_server = "smtp.gmail.com"
117
+ smtp_port = 587
118
+ smtp_user = "you@example.com"
119
+ smtp_pass = "app-password"
120
+ ```
121
+
122
+ ## Project structure
123
+
124
+ ```
125
+ app.py Entry point: page config, routing, home page
126
+ core/
127
+ registry.py List of tools and categories (add new tools here)
128
+ ui.py Shared UI helpers (headers, uploads, ZIP export, footer)
129
+ net.py Safe HTTP fetching for the web tools
130
+ tools/ One module per tool, each exposing render()
131
+ style/style.css Custom styling
132
+ .streamlit/ Theme and server config
133
+ ```
134
+
135
+ ### Adding a tool
136
+
137
+ 1. Create `tools/my_tool.py` with a `render()` function.
138
+ 2. Register it in `core/registry.py`:
139
+ ```python
140
+ Tool("my-tool", "My Tool", "๐Ÿงช", "One-line description.", TEXT, "my_tool"),
141
+ ```
142
+
143
+ Tools are imported lazily, so a missing optional dependency only affects that one tool.
144
+
145
+ ## License
146
+
147
+ Distributed under the MIT License. See [`LICENSE`](LICENSE).
148
+
149
+ ---
150
+
151
+ Built by [Sarif](https://sarifmiia.netlify.app/) ยท [GitHub](https://github.com/sarif-mia) ยท [LinkedIn](https://www.linkedin.com/in/sarif-mia/)
@@ -0,0 +1,108 @@
1
+ # ๐Ÿงฐ Mini Toolkit
2
+
3
+ A collection of 25 everyday utilities โ€” image editing, document conversion, text tools and website checks โ€” in one fast, clean Streamlit app. No sign-up, no clutter.
4
+
5
+ ## Tools
6
+
7
+ | Category | Tools |
8
+ | --- | --- |
9
+ | **Image** | Background Remover ยท Image Converter ยท Image Enhancer ยท Image Watermarker ยท Image Color Picker ยท QR Code Generator ยท OCR Text Extractor |
10
+ | **Documents** | Document Converter (DOCX โ†” PDF โ†” TXT) ยท PDF Merge & Split ยท File Compressor ยท Resume Builder ยท Email Signature |
11
+ | **Text & Security** | Text Utilities ยท Text to Speech ยท Hash Generator ยท Password Checker & Generator |
12
+ | **Web & Developer** | Website Status ยท Page Speed Checker ยท SSL Certificate Checker ยท SEO Health Check ยท Website Image Downloader ยท URL Shortener ยท Website Change Tracker ยท GitHub Trending ยท Chat Alert Bot (Slack / Teams / Discord) |
13
+
14
+ Every tool has its own shareable URL, e.g. `/?tool=qr-code`.
15
+
16
+ ## Install from PyPI
17
+
18
+ ```sh
19
+ pip install streamlit-mini-toolkit # core tools
20
+ pip install "streamlit-mini-toolkit[all]" # + background remover, OCR, DOCX/PDF conversion
21
+ mini-toolkit # opens the app at http://localhost:8501
22
+ ```
23
+
24
+ Pick extras individually if you prefer: `[bg]` (Background Remover), `[ocr]` (OCR Text Extractor), `[docs]` (DOCX โ†” PDF). Streamlit flags pass through, e.g. `mini-toolkit --server.port 8080`.
25
+
26
+ ## Quick start (from source)
27
+
28
+ Requires Python 3.9+.
29
+
30
+ ```sh
31
+ git clone https://github.com/sarif-mia/mini-toolkit.git
32
+ cd mini-toolkit
33
+ python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
34
+ pip install -r requirements.txt
35
+ streamlit run app.py
36
+ ```
37
+
38
+ The app opens at <http://localhost:8501>.
39
+
40
+ ### System dependencies (optional)
41
+
42
+ A few tools call external programs. Everything else works without them.
43
+
44
+ | Feature | Needs |
45
+ | --- | --- |
46
+ | OCR Text Extractor | [Tesseract](https://tesseract-ocr.github.io/tessdoc/Installation.html) |
47
+ | DOCX โ†’ PDF | [LibreOffice](https://www.libreoffice.org/) (or Microsoft Word on Windows/macOS) |
48
+
49
+ On Debian/Ubuntu: `sudo xargs apt-get install -y < packages.txt`
50
+
51
+ ## Deployment
52
+
53
+ ### Streamlit Community Cloud (recommended, free)
54
+
55
+ 1. Go to [share.streamlit.io](https://share.streamlit.io) and sign in with GitHub.
56
+ 2. **Create app** โ†’ pick this repository, branch `master`, main file `app.py`.
57
+ 3. Deploy. `requirements.txt` and `packages.txt` are picked up automatically.
58
+
59
+ ### Docker (Render, Railway, Fly.io, Hugging Face Spaces, โ€ฆ)
60
+
61
+ ```sh
62
+ docker build -t mini-toolkit .
63
+ docker run -p 8501:8501 mini-toolkit
64
+ ```
65
+
66
+ > **Why not Vercel?** Vercel runs short-lived serverless functions, while Streamlit needs a long-running server with a persistent WebSocket connection. Use one of the options above instead.
67
+
68
+ ### Optional secrets
69
+
70
+ The Website Change Tracker can email change summaries. Add these to `.streamlit/secrets.toml` locally, or to your host's secrets settings:
71
+
72
+ ```toml
73
+ smtp_server = "smtp.gmail.com"
74
+ smtp_port = 587
75
+ smtp_user = "you@example.com"
76
+ smtp_pass = "app-password"
77
+ ```
78
+
79
+ ## Project structure
80
+
81
+ ```
82
+ app.py Entry point: page config, routing, home page
83
+ core/
84
+ registry.py List of tools and categories (add new tools here)
85
+ ui.py Shared UI helpers (headers, uploads, ZIP export, footer)
86
+ net.py Safe HTTP fetching for the web tools
87
+ tools/ One module per tool, each exposing render()
88
+ style/style.css Custom styling
89
+ .streamlit/ Theme and server config
90
+ ```
91
+
92
+ ### Adding a tool
93
+
94
+ 1. Create `tools/my_tool.py` with a `render()` function.
95
+ 2. Register it in `core/registry.py`:
96
+ ```python
97
+ Tool("my-tool", "My Tool", "๐Ÿงช", "One-line description.", TEXT, "my_tool"),
98
+ ```
99
+
100
+ Tools are imported lazily, so a missing optional dependency only affects that one tool.
101
+
102
+ ## License
103
+
104
+ Distributed under the MIT License. See [`LICENSE`](LICENSE).
105
+
106
+ ---
107
+
108
+ Built by [Sarif](https://sarifmiia.netlify.app/) ยท [GitHub](https://github.com/sarif-mia) ยท [LinkedIn](https://www.linkedin.com/in/sarif-mia/)
@@ -0,0 +1,111 @@
1
+ """Mini Toolkit โ€” a collection of everyday utilities in one Streamlit app.
2
+
3
+ Run locally with: streamlit run app.py
4
+ """
5
+ import html
6
+ import traceback
7
+
8
+ import streamlit as st
9
+
10
+ from core.registry import CATEGORIES, TOOLS, TOOLS_BY_SLUG, Tool, tools_in
11
+ from core.ui import footer, load_css, tool_header
12
+
13
+ st.set_page_config(page_title="Mini Toolkit", page_icon="๐Ÿงฐ", layout="wide", initial_sidebar_state="collapsed")
14
+ load_css()
15
+
16
+
17
+ def open_tool(slug=None) -> None:
18
+ if slug:
19
+ st.query_params["tool"] = slug
20
+ else:
21
+ st.query_params.clear()
22
+
23
+
24
+ def render_sidebar(active=None) -> None:
25
+ with st.sidebar:
26
+ st.button("๐Ÿ  All tools", on_click=open_tool, width="stretch", type="primary" if active is None else "secondary")
27
+ for category in CATEGORIES:
28
+ st.markdown(f"<div class='nav-heading'>{category}</div>", unsafe_allow_html=True)
29
+ for tool in tools_in(category):
30
+ st.button(
31
+ f"{tool.icon} {tool.name}",
32
+ key=f"nav_{tool.slug}",
33
+ on_click=open_tool,
34
+ args=(tool.slug,),
35
+ width="stretch",
36
+ type="primary" if active and active.slug == tool.slug else "tertiary",
37
+ )
38
+
39
+
40
+ def tool_card(tool: Tool) -> None:
41
+ with st.container(border=True):
42
+ st.markdown(
43
+ f"""
44
+ <div class="tool-card">
45
+ <div class="tool-card-icon">{tool.icon}</div>
46
+ <div class="tool-card-title">{html.escape(tool.name)}</div>
47
+ <div class="tool-card-desc">{html.escape(tool.description)}</div>
48
+ </div>
49
+ """,
50
+ unsafe_allow_html=True,
51
+ )
52
+ st.button("Open", key=f"open_{tool.slug}", on_click=open_tool, args=(tool.slug,), width="stretch")
53
+
54
+
55
+ def render_home() -> None:
56
+ st.markdown(
57
+ f"""
58
+ <section class="hero">
59
+ <span class="hero-badge">{len(TOOLS)} free tools ยท no sign-up</span>
60
+ <h1>Everyday tools, <span class="accent">one place.</span></h1>
61
+ <p>Edit images, convert documents, check websites and more โ€” right in your browser.</p>
62
+ </section>
63
+ """,
64
+ unsafe_allow_html=True,
65
+ )
66
+
67
+ query = st.text_input("Search tools", placeholder="๐Ÿ”Ž Search tools โ€” e.g. pdf, qr, background", label_visibility="collapsed").strip().lower()
68
+ if query:
69
+ matches = [t for t in TOOLS if query in f"{t.name} {t.description} {t.category}".lower()]
70
+ if not matches:
71
+ st.info(f"No tools match โ€œ{query}โ€.")
72
+ return
73
+ sections = {"Search results": matches}
74
+ else:
75
+ sections = {category: tools_in(category) for category in CATEGORIES}
76
+
77
+ for title, tools in sections.items():
78
+ subtitle = CATEGORIES.get(title, f"{len(tools)} match{'es' if len(tools) != 1 else ''}")
79
+ st.markdown(f"<div class='category-heading'><div class='category-title'>{title}</div><span>{subtitle}</span></div>", unsafe_allow_html=True)
80
+ # Build row by row so cards keep their order when columns stack on mobile.
81
+ for row in range(0, len(tools), 4):
82
+ for col, tool in zip(st.columns(4), tools[row:row + 4]):
83
+ with col:
84
+ tool_card(tool)
85
+
86
+
87
+ def render_tool(tool: Tool) -> None:
88
+ st.button("โ† All tools", on_click=open_tool, key="back_home", type="tertiary")
89
+ tool_header(tool.icon, tool.name, tool.description)
90
+ try:
91
+ tool.load()()
92
+ except Exception as exc: # keep one broken tool from blanking the whole page
93
+ st.error(f"Something went wrong in {tool.name}: {exc}")
94
+ with st.expander("Technical details"):
95
+ st.code(traceback.format_exc())
96
+
97
+
98
+ def main() -> None:
99
+ slug = st.query_params.get("tool")
100
+ tool = TOOLS_BY_SLUG.get(slug) if slug else None
101
+ render_sidebar(tool)
102
+ if slug and not tool:
103
+ st.warning(f"Unknown tool โ€œ{slug}โ€. Showing all tools instead.")
104
+ if tool:
105
+ render_tool(tool)
106
+ else:
107
+ render_home()
108
+ footer()
109
+
110
+
111
+ main()
@@ -0,0 +1 @@
1
+ """Shared building blocks for the Mini Toolkit app."""
@@ -0,0 +1,56 @@
1
+ """HTTP helpers for the web tools.
2
+
3
+ All outbound requests go through :func:`fetch`, which normalises the URL and
4
+ refuses to reach private or loopback addresses, so a publicly hosted copy of the
5
+ app can't be used to probe the host's internal network.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import ipaddress
10
+ import socket
11
+ from urllib.parse import urlparse
12
+
13
+ import requests
14
+
15
+ USER_AGENT = "Mozilla/5.0 (compatible; MiniToolkit/2.0; +https://github.com/sarif-mia/mini-toolkit)"
16
+ DEFAULT_TIMEOUT = 10
17
+
18
+
19
+ class UnsafeURLError(ValueError):
20
+ pass
21
+
22
+
23
+ def normalize_url(url: str) -> str:
24
+ url = url.strip()
25
+ if url and not url.startswith(("http://", "https://")):
26
+ url = "https://" + url
27
+ return url
28
+
29
+
30
+ def ensure_public_url(url: str) -> str:
31
+ """Return the normalised URL, or raise if it is malformed or points at a private address."""
32
+ url = normalize_url(url)
33
+ host = urlparse(url).hostname
34
+ if not host:
35
+ raise UnsafeURLError("Please enter a valid URL, e.g. https://example.com")
36
+ try:
37
+ infos = socket.getaddrinfo(host, None)
38
+ except socket.gaierror:
39
+ raise UnsafeURLError(f"Could not resolve host '{host}'.")
40
+ for info in infos:
41
+ ip = ipaddress.ip_address(info[4][0])
42
+ if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved or ip.is_multicast:
43
+ raise UnsafeURLError(f"'{host}' resolves to a private address and can't be checked.")
44
+ return url
45
+
46
+
47
+ def fetch(url: str, timeout: float = DEFAULT_TIMEOUT, max_redirects: int = 5, **kwargs) -> requests.Response:
48
+ """GET a public URL, re-validating every redirect hop."""
49
+ headers = {"User-Agent": USER_AGENT, **kwargs.pop("headers", {})}
50
+ for _ in range(max_redirects + 1):
51
+ url = ensure_public_url(url)
52
+ response = requests.get(url, timeout=timeout, headers=headers, allow_redirects=False, **kwargs)
53
+ if not response.is_redirect:
54
+ return response
55
+ url = requests.compat.urljoin(url, response.headers["Location"])
56
+ raise requests.TooManyRedirects(f"Exceeded {max_redirects} redirects.")
@@ -0,0 +1,12 @@
1
+ """Helpers for generating PDFs with fpdf2's built-in fonts."""
2
+
3
+ # Built-in PDF fonts only cover Latin-1, so map common typographic characters
4
+ # to ASCII equivalents and replace anything else instead of crashing.
5
+ _TYPOGRAPHIC = str.maketrans({
6
+ "โ€“": "-", "โ€”": "-", "โ€˜": "'", "โ€™": "'",
7
+ "โ€œ": '"', "โ€": '"', "โ€ข": "*", "โ€ฆ": "...", "ย ": " ",
8
+ })
9
+
10
+
11
+ def pdf_safe(text: str) -> str:
12
+ return text.translate(_TYPOGRAPHIC).encode("latin-1", errors="replace").decode("latin-1")
@@ -0,0 +1,75 @@
1
+ """Central registry of every tool in the toolkit.
2
+
3
+ Each tool points at a module in ``tools/`` that exposes a ``render()`` function.
4
+ Modules are imported lazily, so a missing optional dependency only affects the
5
+ tool that needs it instead of taking down the whole app.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import importlib
10
+ from dataclasses import dataclass
11
+ from typing import Callable, Dict, List
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class Tool:
16
+ slug: str
17
+ name: str
18
+ icon: str
19
+ description: str
20
+ category: str
21
+ module: str
22
+
23
+ def load(self) -> Callable[[], None]:
24
+ return importlib.import_module(f"tools.{self.module}").render
25
+
26
+
27
+ IMAGE = "Image"
28
+ DOCUMENT = "Documents"
29
+ TEXT = "Text & Security"
30
+ WEB = "Web & Developer"
31
+
32
+ CATEGORIES: Dict[str, str] = {
33
+ IMAGE: "Edit, convert and analyse images.",
34
+ DOCUMENT: "Convert, merge and create documents.",
35
+ TEXT: "Transform text, hashes and passwords.",
36
+ WEB: "Inspect websites and automate small tasks.",
37
+ }
38
+
39
+ TOOLS: List[Tool] = [
40
+ # Image
41
+ Tool("background-remover", "Background Remover", "๐Ÿช„", "Remove image backgrounds with AI models.", IMAGE, "background_remover"),
42
+ Tool("image-converter", "Image Converter", "๐Ÿ”", "Convert images between PNG, JPG, WEBP, BMP and TIFF.", IMAGE, "image_converter"),
43
+ Tool("image-enhancer", "Image Enhancer", "โœจ", "Adjust brightness, contrast, colour and sharpness.", IMAGE, "image_enhancer"),
44
+ Tool("image-watermarker", "Image Watermarker", "๐Ÿ’ง", "Stamp a text watermark onto your images.", IMAGE, "image_watermarker"),
45
+ Tool("color-picker", "Image Color Picker", "๐ŸŽจ", "Click an image to get exact HEX and RGB values.", IMAGE, "color_picker"),
46
+ Tool("qr-code", "QR Code Generator", "๐Ÿ”ณ", "Create QR codes for links, contacts and Wi-Fi.", IMAGE, "qr_code"),
47
+ Tool("ocr", "OCR Text Extractor", "๐Ÿ“", "Extract text from images and screenshots.", IMAGE, "ocr"),
48
+ # Documents
49
+ Tool("document-converter", "Document Converter", "๐Ÿ“„", "Convert between DOCX, PDF and TXT.", DOCUMENT, "document_converter"),
50
+ Tool("pdf-tools", "PDF Merge & Split", "๐Ÿ“‘", "Combine PDFs or extract pages from one.", DOCUMENT, "pdf_tools"),
51
+ Tool("file-compressor", "File Compressor", "๐Ÿ—œ๏ธ", "Zip files or shrink image file sizes.", DOCUMENT, "file_compressor"),
52
+ Tool("resume-builder", "Resume Builder", "๐Ÿงพ", "Build a resume and cover letter as PDF or DOCX.", DOCUMENT, "resume_builder"),
53
+ Tool("email-signature", "Email Signature", "โœ’๏ธ", "Design an HTML email signature.", DOCUMENT, "signature"),
54
+ # Text & Security
55
+ Tool("text-utilities", "Text Utilities", "๐Ÿ”ค", "Count words, change case and clean up text.", TEXT, "text_utilities"),
56
+ Tool("text-to-speech", "Text to Speech", "๐Ÿ”Š", "Turn text into downloadable MP3 audio.", TEXT, "text_to_speech"),
57
+ Tool("hash-generator", "Hash Generator", "#๏ธโƒฃ", "Compute MD5 and SHA hashes of text or files.", TEXT, "hash_generator"),
58
+ Tool("password-checker", "Password Checker", "๐Ÿ›ก๏ธ", "Score password strength and get tips.", TEXT, "password_checker"),
59
+ # Web & Developer
60
+ Tool("website-status", "Website Status", "๐ŸŒ", "Check whether one or many sites are up.", WEB, "website_status"),
61
+ Tool("page-speed", "Page Speed Checker", "โšก", "Measure server response and download time.", WEB, "page_speed"),
62
+ Tool("ssl-checker", "SSL Certificate Checker", "๐Ÿ”’", "Inspect certificate validity and expiry.", WEB, "ssl_checker"),
63
+ Tool("seo-health", "SEO Health Check", "๐Ÿ”", "Audit titles, meta tags and headings.", WEB, "seo_health"),
64
+ Tool("image-downloader", "Website Image Downloader", "๐Ÿ–ผ๏ธ", "Collect every image on a page as a ZIP.", WEB, "image_downloader"),
65
+ Tool("url-shortener", "URL Shortener", "๐Ÿ”—", "Shorten long links with TinyURL.", WEB, "url_shortener"),
66
+ Tool("change-tracker", "Website Change Tracker", "๐Ÿ””", "Diff a page against its last snapshot.", WEB, "change_tracker"),
67
+ Tool("github-trending", "GitHub Trending", "๐Ÿ“ˆ", "Discover fast-rising GitHub repositories.", WEB, "github_trending"),
68
+ Tool("chat-alert", "Chat Alert Bot", "๐Ÿ’ฌ", "Send messages to Slack or Teams webhooks.", WEB, "chat_alert"),
69
+ ]
70
+
71
+ TOOLS_BY_SLUG: Dict[str, Tool] = {tool.slug: tool for tool in TOOLS}
72
+
73
+
74
+ def tools_in(category: str) -> List[Tool]:
75
+ return [tool for tool in TOOLS if tool.category == category]
@@ -0,0 +1,90 @@
1
+ """Reusable Streamlit UI helpers shared across tools."""
2
+ from __future__ import annotations
3
+
4
+ import html
5
+ import io
6
+ import zipfile
7
+ from pathlib import Path
8
+ from typing import Iterable, List, Optional, Tuple
9
+
10
+ import streamlit as st
11
+
12
+ MAX_UPLOAD_MB = 10
13
+ STYLE_PATH = Path(__file__).resolve().parent.parent / "style" / "style.css"
14
+ GITHUB_URL = "https://github.com/sarif-mia/mini-toolkit"
15
+
16
+
17
+ def load_css() -> None:
18
+ st.markdown(f"<style>{STYLE_PATH.read_text(encoding='utf-8')}</style>", unsafe_allow_html=True)
19
+
20
+
21
+ def tool_header(icon: str, title: str, description: str) -> None:
22
+ st.markdown(
23
+ f"""
24
+ <div class="tool-header">
25
+ <div class="tool-header-icon">{icon}</div>
26
+ <div>
27
+ <h1>{html.escape(title)}</h1>
28
+ <p>{html.escape(description)}</p>
29
+ </div>
30
+ </div>
31
+ """,
32
+ unsafe_allow_html=True,
33
+ )
34
+
35
+
36
+ def section(title: str) -> None:
37
+ st.markdown(f"<div class='section-title'>{html.escape(title)}</div>", unsafe_allow_html=True)
38
+
39
+
40
+ def footer() -> None:
41
+ st.markdown(
42
+ f"""
43
+ <footer class="app-footer">
44
+ <span>Mini Toolkit ยท built by <a href="https://sarifmiia.netlify.app/" target="_blank" rel="noopener">Sarif</a></span>
45
+ <span class="footer-links">
46
+ <a href="{GITHUB_URL}" target="_blank" rel="noopener">GitHub</a>
47
+ <a href="https://www.linkedin.com/in/sarif-mia/" target="_blank" rel="noopener">LinkedIn</a>
48
+ <a href="mailto:sarif@lovegenbd.com">Contact</a>
49
+ </span>
50
+ </footer>
51
+ """,
52
+ unsafe_allow_html=True,
53
+ )
54
+
55
+
56
+ def sanitize_filename(name: str) -> str:
57
+ stem = "".join(c if c.isalnum() or c in "-_" else "_" for c in Path(name).stem)
58
+ return stem or "file"
59
+
60
+
61
+ def image_uploader(key: str, types: Iterable[str] = ("png", "jpg", "jpeg", "webp", "bmp", "tiff")) -> List:
62
+ """Multi-image uploader that drops files above the size limit with a warning."""
63
+ files = st.file_uploader(
64
+ f"Upload images (max {MAX_UPLOAD_MB} MB each)",
65
+ type=list(types),
66
+ accept_multiple_files=True,
67
+ key=key,
68
+ ) or []
69
+ accepted = []
70
+ for f in files:
71
+ if f.size > MAX_UPLOAD_MB * 1024 ** 2:
72
+ st.warning(f"{f.name} is {f.size / 1024 ** 2:.1f} MB and was skipped (limit {MAX_UPLOAD_MB} MB).")
73
+ else:
74
+ accepted.append(f)
75
+ return accepted
76
+
77
+
78
+ def build_zip(entries: Iterable[Tuple[str, bytes]]) -> bytes:
79
+ buffer = io.BytesIO()
80
+ with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
81
+ for name, data in entries:
82
+ zf.writestr(name, data)
83
+ return buffer.getvalue()
84
+
85
+
86
+ def missing_dependency(package: str, extra: Optional[str] = None) -> None:
87
+ message = f"This tool needs the `{package}` package, which isn't installed on this server."
88
+ if extra:
89
+ message += f" {extra}"
90
+ st.error(message)
@@ -0,0 +1,3 @@
1
+ """Mini Toolkit โ€” everyday utilities in one Streamlit app."""
2
+
3
+ __version__ = "0.1.0"