├── favicon.ico ├── requirements.txt ├── logo-dark.png ├── logo-light.png ├── asset ├── thumb14.jpg └── Screenshot_2024-05-28_21-33-01.jpg ├── .github └── workflows │ ├── pr-checker.yml │ ├── pr-merge.yml │ ├── detect-duplicate.yml │ ├── auto-label.yml │ ├── issue-close-open.yml │ ├── static.yml │ └── pr-raise.yml Latest ├── LICENSE ├── dl.html ├── script.js ├── generate_qr.py ├── README.md ├── style.css └── index.html /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanraox/share-bits-wirelessly/HEAD/favicon.ico -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pypng==0.20220715.0 2 | qrcode==7.4.2 3 | typing_extensions==4.9.0 4 | -------------------------------------------------------------------------------- /logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanraox/share-bits-wirelessly/HEAD/logo-dark.png -------------------------------------------------------------------------------- /logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanraox/share-bits-wirelessly/HEAD/logo-light.png -------------------------------------------------------------------------------- /asset/thumb14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanraox/share-bits-wirelessly/HEAD/asset/thumb14.jpg -------------------------------------------------------------------------------- /asset/Screenshot_2024-05-28_21-33-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanraox/share-bits-wirelessly/HEAD/asset/Screenshot_2024-05-28_21-33-01.jpg -------------------------------------------------------------------------------- /.github/workflows/pr-checker.yml: -------------------------------------------------------------------------------- 1 | name: PR Issue Checker 2 | # Created my @smog-root. 3 | on: 4 | pull_request: 5 | types: [opened, edited] 6 | 7 | jobs: 8 | check_pr_description: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | 15 | - name: Check PR Description 16 | id: check_pr_description 17 | run: | 18 | PR_DESCRIPTION="${{ github.event.pull_request.body }}" 19 | if [[ -z "$PR_DESCRIPTION" ]]; then 20 | echo "PR description is missing." 21 | exit 1 22 | fi 23 | 24 | if [[ ! "$PR_DESCRIPTION" =~ Fixes\ #[0-9]+ ]]; then 25 | echo "The PR description should include 'Fixes #' if not addressing any issue." 26 | echo "##[error]Fixes #NEW must be included in the description." 27 | exit 1 28 | fi 29 | 30 | echo "PR description is valid." 31 | 32 | - name: Output result 33 | run: echo "All checks passed." 34 | -------------------------------------------------------------------------------- /.github/workflows/pr-merge.yml: -------------------------------------------------------------------------------- 1 | name: Merge Thank You 2 | 3 | 4 | 5 | on: 6 | pull_request_target: 7 | types: [closed] # Trigger when a PR is closed 8 | 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | 13 | jobs: 14 | post_merge_message: 15 | if: github.event.pull_request.merged == true # Only run if the PR was merged 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Post thank you message 20 | uses: actions/github-script@v7 21 | with: 22 | github-token: ${{ secrets.GITHUB_TOKEN }} # Ensure token is used 23 | script: | 24 | const prNumber = context.payload.pull_request.number; 25 | const owner = context.repo.owner; 26 | const repo = context.repo.repo; 27 | 28 | // Post a thank you message upon PR merge 29 | await github.rest.issues.createComment({ 30 | owner: owner, 31 | repo: repo, 32 | issue_number: prNumber, 33 | body: `🎉🎉 Thank you for your contribution! Your PR #${prNumber} has been merged! 🎉🎉` 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Guy Dupont 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. 22 | -------------------------------------------------------------------------------- /dl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/detect-duplicate.yml: -------------------------------------------------------------------------------- 1 | name: Duplicate Issue Detector 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | 7 | jobs: 8 | detect-duplicate: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - name: Check out repository 13 | uses: actions/checkout@v2 14 | 15 | - name: Detect duplicate issues 16 | id: detect 17 | uses: actions-cool/issues-similarity-analysis@v1 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | owner: ${{ github.repository_owner }} 21 | repo: ${{ github.event.repository.name }} 22 | threshold: 0.7 # Similarity threshold; adjust as needed 23 | label: duplicate 24 | close: true 25 | 26 | - name: Comment on Duplicate Issue 27 | if: steps.detect.outputs.duplicate == 'true' 28 | run: | 29 | curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 30 | -H "Accept: application/vnd.github.v3+json" \ 31 | https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments \ 32 | -d '{"body": "This issue is a duplicate of #${{ steps.detect.outputs.original_issue_number }}. Please refer to the original issue for updates."}' 33 | -------------------------------------------------------------------------------- /.github/workflows/auto-label.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Auto Label Issue 3 | 4 | on: 5 | issues: 6 | types: [opened, reopened, edited] 7 | 8 | jobs: 9 | label_issue: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | - name: Label Issue 15 | uses: actions/github-script@v6 16 | with: 17 | github-token: ${{secrets.GITHUB_TOKEN}} 18 | script: | 19 | const issue = context.payload.issue; 20 | const issueBody = issue.body ? issue.body.toLowerCase() : ''; 21 | const issueTitle = issue.title.toLowerCase(); 22 | 23 | // Add gssoc label to all issues 24 | await github.rest.issues.addLabels({ 25 | owner: context.repo.owner, 26 | repo: context.repo.repo, 27 | issue_number: issue.number, 28 | labels: ['IWOC2025','good first issue'] 29 | }); 30 | const addLabel = async (label) => { 31 | await github.rest.issues.addLabels({ 32 | owner: context.repo.owner, 33 | repo: context.repo.repo, 34 | issue_number: issue.number, 35 | labels: [label] 36 | }); 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/issue-close-open.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | name: Issue Auto Comment 4 | on: 5 | issues: 6 | types: [opened, closed] 7 | 8 | jobs: 9 | comment: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | - name: Add a comment when an issue is opened 15 | if: github.event.action == 'opened' 16 | uses: actions-ecosystem/action-create-comment@v1 17 | with: 18 | github_token: ${{ secrets.GITHUB_TOKEN }} 19 | number: ${{ github.event.issue.number }} 20 | body: | 21 | 👋 Thanks for opening this issue! We appreciate your contribution. Please make sure you’ve provided all the necessary details and screenshots, and don't forget to follow our Guidelines and Code of Conduct. Happy coding! 🚀 22 | 23 | - name: Add a comment when an issue is closed 24 | if: github.event.action == 'closed' 25 | uses: actions-ecosystem/action-create-comment@v1 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | number: ${{ github.event.issue.number }} 29 | body: | 30 | ✅ This issue has been successfully closed. Thank you for your contribution and helping us improve the project! If you have any more ideas or run into other issues, feel free to open a new one. Happy coding! 🚀 31 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v5 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | # Upload entire repository 40 | path: '.' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@v4 44 | -------------------------------------------------------------------------------- /.github/workflows/pr-raise.yml Latest: -------------------------------------------------------------------------------- 1 | name: Auto Comment on PR 2 | 3 | 4 | on: 5 | pull_request_target: 6 | types: [opened] 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | comment: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | pull-requests: write 17 | steps: 18 | - name: Add Comment to Pull Request 19 | run: | 20 | COMMENT=$(cat < { 2 | anchor.addEventListener('click', function (e) { 3 | e.preventDefault(); 4 | const target = document.querySelector(this.getAttribute('href')); 5 | const offset = 80; // Navbar height 6 | const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - offset; 7 | 8 | window.scrollTo({ 9 | top: targetPosition, 10 | behavior: 'smooth' 11 | }); 12 | }); 13 | }); 14 | 15 | document.getElementById('encodeButton').addEventListener('click', function() { 16 | this.classList.add('loading'); 17 | setTimeout(() => this.classList.remove('loading'), 1500); 18 | }); 19 | 20 | document.querySelectorAll('.button').forEach(button => { 21 | button.addEventListener('click', function(e) { 22 | let ripple = document.createElement('div'); 23 | ripple.style.position = 'absolute'; 24 | ripple.style.background = 'rgba(255,255,255,0.4)'; 25 | ripple.style.transform = 'translate(-50%, -50%)'; 26 | ripple.style.pointerEvents = 'none'; 27 | ripple.style.borderRadius = '50%'; 28 | 29 | const rect = this.getBoundingClientRect(); 30 | const size = Math.max(rect.width, rect.height); 31 | 32 | ripple.style.width = ripple.style.height = `${size}px`; 33 | ripple.style.left = `${e.clientX - rect.left}px`; 34 | ripple.style.top = `${e.clientY - rect.top}px`; 35 | 36 | this.appendChild(ripple); 37 | setTimeout(() => ripple.remove(), 1000); 38 | }); 39 | }); -------------------------------------------------------------------------------- /generate_qr.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import base64 3 | import qrcode 4 | import urllib.parse 5 | import os 6 | 7 | # Change this if you want to use decoding file hosted elsewhere 8 | WEB_URL_PREFIX = "https://amanraox.github.io/share-bits-wirelessly/dl.html" 9 | 10 | def validate_file(input_filename): 11 | """Check if the input file exists and is a valid file.""" 12 | if not os.path.isfile(input_filename): 13 | raise FileNotFoundError(f"The file {input_filename} does not exist.") 14 | 15 | def encode_file(input_filename): 16 | """Encode the file to base64.""" 17 | with open(input_filename, "rb") as f: 18 | file_data = f.read() 19 | return base64.b64encode(file_data).decode('ascii') 20 | 21 | def generate_qr_code(url, output_filename): 22 | """Generate QR code and save it.""" 23 | qr_img = qrcode.make(url) 24 | qr_img.save(f"{output_filename}_qr.png") 25 | print(f"QR code saved as {output_filename}_qr.png") 26 | 27 | def main(): 28 | if len(sys.argv) > 1: 29 | input_filename = sys.argv[1] 30 | output_filename = input_filename if len(sys.argv) == 2 else sys.argv[2] 31 | else: 32 | print("Usage: python generate_qr.py inputfile [optional output filename]") 33 | exit(-1) 34 | 35 | try: 36 | # Validate input file existence 37 | validate_file(input_filename) 38 | 39 | # Encode file content to base64 40 | b64_file_data = encode_file(input_filename) 41 | 42 | # URL encode the base64 file data 43 | url_file_data = urllib.parse.quote_plus(b64_file_data) 44 | 45 | # Generate the full URL 46 | full_url = f"{WEB_URL_PREFIX}?f={output_filename}#{url_file_data}" 47 | print("Encoded URL:", full_url) 48 | 49 | # Generate QR code and save it 50 | generate_qr_code(full_url, output_filename) 51 | 52 | except FileNotFoundError as e: 53 | print(e) 54 | except Exception as e: 55 | print(f"An error occurred: {e}") 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # share bits wirelessly 2 | 3 | 4 | ![ss](/asset/Screenshot_2024-05-28_21-33-01.jpg) 5 | ## Try this💡 6 | ### Here is my simple solution for sharing really small files. 7 | 8 | Share small files from an offline source using only a QR code! 9 | 10 | ## Why? 11 | 12 | Imagine: You want to share a file with a friend nearby. You want to share from a source that does not have internet access - you just want to beam the file directly to your friend. This system lets you _embed_ your file into a QR code that loads as an actual file download on your friend's device. Try it out here if you're feeling brave! The image that is downloaded by scanning this QR code **is not hosted on any server, it only lives _inside_ the QR code**. 13 | 14 | ![QR code with file embedded](/asset/thumb14.jpg) 15 | 16 | ## How it works 17 | 18 | 1. **Generate a QR Code** 19 | - Embed a small file into a QR code. 20 | - use the [web app](https://amanraox.github.io/share-bits-wirelessly/) or the provided Python script. 21 | 22 | 2. **Share and Scan** 23 | - Show the generated QR code to the recipient. 24 | - They scan it using any standard QR code scanner capable of handling embedded data (e.g., a smartphone camera or QR reader app). 25 | - The file downloads directly to their device. 26 | 27 | ## Usage 28 | 29 | ### Option 1 : Web App 30 | 1. Visit the [web app](https://amanraox.github.io/share-bits-wirelessly/). 31 | 2. Upload a file and download the QR code. 32 | 3. Share the QR code with others. 33 | 34 | ### Option 2 : Python Script 35 | 1. Clone the repository. 36 | 2. Install dependencies: 37 | ```bash 38 | pip install -r requirements.txt 39 | ``` 40 | 3. Generate a QR Code: 41 | ```bash 42 | python3 generate_pr.py input_file.txt 43 | ``` 44 | - The QR code is saved as `input_file.txt.png` (or provide an alternate output name as the second argument). 45 | 46 | ## Clarification: QR System 47 | 48 | The process for generating a QR code is clear. However, for retrieving files, note the following: 49 | 50 | 1. Use any QR code scanner or a smartphone camera to scan the code. 51 | 2. Ensure the QR reader supports **embedded data decoding**, as the file resides entirely within the QR code. 52 | 3. Once scanned, the QR reader will automatically prompt the download of the embedded file. 53 | 54 | If additional steps are needed, or you face issues, feel free to raise a question or open an issue in the repository! 55 | 56 | ## CONTRIBUTING 57 | 58 | We welcome contributions to SHare Bits Wirelessly! To contribute: 59 | 60 | 1. Fork and Star the repository 🍴⭐ 61 | 2. Create a new branch 🌱 62 | ```bash 63 | git checkout -b feature/your-feature 64 | ``` 65 | 3. Make your changes ✨ 66 | ```bash 67 | git commit -m "Add your message" 68 | ``` 69 | 4. Push your changes 🚀 70 | 5. Create a pull request 🔄 71 | 72 | ## Contributors 73 | 74 | A heartfelt thank you to the following individuals for their valuable contributions to this project. Your support and dedication are greatly appreciated: 75 | 76 | 77 | 78 | 79 | 80 |
81 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .canvas-bg { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | z-index: -1; 6 | } 7 | 8 | :root { 9 | --primary-color: #6366f1; 10 | --secondary-color: #1e1e1e; 11 | --background-dark: #0a0a0a; 12 | --background-light: #ffffff; 13 | --text-dark: #ffffff; 14 | --text-light: #000000; 15 | --accent-glow: rgba(99, 102, 241, 0.2); 16 | --glass-bg-dark: rgba(255, 255, 255, 0.1); 17 | --glass-bg-light: rgba(0, 0, 0, 0.1); 18 | 19 | scrollbar-width: none; 20 | } 21 | 22 | [data-theme="dark"] { 23 | --background: var(--background-dark); 24 | --text: var(--text-dark); 25 | --glass-bg: var(--glass-bg-dark); 26 | } 27 | 28 | [data-theme="light"] { 29 | --background: var(--background-light); 30 | --text: var(--text-light); 31 | --glass-bg: var(--glass-bg-light); 32 | } 33 | 34 | body { 35 | font-family: 'Space Grotesk', sans-serif; 36 | margin: 0; 37 | padding: 0; 38 | background: var(--background); 39 | color: var(--text); 40 | min-height: 100vh; 41 | overflow-x: hidden; 42 | transition: background-color 0.3s, color 0.3s; 43 | } 44 | 45 | .theme-toggle { 46 | position: fixed; 47 | top: 20px; 48 | right: 20px; 49 | background: var(--primary-color); 50 | color: var(--text-dark); 51 | width: 50px; 52 | height: 50px; 53 | border-radius: 50%; 54 | border: none; 55 | cursor: pointer; 56 | font-size: 1.5rem; 57 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); 58 | z-index: 1000; 59 | transition: transform 0.3s ease; 60 | } 61 | 62 | .theme-toggle:hover { 63 | transform: scale(1.1); 64 | } 65 | 66 | .container { 67 | display: grid; 68 | grid-template-columns: 1fr 1fr; 69 | gap: 2rem; 70 | max-width: 1200px; 71 | margin: 4rem auto; 72 | padding: 3rem 2rem 2rem; 73 | perspective: 1000px; 74 | } 75 | 76 | header { 77 | grid-column: 1 / -1; 78 | text-align: center; 79 | transform-style: preserve-3d; 80 | animation: float 6s ease-in-out infinite; 81 | scroll-margin-top: 80px; 82 | position: relative; 83 | z-index: 1; 84 | } 85 | 86 | .info-text { 87 | max-width: 800px; 88 | margin: 2rem auto; 89 | line-height: 1.6; 90 | text-align: left; 91 | } 92 | 93 | .info-text a { 94 | color: var(--primary-color); 95 | text-decoration: none; 96 | } 97 | 98 | .info-text a:hover { 99 | text-decoration: underline; 100 | } 101 | 102 | .info-text ul { 103 | margin-top: 1rem; 104 | padding-left: 2rem; 105 | } 106 | 107 | .info-text li { 108 | margin-bottom: 0.5rem; 109 | } 110 | 111 | @keyframes float { 112 | 113 | 0%, 114 | 100% { 115 | transform: translateY(0px) rotateX(0deg); 116 | } 117 | 118 | 50% { 119 | transform: translateY(-10px) rotateX(5deg); 120 | } 121 | } 122 | 123 | h1 { 124 | font-size: 3.5rem; 125 | margin: 0; 126 | background: linear-gradient(135deg, var(--primary-color), #a855f7); 127 | -webkit-background-clip: text; 128 | -webkit-text-fill-color: transparent; 129 | text-shadow: 0 0 30px var(--accent-glow); 130 | } 131 | 132 | .card { 133 | display: flex; 134 | flex-direction: column; /* Ensures elements are stacked vertically */ 135 | gap: 2rem; /* Adds space between elements inside the card */ 136 | background: var(--glass-bg); 137 | backdrop-filter: blur(16px) saturate(180%); 138 | border-radius: 24px; 139 | padding: 2rem; 140 | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); 141 | border: 1px solid rgba(255, 255, 255, 0.125); 142 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); 143 | -webkit-backdrop-filter: blur(16px) saturate(180%); 144 | background-color: rgba(17, 25, 40, 0.25); 145 | } 146 | 147 | .card:hover { 148 | transform: translateY(-10px); 149 | box-shadow: 0 12px 48px rgba(99, 102, 241, 0.2); 150 | } 151 | 152 | .input-section { 153 | display: flex; 154 | flex-direction: column; 155 | gap: 1.5rem; 156 | } 157 | 158 | .file-input-wrapper { 159 | position: relative; 160 | height: 200px; 161 | border: 2px solid transparent; 162 | border-radius: 16px; 163 | display: flex; 164 | align-items: center; 165 | justify-content: center; 166 | overflow: hidden; 167 | background: linear-gradient(var(--background), var(--background)) padding-box, linear-gradient(135deg, #6366f1, #a855f7) border-box; 168 | transition: all 0.3s ease; 169 | } 170 | 171 | .file-input-wrapper.uploaded { 172 | animation: pulse 1.5s ease-in-out; 173 | } 174 | 175 | @keyframes pulse { 176 | 0% { transform: scale(1); } 177 | 50% { transform: scale(1.02); } 178 | 100% { transform: scale(1); } 179 | } 180 | 181 | .file-name-display { 182 | margin-top: 0.5rem; 183 | color: var(--primary-color); 184 | font-size: 0.9rem; 185 | font-weight: bold; 186 | text-align: center; 187 | } 188 | 189 | .file-input-wrapper:hover { 190 | border-color: #a855f7; 191 | } 192 | 193 | #fileInput { 194 | position: absolute; 195 | width: 100%; 196 | height: 100%; 197 | opacity: 0; 198 | cursor: pointer; 199 | } 200 | 201 | .upload-icon { 202 | font-size: 3rem; 203 | color: var(--primary-color); 204 | } 205 | 206 | .button { 207 | width: 100%; 208 | background: linear-gradient(135deg, var(--primary-color), #a855f7); 209 | color: white; 210 | padding: 1rem 2rem; 211 | border: none; 212 | border-radius: 12px; 213 | font-size: 1.1rem; 214 | font-weight: 600; 215 | cursor: pointer; 216 | transition: all 0.3s ease; 217 | position: relative; 218 | overflow: hidden; 219 | } 220 | 221 | .button:hover { 222 | transform: translateY(-2px); 223 | box-shadow: 0 8px 24px rgba(99, 102, 241, 0.3); 224 | } 225 | 226 | .button::before { 227 | content: ''; 228 | position: absolute; 229 | top: 0; 230 | left: -100%; 231 | width: 100%; 232 | height: 100%; 233 | background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); 234 | transition: 0.5s; 235 | } 236 | 237 | .button:hover::before { 238 | left: 100%; 239 | } 240 | 241 | #qrcode { 242 | display: flex; 243 | justify-content: center; 244 | align-items: center; 245 | min-height: 256px; 246 | animation: fadeIn 0.5s ease-out; 247 | } 248 | 249 | #qrcode canvas { 250 | border-radius: 16px; 251 | box-shadow: 0 0 30px rgba(99, 102, 241, 0.2); 252 | transition: all 0.3s ease; 253 | } 254 | 255 | #qrcode:hover canvas { 256 | transform: scale(1.02); 257 | box-shadow: 0 0 40px rgba(99, 102, 241, 0.3); 258 | } 259 | 260 | .particles { 261 | animation: particles-float 20s ease-in-out infinite; 262 | } 263 | 264 | @keyframes particles-float { 265 | 0%, 100% { transform: translateY(0); } 266 | 50% { transform: translateY(-20px); } 267 | } 268 | 269 | @keyframes shimmer { 270 | 0% { background-position: -200% 0; } 271 | 100% { background-position: 200% 0; } 272 | } 273 | 274 | .loading { 275 | background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); 276 | background-size: 200% 100%; 277 | animation: shimmer 1.5s infinite; 278 | } 279 | 280 | @keyframes fadeIn { 281 | from { 282 | opacity: 0; 283 | transform: scale(0.9); 284 | } 285 | 286 | to { 287 | opacity: 1; 288 | transform: scale(1); 289 | } 290 | } 291 | 292 | footer { 293 | grid-column: 1 / -1; 294 | text-align: center; 295 | padding: 2rem; 296 | background: var(--glass-bg); 297 | backdrop-filter: blur(12px); 298 | border-radius: 16px; 299 | margin-top: 2rem; 300 | 301 | } 302 | .footer-content{ 303 | max-width: 1200px; 304 | margin:0 auto; 305 | } 306 | .iwoc-text{ 307 | margin-top: 0.5rem; 308 | font-weight: 500; 309 | } 310 | .github{ 311 | margin-top: 15px; 312 | 313 | } 314 | 315 | .footer-content a { 316 | position: relative; 317 | transition: all 0.3s ease; 318 | } 319 | 320 | footer a { 321 | color: var(--primary-color); 322 | text-decoration: none; 323 | transition: color 0.3s ease; 324 | font-weight: 500; 325 | } 326 | 327 | .footer-card { 328 | display: flex; 329 | flex-direction:column; /* Ensures elements are stacked vertically */ 330 | gap: 2rem; /* Adds space between elements inside the card */ 331 | background: var(--glass-bg); 332 | backdrop-filter: blur(12px); 333 | border-radius: 24px; 334 | padding: 2rem; 335 | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); 336 | border: 1px solid rgba(255, 255, 255, 0.1); 337 | transition: transform 0.3s ease, box-shadow 0.3s ease; 338 | 339 | } 340 | 341 | .footer-card:hover { 342 | transform: translateY(-10px); 343 | box-shadow: 0 12px 48px rgba(99, 102, 241, 0.2); 344 | } 345 | 346 | footer a:hover { 347 | color: #a855f7; 348 | } 349 | 350 | .footer-content a::after { 351 | content: ''; 352 | position: absolute; 353 | bottom: -2px; 354 | left: 0; 355 | width: 0; 356 | height: 2px; 357 | background: var(--primary-color); 358 | transition: width 0.3s ease; 359 | } 360 | 361 | .footer-content a:hover::after { 362 | width: 100%; 363 | } 364 | 365 | @media (max-width: 768px) { 366 | h1 { 367 | font-size: 2.5rem; 368 | } 369 | 370 | .info-text { 371 | font-size: 0.9rem; 372 | } 373 | 374 | .container { 375 | grid-template-columns: 1fr; 376 | } 377 | } 378 | 379 | ::-webkit-scrollbar { 380 | width: 8px; 381 | } 382 | 383 | ::-webkit-scrollbar-track { 384 | background: var(--background); 385 | } 386 | 387 | ::-webkit-scrollbar-thumb { 388 | background: var(--primary-color); 389 | border-radius: 4px; 390 | } 391 | 392 | /* Navbar Styles */ 393 | .navbar { 394 | position: fixed; 395 | top: 0; 396 | left: 0; 397 | width: 100%; 398 | height: 80px; 399 | display: flex; 400 | justify-content: space-between; 401 | align-items: center; 402 | padding: 0.2rem 0rem; 403 | background: rgba(0, 0, 0, 0.5); 404 | backdrop-filter: blur(10px); 405 | z-index: 1000; 406 | } 407 | 408 | /* Logo Section */ 409 | .navbar-logo { 410 | display: flex; 411 | align-items: center; 412 | gap: 0.5rem; 413 | padding-left: 25px; 414 | } 415 | 416 | .navbar-logo img { 417 | width: 70px; 418 | height: 70px; 419 | border-radius: 50%; 420 | transition: all 0.3s ease; 421 | } 422 | 423 | .navbar-logo:hover img { 424 | transform: rotate(15deg) scale(1.1); 425 | filter: drop-shadow(0 0 8px rgba(99, 102, 241, 0.4)); 426 | } 427 | 428 | /* Theme Toggle Button */ 429 | 430 | .theme-toggle { 431 | background: var(--primary-color, #6366f1); 432 | color: #ffffff; 433 | width: 40px; 434 | height: 40px; 435 | border: none; 436 | border-radius: 50%; 437 | display: flex; 438 | align-items: center; 439 | justify-content: center; 440 | font-size: 1.2rem; 441 | cursor: pointer; 442 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); 443 | transition: transform 0.3s ease, background-color 0.3s ease; 444 | } 445 | 446 | .theme-toggle:hover { 447 | transform: scale(1.1); 448 | background-color: #4f46e5; 449 | } 450 | 451 | /* Responsive Adjustments */ 452 | @media (max-width: 768px) { 453 | .navbar { 454 | padding: 0.8rem 1.5rem; 455 | } 456 | 457 | .navbar-logo img { 458 | width: 43px; 459 | height: 43px; 460 | margin-left: -28px; 461 | } 462 | 463 | .theme-toggle { 464 | width: 35px; 465 | height: 35px; 466 | font-size: 1rem; 467 | margin-top: -8px; 468 | } 469 | } 470 | 471 | #scrollBtn { 472 | position: fixed; 473 | bottom: 20px; 474 | right: 20px; 475 | background-color: #1c85f5; 476 | color: white; 477 | border: none; 478 | border-radius: 50%; 479 | width: 50px; 480 | height: 50px; 481 | font-size: 24px; 482 | cursor: pointer; 483 | display: none; 484 | justify-content: center; 485 | align-items: center; 486 | box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); 487 | } 488 | #scrollBtn:hover { 489 | background-color: #0056b3; 490 | transform: scale(1.03); 491 | } 492 | dialog { 493 | border: none; 494 | border-radius: 10px; 495 | padding: 20px; 496 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 497 | text-align: center; 498 | } 499 | 500 | dialog::backdrop { 501 | background: rgba(0, 0, 0, 0.3); 502 | } 503 | 504 | #closeDialogBtn { 505 | background-color: #6366f1; 506 | color: white; 507 | border: none; 508 | padding: 8px 16px; 509 | border-radius: 5px; 510 | cursor: pointer; 511 | } 512 | 513 | #closeDialogBtn:hover { 514 | background-color: #3c3fef; 515 | } 516 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | ØR-Code Generator 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 33 | 34 | 35 | 36 | 37 |
38 |
39 |

QR Code File Transfer

40 |
41 |

42 | Use this tool to generate a QR code to transfer a small file (< 2kb). The file is encoded into the 43 | QR code itself and is not uploaded to any server. 44 |

The file's data never leaves your computer. (Though this site's server can technically 45 | see the 46 | file's name) 47 |

for testing purpose you can download any small file from here 49 |

To generate one of these QR codes without needing to load this page from the internet, 50 | you can 51 | either: 52 |

    53 |
  • save it to your computer (File -> Save As -> Webpage, Complete)
  • 54 |
  • use the Python script in the GitHub 56 | repository
  • 57 |
58 |

59 |
60 |
61 | 62 |
63 |
64 | 65 |
📁
66 |

Drop your file here or click to browse

67 |
68 |

69 | 70 |
71 | 72 |
73 |
74 | 75 |
76 | 77 |
78 | 91 |
92 |
93 | 94 | 264 | 277 | 278 |

279 | 280 |
281 | 282 | 283 | 284 | 285 | --------------------------------------------------------------------------------