
westakof
Муж-геймер - горе семье?
Часто встречаю мнение, что если муж играет семье конец.
Решил проверить, как это выглядит в реальности, а не в стереотипах.
Кто у вас геймер?
Поднимите вверх, чтоб больше людей проголосовало
Продолжение поста «Добавил иконки в Gmail почту! Красота =)»1
Напомню. Я сделал расширение, которое добавляет иконки в Gmail почту. И в комментариях попросили сделать не расширением, а скриптом для Tampermonkey. Мне было лень, но потом я подумал почему бы и нет. Скинул код в chatGPT и он подогнал его под формат Tampermonkey. Ниже расскажу как поставить чтоб работало.
Выглядит вот так:
Как поставить?
Установить Tampermonkey если у вас его ещё нет https://chromewebstore.google.com/detail/tampermonkey/dhdgff...
Правой кнопкой по иконке расширения и Параметры
В верхнем меню панели Tampermonkey жмём на плюсик и открывается страница создания нового скрипта
Вставляем код скприта сюда и сохраняем
Готово.
Вот сам код скрипта:
// ==UserScript==
// @name Gmail Sender Favicon (base + S2, fallback letter)
// @namespace https://tampermonkey.net/
// @VERSION 2026-01-02
// @Description Gmail: show sender base-domain favicon; fallback to Google S2; then fallback to letter.
// @match https://mail.google.com/*
// @run-at document-idle
// ==/UserScript==
(() => {
'use strict';
const STYLE_ID = 'gm-sender-favicon-style-final';
const BADGE_CLASS = 'gm-sender-badge-final';
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = `
.${BADGE_CLASS}{
display:inline-flex;
align-items:center;
margin-right:6px;
vertical-align:middle;
flex: 0 0 auto;
transform: translateY(-0.5px);
}
.${BADGE_CLASS} img{
width:14px;height:14px;border-radius:3px;display:block;
}
.${BADGE_CLASS} .gm-letter{
font-weight:700;
font-size:12px;
line-height:1;
display:inline-block;
}
`;
document.documentElement.appendChild(style);
}
// ---------- domain/email helpers ----------
function extractEmailLike(str) {
if (!str) return '';
const s = String(str);
// <mail@domain>
const m1 = s.match(/<\s*([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})\s*>/i);
if (m1) return m1[1];
// mail@domain
const m2 = s.match(/([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})/i);
if (m2) return m2[1];
return '';
}
function getDomainFromEmail(email) {
const at = (email || '').indexOf('@');
if (at === -1) return '';
return email.slice(at + 1).trim().toLowerCase();
}
// Простая нормализация к base-domain: example.com
function baseDomain(domain) {
const parts = (domain || '').split('.').filter(Boolean);
if (parts.length < 2) return domain || '';
return parts.slice(-2).join('.');
}
// стабильный цвет по seed (чтобы буква была “привычного” цвета)
function stableColor(seed) {
let h = 0;
for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) | 0;
const hue = Math.abs(h) % 360;
return `hsl(${hue}, 70%, 40%)`;
}
function pickLetter(senderText, base) {
const m = (senderText || '').match(/[a-z0-9а-яё]/i);
if (m) return m[0].toUpperCase();
if (base) return base[0].toUpperCase();
return '?';
}
// ---------- locating sender in a row ----------
function findSenderInfo(row) {
// 1) самый надежный: span[email]
const emailSpan = row.querySelector('span[email]');
if (emailSpan) {
const email = emailSpan.getAttribute('email') || '';
const senderText =
(emailSpan.getAttribute('name') || '').trim() ||
(emailSpan.getAttribute('aria-label') || '').trim() ||
(emailSpan.textContent || '').trim();
return { email, senderText, senderNode: emailSpan };
}
// 2) data-hovercard-id часто содержит email
const hcSpan = row.querySelector('span[data-hovercard-id]');
if (hcSpan) {
const hc = hcSpan.getAttribute('data-hovercard-id') || '';
const email = extractEmailLike(hc) || extractEmailLike(hcSpan.getAttribute('aria-label')) || '';
const senderText =
(hcSpan.getAttribute('name') || '').trim() ||
(hcSpan.getAttribute('aria-label') || '').trim() ||
(hcSpan.textContent || '').trim();
return { email, senderText, senderNode: hcSpan };
}
// 3) иногда sender виден как yP/zF
const nameSpan = row.querySelector('span.yP, span.zF');
if (nameSpan) {
const email =
extractEmailLike(nameSpan.getAttribute('aria-label')) ||
extractEmailLike(nameSpan.getAttribute('title')) ||
'';
const senderText =
(nameSpan.getAttribute('aria-label') || '').trim() ||
(nameSpan.getAttribute('title') || '').trim() ||
(nameSpan.textContent || '').trim();
return { email, senderText, senderNode: nameSpan };
}
return null;
}
// ---------- badge creation ----------
function makeBadge(base, letter, fontSizePx) {
const badge = document.createElement('span');
badge.className = BADGE_CLASS;
badge.setAttribute('data-base', base || '');
const img = document.createElement('img');
img.alt = '';
img.referrerPolicy = 'no-referrer';
const sources = base
? [
`https://${base}/favicon.ico`,
`https://www.google.com/s2/favicons?sz=64&domain=%24%7Ben...}`
]
: [];
let idx = 0;
const fallbackToLetter = () => {
img.remove();
const span = document.createElement('span');
span.className = 'gm-letter';
span.textContent = letter;
span.style.color = stableColor(base || letter || 'x');
if (fontSizePx) span.style.fontSize = fontSizePx;
badge.appendChild(span);
};
img.onerror = () => {
idx++;
if (idx < sources.length) {
img.src = sources[idx];
} else {
fallbackToLetter();
}
};
if (sources.length) {
img.src = sources[0];
badge.appendChild(img);
} else {
fallbackToLetter();
}
return badge;
}
// ---------- main enhancer ----------
function enhanceRow(row) {
const info = findSenderInfo(row);
if (!info) return;
const domain = getDomainFromEmail(info.email);
const base = baseDomain(domain);
// текст для буквы
const senderText = info.senderText || (info.senderNode?.textContent || '').trim();
const letter = pickLetter(senderText, base);
// куда вставлять:
// A) предпочитаем контейнер отправителя (как у тебя работало)
let target = row.querySelector('.yX.xY');
// B) если нет — рядом с узлом senderNode
let insertBeforeNode = null;
if (!target && info.senderNode && info.senderNode.parentElement) {
target = info.senderNode.parentElement;
insertBeforeNode = info.senderNode;
}
if (!target) return;
// уже вставлено?
const existing = target.querySelector(`:scope > .${BADGE_CLASS}`);
if (existing) {
const exBase = existing.getAttribute('data-base') || '';
if (exBase === (base || '')) return;
existing.remove();
}
const cs = window.getComputedStyle(target);
const fontSizePx = cs.fontSize ? `calc(${cs.fontSize} - 1px)` : '';
const badge = makeBadge(base, letter, fontSizePx);
if (insertBeforeNode) target.insertBefore(badge, insertBeforeNode);
else target.prepend(badge);
}
function enhanceAll() {
injectStyles();
document.querySelectorAll('.zA').forEach(enhanceRow);
}
// ---------- observer (throttled) ----------
let rafPending = false;
function schedule() {
if (rafPending) return;
rafPending = true;
requestAnimationFrame(() => {
rafPending = false;
enhanceAll();
});
}
const obs = new MutationObserver(schedule);
obs.observe(document.body, { childList: true, subtree: true });
enhanceAll();
})();
Всегда мечтал сделать гоночную игру и вот делаю!
Всем привет и с наступающим Новым Годом!
Я решил, что сейчас самое время, чтобы исполнить мечту детства и попробовать самому сделать гоночную игру =)
Она ещё пока очень сырая и её нет в интернете, но уже есть машинки соперники и трасса, так что вот пока что фото моего утреннего заезда )
Буду благодарен за поддержку!
Добавил иконки в Gmail почту! Красота =)1
Сегодня переписал бесплатное расширение меток для гугловской почты и теперь это расширение, которое добавляет в гугл почту иконки отправителей, а если иконки нет, то показывается метку.
Мне кажется, что стало красиво!
Скачать можно тут https://chromewebstore.google.com/detail/gmail-email-icons/n...
Стало:
Надо?
Ребята, могу сделать штуку, которая следит за ценой товара на Ozon и пишет в Telegram, если цена упала больше чем на 5%.
📌 Фишка: вы просто с телефона кидаете ссылку боту и товар сразу добавляется в отслеживание.
⚠️ Нюанс: чтобы всё работало, нужно будет поставить небольшое расширение в браузер на компьютере - оно будет периодически проверять цену.
Если идея нужна - плюсуйте пост, и если желающих будет много, сделаю 👍
Чужая победа, твой счет за ремонт
Мне вдруг стало ясно: я гораздо сильнее люблю истории не про героев, а про "маленьких людей", таких же как и я сам.
Про тех, кто пытается строить свой быт, ищет деньги на ипотеку, переживает за детей, смотрит в завтра с тревогой, ведь его могут перемолоть в пыль жернова обстоятесльств и очередные поиски правды какого-нибудь Человека Мыши, который живёт на соседней улице.
И при всё этом сюре, что творится вокруг, маленький и простой человек с понятными желаниями и тревогами, умудряется сохранять человечность.
Не про очередную войну за власть и громкие пророчества, а про то, как люди живут “на фоне” всего этого.
Про стражника на воротах, которому мимоходом оторвало руку, пока главный герой пытается доблестно спасти принцессу юга. Что с ним завтра то случилось, как он теперь вообще выживает дальше?
Про девушку, которая 3 месяца училась на права и сдала с первой попытки, кроила бюджет и смогла купить какую-никакую, но машину, а на неё свалился и разрушил в хлам супермен на следующих день. Как у неё нервы после такого, не сдали, куда её дорога дальше заведёт?
Про кассира/продавщицу, у которой магазин оказался “в эпицентре” и витрины вдребезги, товар пропал, охраны нет, начальство требует выйти завтра как ни в чем не бывало. А у неё кредит, аренда, мама на лекарствах. И вот она стоит среди стекла и думает: это мне теперь что всё начинать с нуля? И где взять силы, когда на тебя всем плевать?
Если знаете такие фильмы и особенно книги — киньте рекомендации.
Плюсуйте пост, что больше рекомендаций собрать в комментариях.





