@jjlmoya/utils-social 1.1.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.
Files changed (85) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +23 -0
  3. package/src/category/i18n/es.ts +23 -0
  4. package/src/category/i18n/fr.ts +23 -0
  5. package/src/category/index.ts +13 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +27 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/seo_length.test.ts +22 -0
  21. package/src/tests/tool_validation.test.ts +17 -0
  22. package/src/tool/redditFormatter/bibliography.astro +14 -0
  23. package/src/tool/redditFormatter/component.astro +246 -0
  24. package/src/tool/redditFormatter/i18n/en.ts +193 -0
  25. package/src/tool/redditFormatter/i18n/es.ts +193 -0
  26. package/src/tool/redditFormatter/i18n/fr.ts +193 -0
  27. package/src/tool/redditFormatter/index.ts +34 -0
  28. package/src/tool/redditFormatter/logic.ts +24 -0
  29. package/src/tool/redditFormatter/seo.astro +14 -0
  30. package/src/tool/redditFormatter/ui.ts +13 -0
  31. package/src/tool/safeZones/bibliography.astro +14 -0
  32. package/src/tool/safeZones/component.astro +857 -0
  33. package/src/tool/safeZones/i18n/en.ts +244 -0
  34. package/src/tool/safeZones/i18n/es.ts +244 -0
  35. package/src/tool/safeZones/i18n/fr.ts +244 -0
  36. package/src/tool/safeZones/index.ts +34 -0
  37. package/src/tool/safeZones/logic.ts +137 -0
  38. package/src/tool/safeZones/seo.astro +14 -0
  39. package/src/tool/safeZones/ui.ts +34 -0
  40. package/src/tool/socialImageResizer/bibliography.astro +14 -0
  41. package/src/tool/socialImageResizer/component.astro +498 -0
  42. package/src/tool/socialImageResizer/i18n/en.ts +211 -0
  43. package/src/tool/socialImageResizer/i18n/es.ts +211 -0
  44. package/src/tool/socialImageResizer/i18n/fr.ts +211 -0
  45. package/src/tool/socialImageResizer/index.ts +34 -0
  46. package/src/tool/socialImageResizer/logic.ts +133 -0
  47. package/src/tool/socialImageResizer/seo.astro +14 -0
  48. package/src/tool/socialImageResizer/ui.ts +7 -0
  49. package/src/tool/socialValueCalculator/bibliography.astro +14 -0
  50. package/src/tool/socialValueCalculator/component.astro +829 -0
  51. package/src/tool/socialValueCalculator/i18n/en.ts +310 -0
  52. package/src/tool/socialValueCalculator/i18n/es.ts +310 -0
  53. package/src/tool/socialValueCalculator/i18n/fr.ts +310 -0
  54. package/src/tool/socialValueCalculator/index.ts +34 -0
  55. package/src/tool/socialValueCalculator/logic.ts +60 -0
  56. package/src/tool/socialValueCalculator/seo.astro +14 -0
  57. package/src/tool/socialValueCalculator/ui.ts +31 -0
  58. package/src/tool/tinderPhotoOptimizer/bibliography.astro +14 -0
  59. package/src/tool/tinderPhotoOptimizer/component.astro +1514 -0
  60. package/src/tool/tinderPhotoOptimizer/i18n/en.ts +248 -0
  61. package/src/tool/tinderPhotoOptimizer/i18n/es.ts +248 -0
  62. package/src/tool/tinderPhotoOptimizer/i18n/fr.ts +248 -0
  63. package/src/tool/tinderPhotoOptimizer/index.ts +34 -0
  64. package/src/tool/tinderPhotoOptimizer/logic.ts +199 -0
  65. package/src/tool/tinderPhotoOptimizer/seo.astro +14 -0
  66. package/src/tool/tinderPhotoOptimizer/ui.ts +37 -0
  67. package/src/tool/youtubeThumbnail/bibliography.astro +14 -0
  68. package/src/tool/youtubeThumbnail/component.astro +419 -0
  69. package/src/tool/youtubeThumbnail/i18n/en.ts +207 -0
  70. package/src/tool/youtubeThumbnail/i18n/es.ts +207 -0
  71. package/src/tool/youtubeThumbnail/i18n/fr.ts +207 -0
  72. package/src/tool/youtubeThumbnail/index.ts +34 -0
  73. package/src/tool/youtubeThumbnail/logic.ts +7 -0
  74. package/src/tool/youtubeThumbnail/seo.astro +14 -0
  75. package/src/tool/youtubeThumbnail/ui.ts +13 -0
  76. package/src/tool/youtubeThumbnailPreviewer/bibliography.astro +14 -0
  77. package/src/tool/youtubeThumbnailPreviewer/component.astro +791 -0
  78. package/src/tool/youtubeThumbnailPreviewer/i18n/en.ts +321 -0
  79. package/src/tool/youtubeThumbnailPreviewer/i18n/es.ts +321 -0
  80. package/src/tool/youtubeThumbnailPreviewer/i18n/fr.ts +321 -0
  81. package/src/tool/youtubeThumbnailPreviewer/index.ts +34 -0
  82. package/src/tool/youtubeThumbnailPreviewer/seo.astro +14 -0
  83. package/src/tool/youtubeThumbnailPreviewer/ui.ts +19 -0
  84. package/src/tools.ts +10 -0
  85. package/src/types.ts +72 -0
@@ -0,0 +1,498 @@
1
+ ---
2
+ import type { KnownLocale } from '../../types';
3
+ import type { SocialImageResizerUI } from './ui';
4
+
5
+ interface Props {
6
+ locale?: KnownLocale;
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ const t = (ui ?? {}) as SocialImageResizerUI;
12
+
13
+ const presets = [
14
+ { id: 'ig-square', name: 'Instagram Square', size: '1080×1080', width: 1080, height: 1080, iconKey: 'instagram' },
15
+ { id: 'ig-portrait', name: 'Instagram Portrait', size: '1080×1350', width: 1080, height: 1350, iconKey: 'instagram' },
16
+ { id: 'ig-story', name: 'Stories / TikTok', size: '1080×1920', width: 1080, height: 1920, iconKey: 'phone' },
17
+ { id: 'yt-thumb', name: 'YouTube Thumb', size: '1280×720', width: 1280, height: 720, iconKey: 'youtube' },
18
+ { id: 'yt-banner', name: 'YouTube Banner', size: '2560×1440', width: 2560, height: 1440, iconKey: 'youtube' },
19
+ { id: 'x-post', name: 'X / Twitter Post', size: '1200×675', width: 1200, height: 675, iconKey: 'twitter' },
20
+ { id: 'x-header', name: 'X / Twitter Header', size: '1500×500', width: 1500, height: 500, iconKey: 'header' },
21
+ { id: 'li-header', name: 'LinkedIn Cover', size: '1584×396', width: 1584, height: 396, iconKey: 'linkedin' },
22
+ ];
23
+
24
+ const icons: Record<string, string> = {
25
+ responsive: `<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M4 6h18V4H4c-1.1 0-2 .9-2 2v11H0v3h14v-3H4V6zm19 2h-6c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 9h-4v-7h4v7z"/></svg>`,
26
+ instagram: `<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M7.8 2h8.4C19.4 2 22 4.6 22 7.8v8.4a5.8 5.8 0 0 1-5.8 5.8H7.8C4.6 22 2 19.4 2 16.2V7.8A5.8 5.8 0 0 1 7.8 2m-.2 2A3.6 3.6 0 0 0 4 7.6v8.8C4 18.39 5.61 20 7.6 20h8.8a3.6 3.6 0 0 0 3.6-3.6V7.6C20 5.61 18.39 4 16.4 4H7.6m9.65 1.5a1.25 1.25 0 0 1 1.25 1.25A1.25 1.25 0 0 1 17.25 8 1.25 1.25 0 0 1 16 6.75a1.25 1.25 0 0 1 1.25-1.25M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3z"/></svg>`,
27
+ phone: `<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M17 1H7C5.9 1 5 1.9 5 3v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 18H7V5h10v14zm-5 2c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></svg>`,
28
+ youtube: `<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M10 15l5.19-3L10 9v6m11.56-7.83c.13.47.22 1.1.28 1.9.07.8.1 1.49.1 2.09L22 12c0 2.19-.16 3.8-.44 4.83-.25.9-.83 1.48-1.73 1.73-.47.13-1.33.22-2.65.28-1.3.07-2.49.1-3.59.1L12 19c-4.19 0-6.8-.16-7.83-.44-.9-.25-1.48-.83-1.73-1.73-.13-.47-.22-1.1-.28-1.9-.07-.8-.1-1.49-.1-2.09L2 12c0-2.19.16-3.8.44-4.83.25-.9.83-1.48 1.73-1.73.47-.13 1.33-.22 2.65-.28 1.3-.07 2.49-.1 3.59-.1L12 5c4.19 0 6.8.16 7.83.44.9.25 1.48.83 1.73 1.73z"/></svg>`,
29
+ twitter: `<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.73-8.835L1.254 2.25H8.08l4.263 5.637zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>`,
30
+ header: `<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M2 5v4h20V5H2zm0 6v8h20v-8H2z"/></svg>`,
31
+ linkedin: `<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14m-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93h2.79M6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37h2.77z"/></svg>`,
32
+ upload: `<svg viewBox="0 0 24 24" fill="currentColor" width="40" height="40"><path d="M14 13v4h-4v-4H7l5-5 5 5h-3zm5 6H5v2h14v-2zM5 3h14v8h-2V5H7v6H5V3z"/></svg>`,
33
+ zoomOut: `<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 0 0 9.5 3 6.5 6.5 0 0 0 3 9.5 6.5 6.5 0 0 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7V9z"/></svg>`,
34
+ zoomIn: `<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 0 0 9.5 3 6.5 6.5 0 0 0 3 9.5 6.5 6.5 0 0 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM10 9H9v2H7v1h2v2h1v-2h2V11h-2V9z"/></svg>`,
35
+ refresh: `<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>`,
36
+ download: `<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M5 20h14v-2H5m14-9h-4V3H9v6H5l7 7 7-7z"/></svg>`,
37
+ };
38
+ ---
39
+
40
+ <div class="sri-container">
41
+ <aside class="sri-sidebar">
42
+ <div class="sri-sidebar-section">
43
+ <h3 class="sri-sidebar-title">
44
+ <span set:html={icons.responsive} />
45
+ {t.sidebarTitle}
46
+ </h3>
47
+ <div class="sri-preset-grid">
48
+ {presets.map((p, index) => (
49
+ <button
50
+ class={`sri-preset-btn${index === 0 ? ' active' : ''}`}
51
+ data-preset={JSON.stringify({ id: p.id, name: p.name, width: p.width, height: p.height, platform: p.iconKey })}
52
+ id={`sri-btn-${p.id}`}
53
+ >
54
+ <div class="sri-preset-icon">
55
+ <span set:html={icons[p.iconKey]} />
56
+ </div>
57
+ <div class="sri-preset-info">
58
+ <span class="sri-preset-name">{p.name}</span>
59
+ <span class="sri-preset-size">{p.size}</span>
60
+ </div>
61
+ </button>
62
+ ))}
63
+ </div>
64
+ </div>
65
+ </aside>
66
+
67
+ <main class="sri-main">
68
+ <div class="sri-upload-zone" id="sri-dropzone">
69
+ <div class="sri-upload-icon">
70
+ <span set:html={icons.upload} />
71
+ </div>
72
+ <div class="sri-upload-text">
73
+ <h2>{t.uploadTitle}</h2>
74
+ <p>{t.uploadSubtitle}</p>
75
+ </div>
76
+ <input type="file" id="sri-file-input" accept="image/*" hidden />
77
+ </div>
78
+
79
+ <div class="sri-editor-view" id="sri-editor-view">
80
+ <div class="sri-canvas-container" id="sri-canvas-container">
81
+ <canvas id="sri-canvas"></canvas>
82
+ </div>
83
+
84
+ <div class="sri-editor-actions">
85
+ <div class="sri-zoom-control">
86
+ <span set:html={icons.zoomOut} />
87
+ <input type="range" id="sri-zoom-range" min="10" max="300" value="100" />
88
+ <span set:html={icons.zoomIn} />
89
+ </div>
90
+
91
+ <div class="sri-btn-group">
92
+ <button class="sri-action-btn" id="sri-reset-btn">
93
+ <span set:html={icons.refresh} />
94
+ {t.btnReset}
95
+ </button>
96
+ <button class="sri-action-btn sri-primary" id="sri-download-btn">
97
+ <span set:html={icons.download} />
98
+ {t.btnDownload}
99
+ </button>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </main>
104
+ </div>
105
+
106
+ <script>
107
+ import { SocialResizer } from './logic';
108
+
109
+ let resizer: SocialResizer | null = null;
110
+ let dropzoneEl: HTMLElement | null = null;
111
+ let editorViewEl: HTMLElement | null = null;
112
+
113
+ async function handleFile(file: File) {
114
+ if (!file.type.startsWith('image/') || !resizer || !dropzoneEl) return;
115
+ await resizer.loadImage(file);
116
+ dropzoneEl.style.display = 'none';
117
+ editorViewEl?.classList.add('active');
118
+ }
119
+
120
+ function setupDropzone(dropzone: HTMLElement, fileInput: HTMLInputElement) {
121
+ dropzone.addEventListener('click', () => fileInput.click());
122
+ fileInput.addEventListener('change', async (e: Event) => {
123
+ const file = (e.target as HTMLInputElement).files?.[0];
124
+ if (file) await handleFile(file);
125
+ });
126
+ dropzone.addEventListener('dragover', (e) => {
127
+ e.preventDefault();
128
+ dropzone.classList.add('sri-hover');
129
+ });
130
+ dropzone.addEventListener('dragleave', () => dropzone.classList.remove('sri-hover'));
131
+ dropzone.addEventListener('drop', async (e: DragEvent) => {
132
+ e.preventDefault();
133
+ const file = e.dataTransfer?.files[0];
134
+ if (file) await handleFile(file);
135
+ });
136
+ }
137
+
138
+ function setupPresets(presetBtns: NodeListOf<Element>, zoomRange: HTMLInputElement | null) {
139
+ presetBtns.forEach((btn) => {
140
+ btn.addEventListener('click', () => {
141
+ const preset = JSON.parse(btn.getAttribute('data-preset')!);
142
+ presetBtns.forEach((b) => b.classList.remove('active'));
143
+ btn.classList.add('active');
144
+ resizer?.setPreset(preset);
145
+ if (zoomRange) zoomRange.value = '100';
146
+ });
147
+ });
148
+ }
149
+
150
+ function setupControls(zoomRange: HTMLInputElement | null, resetBtn: HTMLElement | null, downloadBtn: HTMLElement | null) {
151
+ zoomRange?.addEventListener('input', () => {
152
+ resizer?.setScale(parseInt(zoomRange.value) / 100);
153
+ });
154
+ resetBtn?.addEventListener('click', () => {
155
+ const activeBtn = document.querySelector('.sri-preset-btn.active');
156
+ if (activeBtn) {
157
+ resizer?.setPreset(JSON.parse(activeBtn.getAttribute('data-preset')!));
158
+ if (zoomRange) zoomRange.value = '100';
159
+ }
160
+ });
161
+ downloadBtn?.addEventListener('click', () => resizer?.download());
162
+ }
163
+
164
+ function init() {
165
+ const dropzone = document.getElementById('sri-dropzone');
166
+ const fileInput = document.getElementById('sri-file-input') as HTMLInputElement | null;
167
+ if (!dropzone || !fileInput) return;
168
+ dropzoneEl = dropzone;
169
+ editorViewEl = document.getElementById('sri-editor-view');
170
+ const presetBtns = document.querySelectorAll('.sri-preset-btn');
171
+ const zoomRange = document.getElementById('sri-zoom-range') as HTMLInputElement | null;
172
+ resizer = new SocialResizer('sri-canvas');
173
+ resizer.zoomRangeEl = zoomRange;
174
+ setupDropzone(dropzone, fileInput);
175
+ setupPresets(presetBtns, zoomRange);
176
+ setupControls(zoomRange, document.getElementById('sri-reset-btn'), document.getElementById('sri-download-btn'));
177
+ resizer.setPreset(JSON.parse(presetBtns[0].getAttribute('data-preset')!));
178
+ }
179
+
180
+ document.addEventListener('astro:page-load', init);
181
+ init();
182
+ </script>
183
+
184
+ <style>
185
+ .sri-container {
186
+ --bg: #fff;
187
+ --bg-sidebar: #f8fafc;
188
+ --bg-main: #fff;
189
+ --bg-upload: #f8fafc;
190
+ --bg-upload-icon: #fff;
191
+ --bg-canvas: #f1f5f9;
192
+ --bg-btn: #fff;
193
+ --bg-btn-hover: #f8fafc;
194
+ --bg-zoom: #fff;
195
+ --bg-preset-icon: #f1f5f9;
196
+ --bg-preset-hover: #f5f3ff;
197
+ --border: #e2e8f0;
198
+ --text: #1e293b;
199
+ --text-sidebar-title: #64748b;
200
+ --text-upload-sub: #64748b;
201
+ --text-zoom: #64748b;
202
+ --text-action: #475569;
203
+ --text-preset-name: #1e293b;
204
+ --text-preset-size: #64748b;
205
+
206
+ width: 100%;
207
+ max-width: 1200px;
208
+ margin: 0 auto;
209
+ background: var(--bg);
210
+ border-radius: 32px;
211
+ box-shadow: 0 30px 60px rgba(0, 0, 0, 0.08);
212
+ display: grid;
213
+ grid-template-columns: 320px 1fr;
214
+ overflow: hidden;
215
+ color: var(--text);
216
+ border: 1px solid var(--border);
217
+ min-height: 700px;
218
+ }
219
+
220
+ .sri-sidebar {
221
+ background: var(--bg-sidebar);
222
+ border-right: 1px solid var(--border);
223
+ padding: 2rem;
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 2rem;
227
+ overflow-y: auto;
228
+ }
229
+
230
+ .sri-sidebar-title {
231
+ font-size: 0.85rem;
232
+ font-weight: 800;
233
+ text-transform: uppercase;
234
+ letter-spacing: 0.1em;
235
+ color: var(--text-sidebar-title);
236
+ margin-bottom: 1.25rem;
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 0.5rem;
240
+ }
241
+
242
+ .sri-preset-grid {
243
+ display: flex;
244
+ flex-direction: column;
245
+ gap: 0.75rem;
246
+ }
247
+
248
+ .sri-preset-btn {
249
+ width: 100%;
250
+ padding: 1rem;
251
+ background: var(--bg-btn);
252
+ border: 1px solid var(--border);
253
+ border-radius: 16px;
254
+ display: flex;
255
+ align-items: center;
256
+ gap: 1rem;
257
+ cursor: pointer;
258
+ transition: all 0.2s ease;
259
+ text-align: left;
260
+ }
261
+
262
+ .sri-preset-btn:hover {
263
+ border-color: #8b5cf6;
264
+ background: var(--bg-preset-hover);
265
+ transform: translateX(4px);
266
+ }
267
+
268
+ .sri-preset-btn.active {
269
+ background: #8b5cf6;
270
+ border-color: #7c3aed;
271
+ color: #fff;
272
+ box-shadow: 0 10px 20px rgba(139, 92, 246, 0.2);
273
+ }
274
+
275
+ .sri-preset-icon {
276
+ width: 40px;
277
+ height: 40px;
278
+ background: var(--bg-preset-icon);
279
+ border-radius: 10px;
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ color: var(--text-sidebar-title);
284
+ flex-shrink: 0;
285
+ transition: all 0.2s ease;
286
+ }
287
+
288
+ .sri-preset-btn.active .sri-preset-icon {
289
+ background: rgba(255, 255, 255, 0.2);
290
+ color: #fff;
291
+ }
292
+
293
+ .sri-preset-info {
294
+ display: flex;
295
+ flex-direction: column;
296
+ }
297
+
298
+ .sri-preset-name {
299
+ font-size: 0.95rem;
300
+ font-weight: 700;
301
+ color: var(--text-preset-name);
302
+ }
303
+
304
+ .sri-preset-size {
305
+ font-size: 0.75rem;
306
+ color: var(--text-preset-size);
307
+ }
308
+
309
+ .sri-main {
310
+ background: var(--bg-main);
311
+ padding: 3rem;
312
+ display: flex;
313
+ flex-direction: column;
314
+ gap: 2rem;
315
+ position: relative;
316
+ }
317
+
318
+ .sri-upload-zone {
319
+ flex: 1;
320
+ border: 3px dashed var(--border);
321
+ border-radius: 24px;
322
+ display: flex;
323
+ flex-direction: column;
324
+ align-items: center;
325
+ justify-content: center;
326
+ gap: 1.5rem;
327
+ cursor: pointer;
328
+ transition: all 0.3s ease;
329
+ background: var(--bg-upload);
330
+ min-height: 400px;
331
+ }
332
+
333
+ .sri-upload-zone:hover,
334
+ .sri-upload-zone.sri-hover {
335
+ border-color: #8b5cf6;
336
+ background: var(--bg-preset-hover);
337
+ }
338
+
339
+ .sri-upload-icon {
340
+ width: 80px;
341
+ height: 80px;
342
+ background: var(--bg-upload-icon);
343
+ border-radius: 50%;
344
+ display: flex;
345
+ align-items: center;
346
+ justify-content: center;
347
+ color: #8b5cf6;
348
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
349
+ }
350
+
351
+ .sri-upload-text {
352
+ text-align: center;
353
+ }
354
+
355
+ .sri-upload-text h2 {
356
+ font-size: 1.5rem;
357
+ font-weight: 900;
358
+ margin-bottom: 0.5rem;
359
+ }
360
+
361
+ .sri-upload-text p {
362
+ color: var(--text-upload-sub);
363
+ font-size: 0.95rem;
364
+ }
365
+
366
+ .sri-editor-view {
367
+ display: none;
368
+ flex-direction: column;
369
+ gap: 2rem;
370
+ height: 100%;
371
+ }
372
+
373
+ .sri-editor-view.active {
374
+ display: flex;
375
+ }
376
+
377
+ .sri-canvas-container {
378
+ flex: 1;
379
+ background: var(--bg-canvas);
380
+ border-radius: 24px;
381
+ overflow: hidden;
382
+ display: flex;
383
+ align-items: center;
384
+ justify-content: center;
385
+ min-height: 400px;
386
+ border: 1px solid var(--border);
387
+ }
388
+
389
+ .sri-canvas-container canvas {
390
+ max-width: 100%;
391
+ max-height: 100%;
392
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
393
+ }
394
+
395
+ .sri-editor-actions {
396
+ display: flex;
397
+ justify-content: space-between;
398
+ align-items: center;
399
+ padding-top: 1rem;
400
+ border-top: 1px solid var(--border);
401
+ }
402
+
403
+ .sri-btn-group {
404
+ display: flex;
405
+ gap: 1rem;
406
+ }
407
+
408
+ .sri-action-btn {
409
+ padding: 0.75rem 1.5rem;
410
+ border-radius: 12px;
411
+ font-weight: 700;
412
+ display: flex;
413
+ align-items: center;
414
+ gap: 0.5rem;
415
+ cursor: pointer;
416
+ transition: all 0.2s ease;
417
+ border: 1px solid var(--border);
418
+ background: var(--bg-btn);
419
+ color: var(--text-action);
420
+ font-size: 0.9rem;
421
+ }
422
+
423
+ .sri-action-btn:hover {
424
+ background: var(--bg-btn-hover);
425
+ border-color: var(--border);
426
+ }
427
+
428
+ .sri-action-btn.sri-primary {
429
+ background: #8b5cf6;
430
+ border-color: #7c3aed;
431
+ color: #fff;
432
+ }
433
+
434
+ .sri-action-btn.sri-primary:hover {
435
+ background: #7c3aed;
436
+ box-shadow: 0 10px 20px rgba(139, 92, 246, 0.2);
437
+ }
438
+
439
+ .sri-zoom-control {
440
+ display: flex;
441
+ align-items: center;
442
+ gap: 1rem;
443
+ background: var(--bg-zoom);
444
+ padding: 0.5rem 1rem;
445
+ border-radius: 100px;
446
+ border: 1px solid var(--border);
447
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
448
+ color: var(--text-zoom);
449
+ }
450
+
451
+ .sri-zoom-control input[type='range'] {
452
+ width: 150px;
453
+ accent-color: #8b5cf6;
454
+ }
455
+
456
+ :global(.theme-dark) .sri-container {
457
+ --bg: #1e1e21;
458
+ --bg-sidebar: #161618;
459
+ --bg-main: #1e1e21;
460
+ --bg-upload: #161618;
461
+ --bg-upload-icon: #1e1e21;
462
+ --bg-canvas: #111113;
463
+ --bg-btn: #1e1e21;
464
+ --bg-btn-hover: #2d2d30;
465
+ --bg-zoom: #1e1e21;
466
+ --bg-preset-icon: #161618;
467
+ --bg-preset-hover: #232326;
468
+ --border: #2d2d30;
469
+ --text: #f1f5f9;
470
+ --text-sidebar-title: #94a3b8;
471
+ --text-upload-sub: #94a3b8;
472
+ --text-zoom: #94a3b8;
473
+ --text-action: #f1f5f9;
474
+ --text-preset-name: #f1f5f9;
475
+ --text-preset-size: #94a3b8;
476
+ }
477
+
478
+ @media (max-width: 900px) {
479
+ .sri-container {
480
+ grid-template-columns: 1fr;
481
+ }
482
+
483
+ .sri-sidebar {
484
+ border-right: none;
485
+ border-bottom: 1px solid var(--border);
486
+ }
487
+
488
+ .sri-editor-actions {
489
+ flex-direction: column;
490
+ gap: 1rem;
491
+ align-items: stretch;
492
+ }
493
+
494
+ .sri-btn-group {
495
+ justify-content: center;
496
+ }
497
+ }
498
+ </style>