favicon-hritik 1.0.0

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.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # favicon-hritik
2
+
3
+ A professional favicon generator that can be easily integrated into any web project. It converts any image (PNG, JPG, SVG) into a complete set of favicons for various platforms.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install favicon-hritik
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Simply add a container with a specific ID in your HTML, and initialize the generator in your JavaScript.
14
+
15
+ ### 1. HTML
16
+
17
+ ```html
18
+ <div id="FavoconHritik"></div>
19
+ ```
20
+
21
+ ### 2. JavaScript
22
+
23
+ ```javascript
24
+ import FaviconHritik from 'favicon-hritik';
25
+
26
+ const generator = new FaviconHritik('FavoconHritik');
27
+ generator.init();
28
+ ```
29
+
30
+ ## Features
31
+
32
+ - **Drag & Drop:** Easy image upload.
33
+ - **Auto-Resizing:** Generates 512px, 192px, 180px, 48px, 32px, and 16px icons.
34
+ - **ZIP Export:** Download the entire set along with a `site.webmanifest`.
35
+ - **Live Apply:** Test the 16x16 favicon on your current page instantly.
36
+ - **Code Snippet:** Get the HTML tags needed for your project's `<head>`.
37
+ - **Self-Contained:** Automatically injects its own styles and UI.
38
+
39
+ ## Requirements
40
+
41
+ - Uses [JSZip](https://www.npmjs.com/package/jszip) for ZIP generation (automatically handled if using a bundler).
42
+
43
+ ## License
44
+
45
+ MIT © Hritik
package/index.html ADDED
@@ -0,0 +1,124 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Favicon Generator | Professional Icon Set Creator</title>
7
+ <meta name="description" content="Generate high-quality favicon sets for your web projects instantly. Upload once, get all sizes.">
8
+ <link rel="stylesheet" href="styles.css">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
13
+ </head>
14
+ <body>
15
+ <div class="app-container">
16
+ <header>
17
+ <div class="logo">
18
+ <span class="icon">✨</span>
19
+ <h1>Favicon<span>Gen</span></h1>
20
+ </div>
21
+ <p class="subtitle">Convert any image into a complete favicon set instantly.</p>
22
+ </header>
23
+
24
+ <main>
25
+ <section class="upload-section" id="dropzone">
26
+ <input type="file" id="fileInput" accept="image/*" hidden>
27
+ <div class="upload-content">
28
+ <div class="upload-icon">
29
+ <svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
30
+ <path d="M12 16V8M12 8L9 11M12 8L15 11M20 12C20 16.4183 16.4183 20 12 20C7.58172 20 4 16.4183 4 12C4 7.58172 7.58172 4 12 4C14.2091 4 16.2091 4.89543 17.6569 6.34315" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
31
+ </svg>
32
+ </div>
33
+ <h2>Drag & drop your image</h2>
34
+ <p>PNG, JPG, or SVG. Square images work best.</p>
35
+ <button class="btn btn-primary" id="selectBtn">Choose File</button>
36
+ </div>
37
+ </section>
38
+
39
+ <section class="preview-section hidden" id="previewArea">
40
+ <div class="section-header">
41
+ <h2>Generated Favicons</h2>
42
+ <div class="actions">
43
+ <button class="btn btn-secondary" id="applyBtn">Apply to Project</button>
44
+ <button class="btn btn-primary" id="downloadAllBtn">Download All (ZIP)</button>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="preview-grid">
49
+ <div class="preview-card" data-size="512">
50
+ <div class="preview-img-container">
51
+ <img src="" alt="512x512" class="preview-img">
52
+ </div>
53
+ <div class="preview-info">
54
+ <span class="size">512 × 512 px</span>
55
+ <span class="label">Android Chrome</span>
56
+ </div>
57
+ </div>
58
+ <div class="preview-card" data-size="192">
59
+ <div class="preview-img-container">
60
+ <img src="" alt="192x192" class="preview-img">
61
+ </div>
62
+ <div class="preview-info">
63
+ <span class="size">192 × 192 px</span>
64
+ <span class="label">Android Chrome</span>
65
+ </div>
66
+ </div>
67
+ <div class="preview-card" data-size="180">
68
+ <div class="preview-img-container">
69
+ <img src="" alt="180x180" class="preview-img">
70
+ </div>
71
+ <div class="preview-info">
72
+ <span class="size">180 × 180 px</span>
73
+ <span class="label">Apple Touch</span>
74
+ </div>
75
+ </div>
76
+ <div class="preview-card" data-size="48">
77
+ <div class="preview-img-container">
78
+ <img src="" alt="48x48" class="preview-img">
79
+ </div>
80
+ <div class="preview-info">
81
+ <span class="size">48 × 48 px</span>
82
+ <span class="label">Browser Tab</span>
83
+ </div>
84
+ </div>
85
+ <div class="preview-card" data-size="32">
86
+ <div class="preview-img-container">
87
+ <img src="" alt="32x32" class="preview-img">
88
+ </div>
89
+ <div class="preview-info">
90
+ <span class="size">32 × 32 px</span>
91
+ <span class="label">Standard</span>
92
+ </div>
93
+ </div>
94
+ <div class="preview-card" data-size="16">
95
+ <div class="preview-img-container">
96
+ <img src="" alt="16x16" class="preview-img">
97
+ </div>
98
+ <div class="preview-info">
99
+ <span class="size">16 × 16 px</span>
100
+ <span class="label">Smallest</span>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="code-section">
106
+ <h3>Integration Code</h3>
107
+ <p>Add these tags to your <code>&lt;head&gt;</code>:</p>
108
+ <div class="code-block">
109
+ <pre id="codeSnippet"></pre>
110
+ <button class="copy-btn" id="copyBtn">Copy</button>
111
+ </div>
112
+ </div>
113
+ </section>
114
+ </main>
115
+
116
+ <footer>
117
+ <p>&copy; 2024 FaviconGen. Created for professional web workflows.</p>
118
+ </footer>
119
+ </div>
120
+
121
+ <canvas id="canvas" style="display: none;"></canvas>
122
+ <script src="script.js"></script>
123
+ </body>
124
+ </html>
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "favicon-hritik",
3
+ "version": "1.0.0",
4
+ "description": "A simple and powerful favicon generator that can be injected into any project.",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "favicon",
11
+ "generator",
12
+ "icon",
13
+ "web",
14
+ "tool"
15
+ ],
16
+ "author": "Hritik",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "jszip": "^3.10.1"
20
+ }
21
+ }
package/script.js ADDED
@@ -0,0 +1,196 @@
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const dropzone = document.getElementById('dropzone');
3
+ const fileInput = document.getElementById('fileInput');
4
+ const selectBtn = document.getElementById('selectBtn');
5
+ const previewArea = document.getElementById('previewArea');
6
+ const canvas = document.getElementById('canvas');
7
+ const ctx = canvas.getContext('2d');
8
+ const downloadAllBtn = document.getElementById('downloadAllBtn');
9
+ const applyBtn = document.getElementById('applyBtn');
10
+ const codeSnippet = document.getElementById('codeSnippet');
11
+ const copyBtn = document.getElementById('copyBtn');
12
+
13
+ const sizes = [
14
+ { size: 512, name: 'android-chrome-512x512.png' },
15
+ { size: 192, name: 'android-chrome-192x192.png' },
16
+ { size: 180, name: 'apple-touch-icon.png' },
17
+ { size: 48, name: 'favicon-48x48.png' },
18
+ { size: 32, name: 'favicon-32x32.png' },
19
+ { size: 16, name: 'favicon-16x16.png' }
20
+ ];
21
+
22
+ let generatedFiles = [];
23
+
24
+ // Drag and drop handlers
25
+ dropzone.addEventListener('dragover', (e) => {
26
+ e.preventDefault();
27
+ dropzone.classList.add('dragover');
28
+ });
29
+
30
+ dropzone.addEventListener('dragleave', () => {
31
+ dropzone.classList.remove('dragover');
32
+ });
33
+
34
+ dropzone.addEventListener('drop', (e) => {
35
+ e.preventDefault();
36
+ dropzone.classList.remove('dragover');
37
+ const file = e.dataTransfer.files[0];
38
+ if (file && file.type.startsWith('image/')) {
39
+ processImage(file);
40
+ }
41
+ });
42
+
43
+ selectBtn.addEventListener('click', (e) => {
44
+ e.stopPropagation();
45
+ fileInput.click();
46
+ });
47
+
48
+ dropzone.addEventListener('click', () => {
49
+ fileInput.click();
50
+ });
51
+
52
+ fileInput.addEventListener('change', (e) => {
53
+ const file = e.target.files[0];
54
+ if (file) {
55
+ processImage(file);
56
+ }
57
+ });
58
+
59
+ async function processImage(file) {
60
+ const reader = new FileReader();
61
+ reader.onload = (e) => {
62
+ const img = new Image();
63
+ img.onload = () => {
64
+ generateFavicons(img);
65
+ };
66
+ img.src = e.target.result;
67
+ };
68
+ reader.readAsDataURL(file);
69
+ }
70
+
71
+ async function generateFavicons(img) {
72
+ generatedFiles = [];
73
+ previewArea.classList.remove('hidden');
74
+
75
+ // Show loading state if needed here
76
+
77
+ for (const config of sizes) {
78
+ canvas.width = config.size;
79
+ canvas.height = config.size;
80
+
81
+ // Clear canvas
82
+ ctx.clearRect(0, 0, config.size, config.size);
83
+
84
+ // Draw image with aspect ratio containment or cropping to square
85
+ const size = config.size;
86
+ const imgAspect = img.width / img.height;
87
+ let drawWidth, drawHeight, offsetX, offsetY;
88
+
89
+ if (imgAspect > 1) {
90
+ drawHeight = size;
91
+ drawWidth = size * imgAspect;
92
+ offsetX = -(drawWidth - size) / 2;
93
+ offsetY = 0;
94
+ } else {
95
+ drawWidth = size;
96
+ drawHeight = size / imgAspect;
97
+ offsetX = 0;
98
+ offsetY = -(drawHeight - size) / 2;
99
+ }
100
+
101
+ ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
102
+
103
+ const dataUrl = canvas.toDataURL('image/png');
104
+ generatedFiles.push({ name: config.name, blob: dataUrl, size: config.size });
105
+
106
+ // Update UI preview
107
+ const card = document.querySelector(`.preview-card[data-size="${config.size}"]`);
108
+ if (card) {
109
+ const previewImg = card.querySelector('.preview-img');
110
+ previewImg.src = dataUrl;
111
+ }
112
+ }
113
+
114
+ updateCodeSnippet();
115
+
116
+ // Scroll to preview
117
+ previewArea.scrollIntoView({ behavior: 'smooth' });
118
+ }
119
+
120
+ function updateCodeSnippet() {
121
+ const snippet = `<!-- Favicon configuration -->
122
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
123
+ <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
124
+ <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
125
+ <link rel="manifest" href="/site.webmanifest">
126
+ <meta name="msapplication-TileColor" content="#6366f1">
127
+ <meta name="theme-color" content="#ffffff">`;
128
+
129
+ codeSnippet.textContent = snippet;
130
+ }
131
+
132
+ downloadAllBtn.addEventListener('click', async () => {
133
+ if (generatedFiles.length === 0) return;
134
+
135
+ const zip = new JSZip();
136
+
137
+ // Add images to ZIP
138
+ generatedFiles.forEach(file => {
139
+ const base64Data = file.blob.replace(/^data:image\/png;base64,/, "");
140
+ zip.file(file.name, base64Data, { base64: true });
141
+ });
142
+
143
+ // Add a basic manifest file
144
+ const manifest = {
145
+ "name": "FaviconGen Project",
146
+ "short_name": "FaviconGen",
147
+ "icons": [
148
+ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
149
+ { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
150
+ ],
151
+ "theme_color": "#ffffff",
152
+ "background_color": "#ffffff",
153
+ "display": "standalone"
154
+ };
155
+ zip.file("site.webmanifest", JSON.stringify(manifest, null, 2));
156
+
157
+ // Generate the ZIP and trigger download
158
+ const content = await zip.generateAsync({ type: "blob" });
159
+ const link = document.createElement('a');
160
+ link.href = URL.createObjectURL(content);
161
+ link.download = "favicons.zip";
162
+ link.click();
163
+ });
164
+
165
+ applyBtn.addEventListener('click', () => {
166
+ const favicon16 = generatedFiles.find(f => f.size === 16);
167
+ if (favicon16) {
168
+ let link = document.querySelector("link[rel~='icon']");
169
+ if (!link) {
170
+ link = document.createElement('link');
171
+ link.rel = 'icon';
172
+ document.head.appendChild(link);
173
+ }
174
+ link.href = favicon16.blob;
175
+
176
+ // Feedback
177
+ applyBtn.textContent = 'Applied! ✨';
178
+ applyBtn.classList.remove('btn-secondary');
179
+ applyBtn.classList.add('btn-primary');
180
+ setTimeout(() => {
181
+ applyBtn.textContent = 'Apply to Project';
182
+ applyBtn.classList.remove('btn-primary');
183
+ applyBtn.classList.add('btn-secondary');
184
+ }, 2000);
185
+ }
186
+ });
187
+
188
+ copyBtn.addEventListener('click', () => {
189
+ navigator.clipboard.writeText(codeSnippet.textContent).then(() => {
190
+ copyBtn.textContent = 'Copied!';
191
+ setTimeout(() => {
192
+ copyBtn.textContent = 'Copy';
193
+ }, 2000);
194
+ });
195
+ });
196
+ });
package/src/index.js ADDED
@@ -0,0 +1,237 @@
1
+ import JSZipModule from 'jszip';
2
+ import { styles } from './styles.js';
3
+ import { template } from './template.js';
4
+
5
+ // Handle JSZip being available as a module or globally
6
+ const JSZip = typeof JSZipModule === 'function' ? JSZipModule : (typeof window !== 'undefined' ? window.JSZip : null);
7
+
8
+ class FaviconHritik {
9
+ constructor(containerId = 'FavoconHritik') {
10
+ this.containerId = containerId;
11
+ this.container = document.getElementById(containerId);
12
+ this.sizes = [
13
+ { size: 512, name: 'android-chrome-512x512.png' },
14
+ { size: 192, name: 'android-chrome-192x192.png' },
15
+ { size: 180, name: 'apple-touch-icon.png' },
16
+ { size: 48, name: 'favicon-48x48.png' },
17
+ { size: 32, name: 'favicon-32x32.png' },
18
+ { size: 16, name: 'favicon-16x16.png' }
19
+ ];
20
+ this.generatedFiles = [];
21
+ }
22
+
23
+ init() {
24
+ if (!this.container) {
25
+ console.error(`Container with ID "${this.containerId}" not found.`);
26
+ return;
27
+ }
28
+
29
+ this.injectStyles();
30
+ this.render();
31
+ this.cacheElements();
32
+ this.setupEventListeners();
33
+ }
34
+
35
+ injectStyles() {
36
+ if (document.getElementById('favicon-hritik-styles')) return;
37
+ const styleTag = document.createElement('style');
38
+ styleTag.id = 'favicon-hritik-styles';
39
+ styleTag.textContent = styles;
40
+ document.head.appendChild(styleTag);
41
+
42
+ // Add Inter font
43
+ if (!document.querySelector('link[href*="fonts.googleapis.com/css2?family=Inter"]')) {
44
+ const fontLink = document.createElement('link');
45
+ fontLink.rel = 'stylesheet';
46
+ fontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap';
47
+ document.head.appendChild(fontLink);
48
+ }
49
+ }
50
+
51
+ render() {
52
+ this.container.innerHTML = template;
53
+ }
54
+
55
+ cacheElements() {
56
+ this.dropzone = this.container.querySelector('#dropzone');
57
+ this.fileInput = this.container.querySelector('#fileInput');
58
+ this.selectBtn = this.container.querySelector('#selectBtn');
59
+ this.previewArea = this.container.querySelector('#previewArea');
60
+ this.canvas = this.container.querySelector('#canvas');
61
+ this.ctx = this.canvas.getContext('2d');
62
+ this.downloadAllBtn = this.container.querySelector('#downloadAllBtn');
63
+ this.applyBtn = this.container.querySelector('#applyBtn');
64
+ this.codeSnippet = this.container.querySelector('#codeSnippet');
65
+ this.copyBtn = this.container.querySelector('#copyBtn');
66
+ }
67
+
68
+ setupEventListeners() {
69
+ // Drag and drop handlers
70
+ this.dropzone.addEventListener('dragover', (e) => {
71
+ e.preventDefault();
72
+ this.dropzone.classList.add('dragover');
73
+ });
74
+
75
+ this.dropzone.addEventListener('dragleave', () => {
76
+ this.dropzone.classList.remove('dragover');
77
+ });
78
+
79
+ this.dropzone.addEventListener('drop', (e) => {
80
+ e.preventDefault();
81
+ this.dropzone.classList.remove('dragover');
82
+ const file = e.dataTransfer.files[0];
83
+ if (file && file.type.startsWith('image/')) {
84
+ this.processImage(file);
85
+ }
86
+ });
87
+
88
+ this.selectBtn.addEventListener('click', (e) => {
89
+ e.stopPropagation();
90
+ this.fileInput.click();
91
+ });
92
+
93
+ this.dropzone.addEventListener('click', () => {
94
+ this.fileInput.click();
95
+ });
96
+
97
+ this.fileInput.addEventListener('change', (e) => {
98
+ const file = e.target.files[0];
99
+ if (file) {
100
+ this.processImage(file);
101
+ }
102
+ });
103
+
104
+ this.downloadAllBtn.addEventListener('click', () => this.downloadAll());
105
+ this.applyBtn.addEventListener('click', () => this.applyToProject());
106
+ this.copyBtn.addEventListener('click', () => this.copyCode());
107
+ }
108
+
109
+ async processImage(file) {
110
+ const reader = new FileReader();
111
+ reader.onload = (e) => {
112
+ const img = new Image();
113
+ img.onload = () => {
114
+ this.generateFavicons(img);
115
+ };
116
+ img.src = e.target.result;
117
+ };
118
+ reader.readAsDataURL(file);
119
+ }
120
+
121
+ async generateFavicons(img) {
122
+ this.generatedFiles = [];
123
+ this.previewArea.classList.remove('hidden');
124
+
125
+ for (const config of this.sizes) {
126
+ this.canvas.width = config.size;
127
+ this.canvas.height = config.size;
128
+
129
+ this.ctx.clearRect(0, 0, config.size, config.size);
130
+
131
+ const size = config.size;
132
+ const imgAspect = img.width / img.height;
133
+ let drawWidth, drawHeight, offsetX, offsetY;
134
+
135
+ if (imgAspect > 1) {
136
+ drawHeight = size;
137
+ drawWidth = size * imgAspect;
138
+ offsetX = -(drawWidth - size) / 2;
139
+ offsetY = 0;
140
+ } else {
141
+ drawWidth = size;
142
+ drawHeight = size / imgAspect;
143
+ offsetX = 0;
144
+ offsetY = -(drawHeight - size) / 2;
145
+ }
146
+
147
+ this.ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
148
+
149
+ const dataUrl = this.canvas.toDataURL('image/png');
150
+ this.generatedFiles.push({ name: config.name, blob: dataUrl, size: config.size });
151
+
152
+ const card = this.container.querySelector(`.preview-card[data-size="${config.size}"]`);
153
+ if (card) {
154
+ const previewImg = card.querySelector('.preview-img');
155
+ previewImg.src = dataUrl;
156
+ }
157
+ }
158
+
159
+ this.updateCodeSnippet();
160
+ this.previewArea.scrollIntoView({ behavior: 'smooth' });
161
+ }
162
+
163
+ updateCodeSnippet() {
164
+ const snippet = `<!-- Favicon configuration -->
165
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
166
+ <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
167
+ <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
168
+ <link rel="manifest" href="/site.webmanifest">
169
+ <meta name="msapplication-TileColor" content="#6366f1">
170
+ <meta name="theme-color" content="#ffffff">`;
171
+
172
+ this.codeSnippet.textContent = snippet;
173
+ }
174
+
175
+ async downloadAll() {
176
+ if (this.generatedFiles.length === 0) return;
177
+
178
+ const zip = new JSZip();
179
+
180
+ this.generatedFiles.forEach(file => {
181
+ const base64Data = file.blob.replace(/^data:image\/png;base64,/, "");
182
+ zip.file(file.name, base64Data, { base64: true });
183
+ });
184
+
185
+ const manifest = {
186
+ "name": "FaviconGen Project",
187
+ "short_name": "FaviconGen",
188
+ "icons": [
189
+ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
190
+ { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
191
+ ],
192
+ "theme_color": "#ffffff",
193
+ "background_color": "#ffffff",
194
+ "display": "standalone"
195
+ };
196
+ zip.file("site.webmanifest", JSON.stringify(manifest, null, 2));
197
+
198
+ const content = await zip.generateAsync({ type: "blob" });
199
+ const link = document.createElement('a');
200
+ link.href = URL.createObjectURL(content);
201
+ link.download = "favicons.zip";
202
+ link.click();
203
+ }
204
+
205
+ applyToProject() {
206
+ const favicon16 = this.generatedFiles.find(f => f.size === 16);
207
+ if (favicon16) {
208
+ let link = document.querySelector("link[rel~='icon']");
209
+ if (!link) {
210
+ link = document.createElement('link');
211
+ link.rel = 'icon';
212
+ document.head.appendChild(link);
213
+ }
214
+ link.href = favicon16.blob;
215
+
216
+ this.applyBtn.textContent = 'Applied! ✨';
217
+ this.applyBtn.classList.remove('btn-secondary');
218
+ this.applyBtn.classList.add('btn-primary');
219
+ setTimeout(() => {
220
+ this.applyBtn.textContent = 'Apply to Project';
221
+ this.applyBtn.classList.remove('btn-primary');
222
+ this.applyBtn.classList.add('btn-secondary');
223
+ }, 2000);
224
+ }
225
+ }
226
+
227
+ copyCode() {
228
+ navigator.clipboard.writeText(this.codeSnippet.textContent).then(() => {
229
+ this.copyBtn.textContent = 'Copied!';
230
+ setTimeout(() => {
231
+ this.copyBtn.textContent = 'Copy';
232
+ }, 2000);
233
+ });
234
+ }
235
+ }
236
+
237
+ export default FaviconHritik;
package/src/styles.js ADDED
@@ -0,0 +1,270 @@
1
+ export const styles = `
2
+ :root {
3
+ --primary: #6366f1;
4
+ --primary-hover: #4f46e5;
5
+ --bg-gradient: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
6
+ --card-bg: #ffffff;
7
+ --text-main: #1e293b;
8
+ --text-muted: #64748b;
9
+ --border: #e2e8f0;
10
+ --shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
11
+ --radius: 16px;
12
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
13
+ }
14
+
15
+ .favicon-hritik-app * {
16
+ margin: 0;
17
+ padding: 0;
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ .favicon-hritik-app {
22
+ font-family: 'Inter', sans-serif;
23
+ color: var(--text-main);
24
+ width: 100%;
25
+ }
26
+
27
+ .app-container {
28
+ max-width: 1000px;
29
+ width: 100%;
30
+ margin: 0 auto;
31
+ }
32
+
33
+ header {
34
+ text-align: center;
35
+ margin-bottom: 3rem;
36
+ }
37
+
38
+ .logo {
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ gap: 0.5rem;
43
+ font-size: 1.5rem;
44
+ font-weight: 800;
45
+ letter-spacing: -0.025em;
46
+ margin-bottom: 0.5rem;
47
+ }
48
+
49
+ .logo .icon {
50
+ font-size: 2rem;
51
+ }
52
+
53
+ .logo span {
54
+ color: var(--primary);
55
+ }
56
+
57
+ .subtitle {
58
+ color: var(--text-muted);
59
+ font-size: 1.1rem;
60
+ }
61
+
62
+ /* Upload Section */
63
+ .upload-section {
64
+ background: var(--card-bg);
65
+ border: 3px dashed var(--border);
66
+ border-radius: var(--radius);
67
+ padding: 4rem 2rem;
68
+ text-align: center;
69
+ cursor: pointer;
70
+ transition: var(--transition);
71
+ }
72
+
73
+ .upload-section:hover, .upload-section.dragover {
74
+ border-color: var(--primary);
75
+ background: rgba(99, 102, 241, 0.05);
76
+ }
77
+
78
+ .upload-icon {
79
+ color: var(--primary);
80
+ margin-bottom: 1.5rem;
81
+ }
82
+
83
+ .upload-content h2 {
84
+ font-size: 1.5rem;
85
+ margin-bottom: 0.5rem;
86
+ }
87
+
88
+ .upload-content p {
89
+ color: var(--text-muted);
90
+ margin-bottom: 2rem;
91
+ }
92
+
93
+ /* Buttons */
94
+ .btn {
95
+ padding: 0.75rem 1.5rem;
96
+ border-radius: 12px;
97
+ font-weight: 600;
98
+ font-size: 1rem;
99
+ cursor: pointer;
100
+ transition: var(--transition);
101
+ border: none;
102
+ display: inline-flex;
103
+ align-items: center;
104
+ gap: 0.5rem;
105
+ }
106
+
107
+ .btn-primary {
108
+ background: var(--primary);
109
+ color: white;
110
+ }
111
+
112
+ .btn-primary:hover {
113
+ background: var(--primary-hover);
114
+ transform: translateY(-2px);
115
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
116
+ }
117
+
118
+ .btn-secondary {
119
+ background: white;
120
+ color: var(--text-main);
121
+ border: 1px solid var(--border);
122
+ }
123
+
124
+ .btn-secondary:hover {
125
+ background: #f8fafc;
126
+ transform: translateY(-2px);
127
+ }
128
+
129
+ /* Preview Section */
130
+ .preview-section {
131
+ margin-top: 3rem;
132
+ }
133
+
134
+ .section-header {
135
+ display: flex;
136
+ justify-content: space-between;
137
+ align-items: center;
138
+ margin-bottom: 2rem;
139
+ }
140
+
141
+ .preview-grid {
142
+ display: grid;
143
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
144
+ gap: 1.5rem;
145
+ margin-bottom: 3rem;
146
+ }
147
+
148
+ .preview-card {
149
+ background: var(--card-bg);
150
+ border-radius: var(--radius);
151
+ padding: 1.5rem;
152
+ box-shadow: var(--shadow);
153
+ display: flex;
154
+ flex-direction: column;
155
+ align-items: center;
156
+ transition: var(--transition);
157
+ }
158
+
159
+ .preview-card:hover {
160
+ transform: translateY(-5px);
161
+ }
162
+
163
+ .preview-img-container {
164
+ width: 100px;
165
+ height: 100px;
166
+ display: flex;
167
+ align-items: center;
168
+ justify-content: center;
169
+ background: #f1f5f9;
170
+ border-radius: 12px;
171
+ margin-bottom: 1rem;
172
+ overflow: hidden;
173
+ }
174
+
175
+ .preview-img {
176
+ max-width: 100%;
177
+ max-height: 100%;
178
+ object-fit: contain;
179
+ }
180
+
181
+ .preview-info {
182
+ text-align: center;
183
+ }
184
+
185
+ .preview-info .size {
186
+ display: block;
187
+ font-weight: 700;
188
+ font-size: 0.9rem;
189
+ }
190
+
191
+ .preview-info .label {
192
+ font-size: 0.8rem;
193
+ color: var(--text-muted);
194
+ }
195
+
196
+ /* Code Section */
197
+ .code-section {
198
+ background: #1e293b;
199
+ color: #e2e8f0;
200
+ border-radius: var(--radius);
201
+ padding: 2rem;
202
+ }
203
+
204
+ .code-section h3 {
205
+ margin-bottom: 0.5rem;
206
+ }
207
+
208
+ .code-section p {
209
+ color: #94a3b8;
210
+ margin-bottom: 1.5rem;
211
+ }
212
+
213
+ .code-block {
214
+ position: relative;
215
+ background: #0f172a;
216
+ padding: 1.5rem;
217
+ border-radius: 8px;
218
+ font-family: 'Monaco', 'Consolas', monospace;
219
+ font-size: 0.85rem;
220
+ line-height: 1.6;
221
+ overflow-x: auto;
222
+ }
223
+
224
+ .copy-btn {
225
+ position: absolute;
226
+ top: 0.75rem;
227
+ right: 0.75rem;
228
+ background: rgba(255, 255, 255, 0.1);
229
+ border: none;
230
+ color: white;
231
+ padding: 0.4rem 0.8rem;
232
+ border-radius: 6px;
233
+ cursor: pointer;
234
+ font-size: 0.75rem;
235
+ transition: var(--transition);
236
+ }
237
+
238
+ .copy-btn:hover {
239
+ background: rgba(255, 255, 255, 0.2);
240
+ }
241
+
242
+ /* Helpers */
243
+ .hidden {
244
+ display: none;
245
+ }
246
+
247
+ footer {
248
+ text-align: center;
249
+ margin-top: 4rem;
250
+ color: var(--text-muted);
251
+ font-size: 0.9rem;
252
+ }
253
+
254
+ @media (max-width: 640px) {
255
+ .section-header {
256
+ flex-direction: column;
257
+ gap: 1rem;
258
+ align-items: flex-start;
259
+ }
260
+ .actions {
261
+ display: flex;
262
+ width: 100%;
263
+ gap: 0.5rem;
264
+ }
265
+ .actions .btn {
266
+ flex: 1;
267
+ justify-content: center;
268
+ }
269
+ }
270
+ `;
@@ -0,0 +1,110 @@
1
+ export const template = `
2
+ <div class="favicon-hritik-app">
3
+ <div class="app-container">
4
+ <header>
5
+ <div class="logo">
6
+ <span class="icon">✨</span>
7
+ <h1>Favicon<span>Gen</span></h1>
8
+ </div>
9
+ <p class="subtitle">Convert any image into a complete favicon set instantly.</p>
10
+ </header>
11
+
12
+ <main>
13
+ <section class="upload-section" id="dropzone">
14
+ <input type="file" id="fileInput" accept="image/*" hidden>
15
+ <div class="upload-content">
16
+ <div class="upload-icon">
17
+ <svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
18
+ <path d="M12 16V8M12 8L9 11M12 8L15 11M20 12C20 16.4183 16.4183 20 12 20C7.58172 20 4 16.4183 4 12C4 7.58172 7.58172 4 12 4C14.2091 4 16.2091 4.89543 17.6569 6.34315" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
19
+ </svg>
20
+ </div>
21
+ <h2>Drag & drop your image</h2>
22
+ <p>PNG, JPG, or SVG. Square images work best.</p>
23
+ <button class="btn btn-primary" id="selectBtn">Choose File</button>
24
+ </div>
25
+ </section>
26
+
27
+ <section class="preview-section hidden" id="previewArea">
28
+ <div class="section-header">
29
+ <h2>Generated Favicons</h2>
30
+ <div class="actions">
31
+ <button class="btn btn-secondary" id="applyBtn">Apply to Project</button>
32
+ <button class="btn btn-primary" id="downloadAllBtn">Download All (ZIP)</button>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="preview-grid">
37
+ <div class="preview-card" data-size="512">
38
+ <div class="preview-img-container">
39
+ <img src="" alt="512x512" class="preview-img">
40
+ </div>
41
+ <div class="preview-info">
42
+ <span class="size">512 × 512 px</span>
43
+ <span class="label">Android Chrome</span>
44
+ </div>
45
+ </div>
46
+ <div class="preview-card" data-size="192">
47
+ <div class="preview-img-container">
48
+ <img src="" alt="192x192" class="preview-img">
49
+ </div>
50
+ <div class="preview-info">
51
+ <span class="size">192 × 192 px</span>
52
+ <span class="label">Android Chrome</span>
53
+ </div>
54
+ </div>
55
+ <div class="preview-card" data-size="180">
56
+ <div class="preview-img-container">
57
+ <img src="" alt="180x180" class="preview-img">
58
+ </div>
59
+ <div class="preview-info">
60
+ <span class="size">180 × 180 px</span>
61
+ <span class="label">Apple Touch</span>
62
+ </div>
63
+ </div>
64
+ <div class="preview-card" data-size="48">
65
+ <div class="preview-img-container">
66
+ <img src="" alt="48x48" class="preview-img">
67
+ </div>
68
+ <div class="preview-info">
69
+ <span class="size">48 × 48 px</span>
70
+ <span class="label">Browser Tab</span>
71
+ </div>
72
+ </div>
73
+ <div class="preview-card" data-size="32">
74
+ <div class="preview-img-container">
75
+ <img src="" alt="32x32" class="preview-img">
76
+ </div>
77
+ <div class="preview-info">
78
+ <span class="size">32 × 32 px</span>
79
+ <span class="label">Standard</span>
80
+ </div>
81
+ </div>
82
+ <div class="preview-card" data-size="16">
83
+ <div class="preview-img-container">
84
+ <img src="" alt="16x16" class="preview-img">
85
+ </div>
86
+ <div class="preview-info">
87
+ <span class="size">16 × 16 px</span>
88
+ <span class="label">Smallest</span>
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="code-section">
94
+ <h3>Integration Code</h3>
95
+ <p>Add these tags to your <code>&lt;head&gt;</code>:</p>
96
+ <div class="code-block">
97
+ <pre id="codeSnippet"></pre>
98
+ <button class="copy-btn" id="copyBtn">Copy</button>
99
+ </div>
100
+ </div>
101
+ </section>
102
+ </main>
103
+
104
+ <footer>
105
+ <p>&copy; 2024 FaviconGen. Created by Hritik.</p>
106
+ </footer>
107
+ </div>
108
+ <canvas id="canvas" style="display: none;"></canvas>
109
+ </div>
110
+ `;
package/styles.css ADDED
@@ -0,0 +1,289 @@
1
+ :root {
2
+ --primary: #6366f1;
3
+ --primary-hover: #4f46e5;
4
+ --bg-gradient: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
5
+ --card-bg: #ffffff;
6
+ --text-main: #1e293b;
7
+ --text-muted: #64748b;
8
+ --border: #e2e8f0;
9
+ --shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
10
+ --radius: 16px;
11
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
12
+ }
13
+
14
+ * {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ body {
21
+ font-family: 'Inter', sans-serif;
22
+ background: var(--bg-gradient);
23
+ color: var(--text-main);
24
+ min-height: 100vh;
25
+ display: flex;
26
+ justify-content: center;
27
+ padding: 2rem 1rem;
28
+ }
29
+
30
+ .app-container {
31
+ max-width: 1000px;
32
+ width: 100%;
33
+ }
34
+
35
+ header {
36
+ text-align: center;
37
+ margin-bottom: 3rem;
38
+ animation: fadeInDown 0.8s ease-out;
39
+ }
40
+
41
+ .logo {
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ gap: 0.5rem;
46
+ font-size: 1.5rem;
47
+ font-weight: 800;
48
+ letter-spacing: -0.025em;
49
+ margin-bottom: 0.5rem;
50
+ }
51
+
52
+ .logo .icon {
53
+ font-size: 2rem;
54
+ }
55
+
56
+ .logo span {
57
+ color: var(--primary);
58
+ }
59
+
60
+ .subtitle {
61
+ color: var(--text-muted);
62
+ font-size: 1.1rem;
63
+ }
64
+
65
+ /* Upload Section */
66
+ .upload-section {
67
+ background: var(--card-bg);
68
+ border: 3px dashed var(--border);
69
+ border-radius: var(--radius);
70
+ padding: 4rem 2rem;
71
+ text-align: center;
72
+ cursor: pointer;
73
+ transition: var(--transition);
74
+ animation: fadeInUp 0.8s ease-out;
75
+ }
76
+
77
+ .upload-section:hover, .upload-section.dragover {
78
+ border-color: var(--primary);
79
+ background: rgba(99, 102, 241, 0.05);
80
+ }
81
+
82
+ .upload-icon {
83
+ color: var(--primary);
84
+ margin-bottom: 1.5rem;
85
+ }
86
+
87
+ .upload-content h2 {
88
+ font-size: 1.5rem;
89
+ margin-bottom: 0.5rem;
90
+ }
91
+
92
+ .upload-content p {
93
+ color: var(--text-muted);
94
+ margin-bottom: 2rem;
95
+ }
96
+
97
+ /* Buttons */
98
+ .btn {
99
+ padding: 0.75rem 1.5rem;
100
+ border-radius: 12px;
101
+ font-weight: 600;
102
+ font-size: 1rem;
103
+ cursor: pointer;
104
+ transition: var(--transition);
105
+ border: none;
106
+ display: inline-flex;
107
+ align-items: center;
108
+ gap: 0.5rem;
109
+ }
110
+
111
+ .btn-primary {
112
+ background: var(--primary);
113
+ color: white;
114
+ }
115
+
116
+ .btn-primary:hover {
117
+ background: var(--primary-hover);
118
+ transform: translateY(-2px);
119
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
120
+ }
121
+
122
+ .btn-secondary {
123
+ background: white;
124
+ color: var(--text-main);
125
+ border: 1px solid var(--border);
126
+ }
127
+
128
+ .btn-secondary:hover {
129
+ background: #f8fafc;
130
+ transform: translateY(-2px);
131
+ }
132
+
133
+ /* Preview Section */
134
+ .preview-section {
135
+ margin-top: 3rem;
136
+ animation: fadeIn 0.8s ease-out;
137
+ }
138
+
139
+ .section-header {
140
+ display: flex;
141
+ justify-content: space-between;
142
+ align-items: center;
143
+ margin-bottom: 2rem;
144
+ }
145
+
146
+ .preview-grid {
147
+ display: grid;
148
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
149
+ gap: 1.5rem;
150
+ margin-bottom: 3rem;
151
+ }
152
+
153
+ .preview-card {
154
+ background: var(--card-bg);
155
+ border-radius: var(--radius);
156
+ padding: 1.5rem;
157
+ box-shadow: var(--shadow);
158
+ display: flex;
159
+ flex-direction: column;
160
+ align-items: center;
161
+ transition: var(--transition);
162
+ }
163
+
164
+ .preview-card:hover {
165
+ transform: translateY(-5px);
166
+ }
167
+
168
+ .preview-img-container {
169
+ width: 100px;
170
+ height: 100px;
171
+ display: flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ background: #f1f5f9;
175
+ border-radius: 12px;
176
+ margin-bottom: 1rem;
177
+ overflow: hidden;
178
+ }
179
+
180
+ .preview-img {
181
+ max-width: 100%;
182
+ max-height: 100%;
183
+ object-fit: contain;
184
+ }
185
+
186
+ .preview-info {
187
+ text-align: center;
188
+ }
189
+
190
+ .preview-info .size {
191
+ display: block;
192
+ font-weight: 700;
193
+ font-size: 0.9rem;
194
+ }
195
+
196
+ .preview-info .label {
197
+ font-size: 0.8rem;
198
+ color: var(--text-muted);
199
+ }
200
+
201
+ /* Code Section */
202
+ .code-section {
203
+ background: #1e293b;
204
+ color: #e2e8f0;
205
+ border-radius: var(--radius);
206
+ padding: 2rem;
207
+ }
208
+
209
+ .code-section h3 {
210
+ margin-bottom: 0.5rem;
211
+ }
212
+
213
+ .code-section p {
214
+ color: #94a3b8;
215
+ margin-bottom: 1.5rem;
216
+ }
217
+
218
+ .code-block {
219
+ position: relative;
220
+ background: #0f172a;
221
+ padding: 1.5rem;
222
+ border-radius: 8px;
223
+ font-family: 'Monaco', 'Consolas', monospace;
224
+ font-size: 0.85rem;
225
+ line-height: 1.6;
226
+ overflow-x: auto;
227
+ }
228
+
229
+ .copy-btn {
230
+ position: absolute;
231
+ top: 0.75rem;
232
+ right: 0.75rem;
233
+ background: rgba(255, 255, 255, 0.1);
234
+ border: none;
235
+ color: white;
236
+ padding: 0.4rem 0.8rem;
237
+ border-radius: 6px;
238
+ cursor: pointer;
239
+ font-size: 0.75rem;
240
+ transition: var(--transition);
241
+ }
242
+
243
+ .copy-btn:hover {
244
+ background: rgba(255, 255, 255, 0.2);
245
+ }
246
+
247
+ /* Helpers */
248
+ .hidden {
249
+ display: none;
250
+ }
251
+
252
+ footer {
253
+ text-align: center;
254
+ margin-top: 4rem;
255
+ color: var(--text-muted);
256
+ font-size: 0.9rem;
257
+ }
258
+
259
+ @keyframes fadeInDown {
260
+ from { opacity: 0; transform: translateY(-20px); }
261
+ to { opacity: 1; transform: translateY(0); }
262
+ }
263
+
264
+ @keyframes fadeInUp {
265
+ from { opacity: 0; transform: translateY(20px); }
266
+ to { opacity: 1; transform: translateY(0); }
267
+ }
268
+
269
+ @keyframes fadeIn {
270
+ from { opacity: 0; }
271
+ to { opacity: 1; }
272
+ }
273
+
274
+ @media (max-width: 640px) {
275
+ .section-header {
276
+ flex-direction: column;
277
+ gap: 1rem;
278
+ align-items: flex-start;
279
+ }
280
+ .actions {
281
+ display: flex;
282
+ width: 100%;
283
+ gap: 0.5rem;
284
+ }
285
+ .actions .btn {
286
+ flex: 1;
287
+ justify-content: center;
288
+ }
289
+ }
package/test.html ADDED
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Test Favicon Hritik Package</title>
7
+ <!-- We need JSZip globally for this test as we aren't using a bundler -->
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
9
+ <style>
10
+ body {
11
+ background-color: #f0f2f5;
12
+ padding: 50px;
13
+ }
14
+ .container {
15
+ max-width: 1100px;
16
+ margin: 0 auto;
17
+ background: white;
18
+ padding: 20px;
19
+ border-radius: 20px;
20
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
21
+ }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <div class="container">
26
+ <h1>Package Integration Test</h1>
27
+ <hr style="margin: 20px 0; border: 0; border-top: 1px solid #eee;">
28
+
29
+ <!-- The Target DIV -->
30
+ <div id="FavoconHritik"></div>
31
+ </div>
32
+
33
+ <script type="module">
34
+ // In a real NPM package, this would be: import FaviconHritik from 'favicon-hritik';
35
+ import FaviconHritik from './src/index.js';
36
+
37
+ // Initialize the generator
38
+ const generator = new FaviconHritik('FavoconHritik');
39
+ generator.init();
40
+ </script>
41
+ </body>
42
+ </html>