From da076701d3468ce20fe0bfbd2ad2c1b4122b029c Mon Sep 17 00:00:00 2001 From: neo Date: Fri, 19 Jun 2026 21:05:57 +0500 Subject: [PATCH] add favicons and meta tags for browser/bookmark support --- generate_favicons.py | 157 +++++++++++++++++++++++++++++++ index-v4.html | 8 ++ logos/android-chrome-192x192.png | Bin 0 -> 1120 bytes logos/android-chrome-512x512.png | Bin 0 -> 3108 bytes logos/apple-touch-icon.png | Bin 0 -> 1007 bytes logos/favicon-16x16.png | Bin 0 -> 183 bytes logos/favicon-32x32.png | Bin 0 -> 272 bytes logos/favicon.ico | Bin 0 -> 15086 bytes logos/site.webmanifest | 20 ++++ 9 files changed, 185 insertions(+) create mode 100644 generate_favicons.py create mode 100644 logos/android-chrome-192x192.png create mode 100644 logos/android-chrome-512x512.png create mode 100644 logos/apple-touch-icon.png create mode 100644 logos/favicon-16x16.png create mode 100644 logos/favicon-32x32.png create mode 100644 logos/favicon.ico create mode 100644 logos/site.webmanifest diff --git a/generate_favicons.py b/generate_favicons.py new file mode 100644 index 0000000..8ada5e7 --- /dev/null +++ b/generate_favicons.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +"""Generate favicons for calls24 landing page.""" + +from PIL import Image, ImageDraw, ImageFont +import os + +COLOR_ACCENT = "#22c55e" +COLOR_WHITE = "#ffffff" +COLOR_DARK = "#0a0a0a" +COLOR_GREEN_LIGHT = "#86efac" + +OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__)) +LOGOS_DIR = os.path.join(OUTPUT_DIR, "logos") + +def create_gradient_bg(img, size, color1=COLOR_DARK, color2="#111111"): + """Create dark gradient background.""" + for y in range(size): + ratio = y / size + r = int(color1[1:3], 16) + int((int(color2[1:3], 16) - int(color1[1:3], 16)) * ratio) + g = int(color1[3:5], 16) + int((int(color2[3:5], 16) - int(color1[3:5], 16)) * ratio) + b = int(color1[5:7], 16) + int((int(color2[5:7], 16) - int(color1[5:7], 16)) * ratio) + draw = ImageDraw.Draw(img) + draw.line([(0, y), (size, y)], fill=f"#{r:02x}{g:02x}{b:02x}") + +def draw_wave_icon(draw, cx, cy, size, color=COLOR_ACCENT, width=0.6): + """Draw the signature wave icon (bar chart style).""" + bar_w = size // 9 + gap = size // 12 + max_h = int(size * width) + heights = [0.35, 0.65, 1.0, 0.65, 0.35] # Wave shape + + total_w = len(heights) * bar_w + (len(heights) - 1) * gap + start_x = cx - total_w // 2 + + for i, h_ratio in enumerate(heights): + x = start_x + i * (bar_w + gap) + h = int(max_h * h_ratio) + y = cy - h // 2 + + # Semi-transparent bars + alpha = int(255 * (0.3 + 0.7 * h_ratio / 1.0)) + rgb = tuple(int(COLOR_ACCENT[i:i+2], 16) for i in (1, 3, 5)) + fill_color = f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}{alpha:02x}" + draw.rectangle([x, y, x + bar_w - 1, y + h], fill=fill_color) + +def draw_wave_with_dot(draw, cx, cy, size, color=COLOR_ACCENT): + """Draw wave icon with accent dot (center bar top).""" + bar_w = size // 9 + gap = size // 12 + max_h = int(size * 0.6) + heights = [0.35, 0.65, 1.0, 0.65, 0.35] + + total_w = len(heights) * bar_w + (len(heights) - 1) * gap + start_x = cx - total_w // 2 + + # Draw bars + for i, h_ratio in enumerate(heights): + x = start_x + i * (bar_w + gap) + h = int(max_h * h_ratio) + y = cy - h // 2 + + if i == 2: # Center bar - full color + draw.rectangle([x, y, x + bar_w - 1, y + h], fill=color) + else: + alpha = int(255 * (0.3 + 0.7 * h_ratio)) + rgb = tuple(int(color[ii:ii+2], 16) for ii in (1, 3, 5)) + draw.rectangle([x, y, x + bar_w - 1, y + h], + fill=f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}{alpha:02x}") + + # Dot at top of center bar + if i == 2: + dot_r = bar_w // 2 + draw.ellipse([x + bar_w//2 - dot_r, y - dot_r, + x + bar_w//2 + dot_r, y + dot_r], fill=COLOR_GREEN_LIGHT) + +def generate_favicon_ico(png_files): + """Generate ICO file from PNG files using ImageMagick.""" + png_paths = [os.path.join(LOGOS_DIR, f) for f in png_files] + ico_path = os.path.join(LOGOS_DIR, "favicon.ico") + + # Use ImageMagick to create multi-size ICO + os.system(f"convert {' '.join(png_paths)} -define icon:auto-resize=16,32,48 {ico_path} 2>/dev/null") + + if os.path.exists(ico_path): + print(f"✓ favicon.ico (from PNGs)") + else: + # Fallback: create from largest + os.system(f"convert {png_paths[-1]} -define icon:auto-resize=16,32,48 {ico_path} 2>/dev/null") + print("✓ favicon.ico (fallback)") + +def generate_png(size, filename): + """Generate PNG favicon.""" + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + + cx, cy = size // 2, size // 2 + + # Dark circle background with rounded corners + padding = size // 6 + draw.rounded_rectangle([padding, padding, size-padding, size-padding], + radius=size//4, fill=COLOR_DARK) + + draw_wave_with_dot(draw, cx, cy, size - size//3) + + img.save(os.path.join(LOGOS_DIR, filename)) + print(f"✓ {filename}") + +def generate_apple_touch_icon(): + """Generate Apple Touch Icon (180x180).""" + img = Image.new("RGBA", (180, 180), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + + # Dark background + draw.rounded_rectangle([0, 0, 179, 179], radius=36, fill=COLOR_DARK) + + cx, cy = 90, 90 + draw_wave_with_dot(draw, cx, cy, 120) + + img.save(os.path.join(LOGOS_DIR, "apple-touch-icon.png")) + print("✓ apple-touch-icon.png") + +def generate_web_manifest(): + """Generate site.webmanifest.""" + manifest = { + "name": "calls24 — Анализ звонков", + "short_name": "calls24", + "description": "Автоматический анализ звонков за 24 часа", + "icons": [ + {"src": "/logos/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png"}, + {"src": "/logos/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png"}, + ], + "theme_color": "#22c55e", + "background_color": "#0a0a0a", + "display": "standalone" + } + + import json + with open(os.path.join(LOGOS_DIR, "site.webmanifest"), "w") as f: + json.dump(manifest, f, ensure_ascii=False, indent=2) + print("✓ site.webmanifest") + +# Generate all favicons +print("🎨 Generating favicons for calls24...\n") + +# Generate PNGs first +generate_png(16, "favicon-16x16.png") +generate_png(32, "favicon-32x32.png") +generate_png(192, "android-chrome-192x192.png") +generate_png(512, "android-chrome-512x512.png") +generate_apple_touch_icon() +generate_web_manifest() + +# Create ICO from PNGs +print() +generate_favicon_ico(["favicon-16x16.png", "favicon-32x32.png", "android-chrome-192x192.png"]) + +print("\n✅ All favicons generated in logos/ folder") diff --git a/index-v4.html b/index-v4.html index 68cf27e..a885f76 100644 --- a/index-v4.html +++ b/index-v4.html @@ -10,6 +10,14 @@ content="Узнайте, сколько денег теряет ваш бизнес. Анализ каждого звонка с операторами за 24 часа." /> + + + + + + + + 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 literal 0 HcmV?d00001 diff --git a/logos/apple-touch-icon.png b/logos/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..545056cb482b39f6311d8c9aef04b738403b1617 GIT binary patch literal 1007 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nD4M^IaWiw)6VBY5G;uumf=j~lXzuN%}$3CjO znj)<5)nM)p#^U^oYm>LfEeuXyw$5j-OMXD7aK%z*R;EWPWq;<)|NHd2w4kTXm+Qy7 z9XH%H*>eB={HT3)r6sZ3bHCo&ymv#z`r9!R!)pZvFE3BK6FM*A1W%yyn??Q_DSClM zZPm1TOfDin>|z55gvi$|R~^K{ZRH&)%WXJ*@W z9(3%U8Fp-oxkiTJ&JO-u6Vp8VeV@T56Bx2!gsJfCGFwPi-+s^HvjAjx+T zi_cwfvE%?siPf%+%2>3A6Ua1sKYQy1mr4*bZ2fH@Q_v)^Y?Qgm+wP$PG_c=NyfnaGQ zl`!U%qyJv~e)jO)?;FQ$?%k_jS*Uwm;pyvK61)iyA7saaLTA&B{d2Y)*?(%X4juzH z-F^Svf33dK$+L&=z5qEPe8;5MDFm&L_fzUZiW+pQArXd4^whf7yK-N5Hwf)ocN>@p zfKdxpx#@P!Klx?R@fyjaikYWX*ZOu{w%@nBO>?p3%WZSFF59xw*LNSkZkCtk#h{B4vW`1k zP6n>owC+$^ht&PMtL4O7eCN+Pe&y!5{g3wL74N;C^{dVA$;_yG8b8?2PI*&Nb^5t1 QFe5W~y85}Sb4q9e0Ar8Q3IG5A literal 0 HcmV?d00001 diff --git a/logos/favicon-16x16.png b/logos/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..e81c4233fde27393c7115fd1132927914a8d2396 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`b)GJcAr*6y6C^SYbofr_?tX2r z$9sQYt@Fj^LkE`&PIvSjsfxc z0|yEwG&VlG!hPLz>4~g;wZD&f>@`@hi}A%d*XM^B&KWc?cHULw(T};Fu=UC9?fE^- g^CxJW;}&6NFuAQMBxSa?ALtSWPgg&ebxsLQ05-Zwx&QzG literal 0 HcmV?d00001 diff --git a/logos/favicon-32x32.png b/logos/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef7810df892ca497959b41613fa8306452e45cb GIT binary patch literal 272 zcmV+r0q_2aP)jVI>3=?e_KF`qx*kwYCnC@?m;EP)O0I=R_vVmHoNa=C6+4l>o8&O_vQK6tE8fj6$Qbfx`L~GKb ziA^IQsUdCP4{QZ0P#{t*t$;i#HXzg@bZZleVrnaGVZrR;E~nqm+&i~tFX!BQ?(VV+ zO;7UOnfcA{HFIWuXU^~3b({gtK&POFfu|_|g6J`0)kNQssmA796M#H5Na4$?id((y!m^ zLu$Wdp7F}lPUX3l<*l#syW8`JjrTiJbtAge}B?k*Kyin zj&o{Y9D4>hP7o{){Kx^~Osz0gAU>Ikz>~$LdE(yn8ZwzqV=GU6qlM21lZ8nc01v## zAnQAZRDXi_L~jzF74`|Q3%fG_9(a*y60)@)lgw=bCL!}pwUKwuolgjOM2`^X@R4s@Xt-CgY4wx`5b_|x0V$#rvv#*wRfM*g@?bSLiz zbKq?!>dY5Dr`4Gwn?r}hK|K?HLMOVDHdE}gR+|Lw8T00=6I(g58yyyL%ST5Is6)%( z2RhN+B$$1XV*ggrR|>3~!26AzAvfl@BW{VhcYUMGfI2k#BpU>DqWf8ay@~ncT&O)o zI4JnbGj84QyjY}TSY7^w(esA2&fZk>37`&*K6o;$2ktHL!O#BwAp7VYIylE2UY~z) z{F5au53a9nd31N(1wb7deef6`g2oY#A;hRu`}nli*S^M{7mc(RRpkAB)cr*#XRNI{ zu=sGpk%e#6{~J(;Mjt%zrkR7?vSIc?%KZDDtvZ7o6V4}a3>tm# zz-#PF&SyTmXMbe$^wjT-c;WG*4XYNtd8K`5^ubdi-m7JQ$sZb5>3(n3eLuF~pvEsy zheqF3v!9s%CyqC+)Ht^+I@EB~+NTbUzFwdI#J(@C&i_#ReAhicDBnEohZXy@-rt?K ztL`G84vju|;O(vTXV#B=$S;Vrp|^BY^i0{!(T{GKJM6;DpI3JP>d@$e2i|V%zbUd6 z7=O^mn1&9@afjTHp0S^aZ^ZhVI||$x z>#7W>Lo=~YJkpa6LGd@-^GD<3Ia7XX%$@YjG6U-7`RBjituL9LKcSw#W^OgcSP9SH zm;rT5Pc!yR&tLm2?632Owcn@bFY^%8AI+OZpPoN`?8oE_J}?j53(ib^dH%NZV9O@i z=Um3#BtCFPNC$Y}MW#uxdb*T$+_ntj6PnqeOBJuClAenamQ zLkGv-D*|#BnRH?o`>b6qySBTGBbAE!Nfj>MSN(2*& zeEe5eZnLmVARZqUhzsnkjQ*Q<_<=cYBtO@!92axPEgtSp`c|1KX+s~Pe)z)ZOW4Ie ze&Ekif#)=rb>GfCWxg;&V9)bRvGF}roY5YIuT#0sj+S%#Si>hCvet?3V!}`4x1zF5JJTq){M=MD_?)9WMuxihPw;T-m$Fo+{~XC&onbOfI(d<_C&TYt8&gIiSVI~iz2PcEq+jadqK)0G|J-lcZLb&;JK6lz z(k)-yQM+1u^v7D~n>5d?AHVr$Uu=R8e&moxPk*hSkZQY8e{yg$It&JBSJKXS^e0^u(?-YL&KPhQL zryoA}k?XJZ&;5?apIFQIClr6ySFe#zKYe6Z-PVU+sok%7i=Msz;`v2M+tAeyAN=pfX;J}pcjyOJjP38Mq#y_O8yzf6F1eS0L5d#R{0ucK*Zai^ZMm*;G&aVcp-ryoA}ku$yrKXbj$ zgRsx9tj?d@3-j#Pei*E8or9bl;;O6PJh=C+sBpD*Oi3F${qQB@C1!2{%fQN;b!^djcno%?dFDQQEeAHL*yC%pe*&i_4n|1$57`rga$jz->B z;jWE|AkBE(H109Xk5Zi+=2254+gM53X$dCl+1L-(Rlx z?_YKwq?Y8c#|g^>?k~Bbm007wrL%&lkAZbM_e;*0oT4(wlK!dYdPdVUwtkuc_mm9tzfFm<4L|h1QDMtH@ax)R{{HWlVdum0xu-NCo_S5S`Ei!{ IU$S`l9|3CZ82|tP literal 0 HcmV?d00001 diff --git a/logos/site.webmanifest b/logos/site.webmanifest new file mode 100644 index 0000000..2a83c95 --- /dev/null +++ b/logos/site.webmanifest @@ -0,0 +1,20 @@ +{ + "name": "calls24 — Анализ звонков", + "short_name": "calls24", + "description": "Автоматический анализ звонков за 24 часа", + "icons": [ + { + "src": "/logos/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/logos/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#22c55e", + "background_color": "#0a0a0a", + "display": "standalone" +} \ No newline at end of file