snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
# @rocket.chat/favicon
## 0.0.5
### Patch Changes
- ([#38989](https://github.com/RocketChat/Rocket.Chat/pull/38989)) chore(eslint): Upgrades ESLint and its configuration
## 0.0.5-rc.0
### Patch Changes
- ([#38989](https://github.com/RocketChat/Rocket.Chat/pull/38989)) chore(eslint): Upgrades ESLint and its configuration
## 0.0.4
### Patch Changes
- ([#37447](https://github.com/RocketChat/Rocket.Chat/pull/37447)) Fixes a GUI crash due to an unhandled cross-origin error happening while rendering favicon badge
## 0.0.4-rc.0
### Patch Changes
- ([#37447](https://github.com/RocketChat/Rocket.Chat/pull/37447)) Fixes a GUI crash due to an unhandled cross-origin error happening while rendering favicon badge
## 0.0.3
### Patch Changes
- ([#37512](https://github.com/RocketChat/Rocket.Chat/pull/37512) by [@dionisio-bot](https://github.com/dionisio-bot)) Fixes a GUI crash due to an unhandled cross-origin error happening while rendering favicon badge
## 0.0.2
### Patch Changes
- ([#31138](https://github.com/RocketChat/Rocket.Chat/pull/31138)) feat(uikit): Move `@rocket.chat/ui-kit` package to the main monorepo
## 0.0.2-rc.0
### Patch Changes
- b223cbde14: feat(uikit): Move `@rocket.chat/ui-kit` package to the main monorepo

View File

@@ -0,0 +1,23 @@
{
"name": "@rocket.chat/favicon",
"version": "0.0.5",
"private": true,
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"files": [
"/dist"
],
"scripts": {
"build": "rm -rf dist && tsc -p tsconfig.json",
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
"lint": "eslint .",
"lint:fix": "eslint --fix ."
},
"devDependencies": {
"eslint": "~9.39.5",
"typescript": "~5.9.3"
},
"volta": {
"extends": "../../package.json"
}
}

View File

@@ -0,0 +1,87 @@
export type Badge = number | string | null | undefined;
const getBadgeText = (badge: NonNullable<Badge>) => {
if (typeof badge === 'number') {
badge = Math.abs(badge | 0);
if (badge > 999) {
return `${badge > 9999 ? 9 : Math.floor(badge / 1000)}k+`;
}
return String(badge);
}
return String(badge);
};
const getBadgeStyle = (badge: NonNullable<Badge>) => {
if (typeof badge === 'number' && badge > 0) {
return { bgColor: '#ac1b1b', textColor: '#fff', fontFamily: 'sans-serif', fontStyle: 'bold' };
}
if (typeof badge === 'string' && badge !== '') {
return { bgColor: '#3d8a3a', textColor: '#fff', fontFamily: 'sans-serif', fontStyle: 'bold' };
}
throw new Error('Invalid badge type');
};
export const drawBadge = (badge: Badge, context: CanvasRenderingContext2D) => {
if (!badge) {
return;
}
const text = getBadgeText(badge);
const { fontFamily, fontStyle, bgColor, textColor } = getBadgeStyle(badge);
let w = 0.6;
const h = 0.6;
let x = 0.4;
const y = 0;
if (text.length === 2) {
x -= w * 0.4;
w *= 1.4;
} else if (text.length >= 3) {
x -= w * 0.65;
w *= 1.65;
}
context.beginPath();
if (text.length > 1) {
context.moveTo(x + w / 2, y);
context.lineTo(x + w - h / 2, y);
context.quadraticCurveTo(x + w, y, x + w, y + h / 2);
context.lineTo(x + w, y + h - h / 2);
context.quadraticCurveTo(x + w, y + h, x + w - h / 2, y + h);
context.lineTo(x + h / 2, y + h);
context.quadraticCurveTo(x, y + h, x, y + h - h / 2);
context.lineTo(x, y + h / 2);
context.quadraticCurveTo(x, y, x + h / 2, y);
} else {
context.arc(x + w / 2, y + h / 2, h / 2, 0, 2 * Math.PI);
}
context.fillStyle = bgColor;
context.fill();
context.closePath();
context.font = `${fontStyle} ${h * (text.length > 2 ? 0.85 : 1)}px ${fontFamily}`;
context.textAlign = 'center';
context.fillStyle = textColor;
context.beginPath();
context.stroke();
if (text.length > 3) {
context.fillText(text, x + w / 2, y + h - h * 0.2);
} else {
context.fillText(text, x + w / 2, y + h - h * 0.15);
}
context.closePath();
};

View File

@@ -0,0 +1,118 @@
import type { Badge } from './badge';
import { drawBadge } from './badge';
const getFavicons = () => {
const favicons = Array.from(document.head.getElementsByTagName('link')).filter((link) =>
/(^|\s)icon(\s|$)/i.test(link.getAttribute('rel') ?? ''),
);
if (favicons.length === 0) {
const link = document.createElement('link');
link.setAttribute('rel', 'icon');
document.head.appendChild(link);
favicons.push(link);
}
for (const favicon of favicons) {
favicon.setAttribute('type', 'image/png');
}
return favicons;
};
const fetchFaviconImage = async (url: string | undefined) => {
const img = new Image();
if (url) {
img.crossOrigin = 'anonymous';
img.src = url;
} else {
img.src = '';
img.width = 32;
img.height = 32;
}
return new Promise<HTMLImageElement>((resolve, reject) => {
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error('Failed to load image'));
};
});
};
const renderAndUpdate = ({
badge,
canvas,
favicons,
context,
img,
}: {
badge: Badge;
canvas: HTMLCanvasElement;
favicons: HTMLLinkElement[];
context: CanvasRenderingContext2D;
img: HTMLImageElement;
}) => {
context.scale(canvas.width, canvas.height);
context.clearRect(0, 0, 1, 1);
context.drawImage(img, 0, 0, 1, 1);
drawBadge(badge, context);
context.setTransform(1, 0, 0, 1, 0, 0);
const url = canvas.toDataURL('image/png');
for (const icon of favicons) {
icon.setAttribute('href', url);
}
};
export const manageFavicon = () => {
let pendingBadge: Badge;
let updateOrCollect = (badge: Badge) => {
pendingBadge = badge;
};
const init = async () => {
const favicons = getFavicons();
const lastFavicon = favicons[favicons.length - 1];
const faviconURL = lastFavicon.getAttribute('href') ?? undefined;
const img = await fetchFaviconImage(faviconURL);
const canvas = document.createElement('canvas');
canvas.width = img.width > 0 ? img.width : 32;
canvas.height = img.height > 0 ? img.height : 32;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Failed to create canvas context');
}
updateOrCollect = (badge) => {
try {
renderAndUpdate({ badge, canvas, favicons, context, img });
} catch (error) {
console.error('Error rendering favicon:', error);
}
};
if (pendingBadge) {
updateOrCollect(pendingBadge);
pendingBadge = undefined;
}
};
init().catch((error) => {
console.error('Error initializing favicon manager:', error);
});
return (badge: Badge) => {
updateOrCollect(badge);
};
};

View File

@@ -0,0 +1,8 @@
{
"extends": "@rocket.chat/tsconfig/client.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["./src/**/*"]
}