favicon-animate 0.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.
- package/dist/animator.d.ts +61 -0
- package/dist/animator.d.ts.map +1 -0
- package/dist/animator.test.d.ts +2 -0
- package/dist/animator.test.d.ts.map +1 -0
- package/dist/badge.d.ts +22 -0
- package/dist/badge.d.ts.map +1 -0
- package/dist/badge.test.d.ts +2 -0
- package/dist/badge.test.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +436 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +72 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +40 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/visibility.d.ts +28 -0
- package/dist/visibility.d.ts.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AnimatorOptions, BadgeConfig, FaviconConfig, FaviconData } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Main FaviconAnimator class
|
|
4
|
+
*/
|
|
5
|
+
export declare class FaviconAnimator {
|
|
6
|
+
private faviconLink;
|
|
7
|
+
private currentFavicon;
|
|
8
|
+
private currentBadge;
|
|
9
|
+
private isAnimating;
|
|
10
|
+
private pauseOnHidden;
|
|
11
|
+
private isVisible;
|
|
12
|
+
private unsubscribeVisibility;
|
|
13
|
+
private faviconSize;
|
|
14
|
+
private updateInterval;
|
|
15
|
+
constructor(options?: AnimatorOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Set favicon
|
|
18
|
+
*/
|
|
19
|
+
setFavicon(config: FaviconConfig | string): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Set badge
|
|
22
|
+
*/
|
|
23
|
+
setBadge(config: BadgeConfig): void;
|
|
24
|
+
/**
|
|
25
|
+
* Update badge
|
|
26
|
+
*/
|
|
27
|
+
updateBadge(number: number | string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Remove badge
|
|
30
|
+
*/
|
|
31
|
+
removeBadge(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Update favicon display (with badge if configured)
|
|
34
|
+
*/
|
|
35
|
+
private updateFaviconDisplay;
|
|
36
|
+
/**
|
|
37
|
+
* Pause animation
|
|
38
|
+
*/
|
|
39
|
+
pause(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Resume animation
|
|
42
|
+
*/
|
|
43
|
+
resume(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get current favicon data
|
|
46
|
+
*/
|
|
47
|
+
getFavicon(): FaviconData | null;
|
|
48
|
+
/**
|
|
49
|
+
* Get current badge config
|
|
50
|
+
*/
|
|
51
|
+
getBadge(): BadgeConfig | null;
|
|
52
|
+
/**
|
|
53
|
+
* Check if currently animating
|
|
54
|
+
*/
|
|
55
|
+
getIsAnimating(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Cleanup
|
|
58
|
+
*/
|
|
59
|
+
destroy(): void;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=animator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animator.d.ts","sourceRoot":"","sources":["../src/animator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAYnF;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,cAAc,CAAe;gBAEzB,OAAO,GAAE,eAAoB;IAoCzC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B/D;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IASnC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAU1C;;OAEG;IACH,WAAW,IAAI,IAAI;IAKnB;;OAEG;YACW,oBAAoB;IAiClC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,IAAI,IAAI;IAMd;;OAEG;IACH,UAAU,IAAI,WAAW,GAAG,IAAI;IAIhC;;OAEG;IACH,QAAQ,IAAI,WAAW,GAAG,IAAI;IAI9B;;OAEG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animator.test.d.ts","sourceRoot":"","sources":["../src/animator.test.ts"],"names":[],"mappings":""}
|
package/dist/badge.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BadgeConfig, BadgePosition } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Badge rendering utilities
|
|
4
|
+
*/
|
|
5
|
+
export declare class BadgeRenderer {
|
|
6
|
+
/**
|
|
7
|
+
* Calculate badge position coordinates
|
|
8
|
+
*/
|
|
9
|
+
static calculatePosition(position: BadgePosition, faviconSize: number, badgeSize: number, offset?: number): {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Draw badge on canvas
|
|
15
|
+
*/
|
|
16
|
+
static drawBadge(ctx: CanvasRenderingContext2D, config: BadgeConfig, faviconSize: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* Validate badge configuration
|
|
19
|
+
*/
|
|
20
|
+
static validateConfig(config: Partial<BadgeConfig>): BadgeConfig;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAErD;;GAEG;AAEH,qBAAa,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CACtB,QAAQ,EAAE,aAAa,EACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,MAAU,GACjB;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAmB3B;;OAEG;IACH,MAAM,CAAC,SAAS,CACd,GAAG,EAAE,wBAAwB,EAC7B,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,GAClB,IAAI;IAiCP;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW;CA4CjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badge.test.d.ts","sourceRoot":"","sources":["../src/badge.test.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* favicon-animate - Lightweight favicon animation library
|
|
3
|
+
* @version 0.1.0
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
export { FaviconAnimator } from './animator';
|
|
7
|
+
export { BadgeRenderer } from './badge';
|
|
8
|
+
export { getVisibilityManager, VisibilityManager } from './visibility';
|
|
9
|
+
export type { BadgePosition, BadgeConfig, FaviconConfig, AnimatorOptions, VisibilityState, FaviconData } from './types';
|
|
10
|
+
export { getFaviconLink, createCanvas, loadImage, detectImageFormat, supportsPageVisibility, debounce, throttle, canvasToDataUrl, isBrowser } from './utils';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGvE,YAAY,EACV,aAAa,EACb,WAAW,EACX,aAAa,EACb,eAAe,EACf,eAAe,EACf,WAAW,EACZ,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,sBAAsB,EACtB,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,SAAS,EACV,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Badge rendering utilities
|
|
3
|
+
*/
|
|
4
|
+
class BadgeRenderer {
|
|
5
|
+
/**
|
|
6
|
+
* Calculate badge position coordinates
|
|
7
|
+
*/
|
|
8
|
+
static calculatePosition(position, faviconSize, badgeSize, offset = 2) {
|
|
9
|
+
const badgeRadius = badgeSize / 2;
|
|
10
|
+
switch (position) {
|
|
11
|
+
case 'top-left':
|
|
12
|
+
return { x: offset + badgeRadius, y: offset + badgeRadius };
|
|
13
|
+
case 'top-right':
|
|
14
|
+
return { x: faviconSize - offset - badgeRadius, y: offset + badgeRadius };
|
|
15
|
+
case 'bottom-left':
|
|
16
|
+
return { x: offset + badgeRadius, y: faviconSize - offset - badgeRadius };
|
|
17
|
+
case 'bottom-right':
|
|
18
|
+
return { x: faviconSize - offset - badgeRadius, y: faviconSize - offset - badgeRadius };
|
|
19
|
+
case 'center':
|
|
20
|
+
return { x: faviconSize / 2, y: faviconSize / 2 };
|
|
21
|
+
default:
|
|
22
|
+
return { x: faviconSize - offset - badgeRadius, y: offset + badgeRadius };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Draw badge on canvas
|
|
27
|
+
*/
|
|
28
|
+
static drawBadge(ctx, config, faviconSize) {
|
|
29
|
+
const { number, position, backgroundColor = '#FF0000', textColor = '#FFFFFF', size = 16, fontSize = 12, borderRadius = '50%', offset = 2, fontWeight = 'bold', fontFamily = 'Arial, sans-serif' } = config;
|
|
30
|
+
const { x, y } = this.calculatePosition(position, faviconSize, size, offset);
|
|
31
|
+
// Draw badge background circle
|
|
32
|
+
ctx.fillStyle = backgroundColor;
|
|
33
|
+
ctx.beginPath();
|
|
34
|
+
ctx.arc(x, y, size / 2, 0, Math.PI * 2);
|
|
35
|
+
ctx.fill();
|
|
36
|
+
// Draw badge text
|
|
37
|
+
ctx.fillStyle = textColor;
|
|
38
|
+
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
39
|
+
ctx.textAlign = 'center';
|
|
40
|
+
ctx.textBaseline = 'middle';
|
|
41
|
+
// Ensure number fits in badge
|
|
42
|
+
const numberStr = String(number).substring(0, 3);
|
|
43
|
+
ctx.fillText(numberStr, x, y);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validate badge configuration
|
|
47
|
+
*/
|
|
48
|
+
static validateConfig(config) {
|
|
49
|
+
const { number = 1, position = 'top-right', backgroundColor = '#FF0000', textColor = '#FFFFFF', size = 16, fontSize = 12, borderRadius = '50%', offset = 2, fontWeight = 'bold', fontFamily = 'Arial, sans-serif' } = config;
|
|
50
|
+
// Validate position
|
|
51
|
+
const validPositions = [
|
|
52
|
+
'top-left',
|
|
53
|
+
'top-right',
|
|
54
|
+
'bottom-left',
|
|
55
|
+
'bottom-right',
|
|
56
|
+
'center'
|
|
57
|
+
];
|
|
58
|
+
if (!validPositions.includes(position)) {
|
|
59
|
+
throw new Error(`Invalid badge position: ${position}`);
|
|
60
|
+
}
|
|
61
|
+
// Validate number
|
|
62
|
+
if (typeof number !== 'number' && typeof number !== 'string') {
|
|
63
|
+
throw new Error('Badge number must be a number or string');
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
number,
|
|
67
|
+
position: position,
|
|
68
|
+
backgroundColor,
|
|
69
|
+
textColor,
|
|
70
|
+
size: Math.max(8, size),
|
|
71
|
+
fontSize: Math.max(6, fontSize),
|
|
72
|
+
borderRadius,
|
|
73
|
+
offset: Math.max(0, offset),
|
|
74
|
+
fontWeight,
|
|
75
|
+
fontFamily
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Page Visibility API wrapper for managing favicon animations
|
|
82
|
+
* when the tab is not in focus
|
|
83
|
+
*/
|
|
84
|
+
class VisibilityManager {
|
|
85
|
+
constructor() {
|
|
86
|
+
this.isVisible = true;
|
|
87
|
+
this.callbacks = new Set();
|
|
88
|
+
this.boundHandleVisibilityChange = this.handleVisibilityChange.bind(this);
|
|
89
|
+
this.init();
|
|
90
|
+
}
|
|
91
|
+
init() {
|
|
92
|
+
// Check initial visibility state
|
|
93
|
+
this.isVisible = !document.hidden;
|
|
94
|
+
// Listen for visibility changes
|
|
95
|
+
document.addEventListener('visibilitychange', this.boundHandleVisibilityChange);
|
|
96
|
+
}
|
|
97
|
+
handleVisibilityChange() {
|
|
98
|
+
const wasVisible = this.isVisible;
|
|
99
|
+
this.isVisible = !document.hidden;
|
|
100
|
+
// Only notify if state actually changed
|
|
101
|
+
if (wasVisible !== this.isVisible) {
|
|
102
|
+
this.notifyCallbacks();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
notifyCallbacks() {
|
|
106
|
+
this.callbacks.forEach(callback => {
|
|
107
|
+
try {
|
|
108
|
+
callback(this.isVisible);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error('Error in visibility callback:', error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Subscribe to visibility changes
|
|
117
|
+
*/
|
|
118
|
+
onChange(callback) {
|
|
119
|
+
this.callbacks.add(callback);
|
|
120
|
+
// Return unsubscribe function
|
|
121
|
+
return () => {
|
|
122
|
+
this.callbacks.delete(callback);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get current visibility state
|
|
127
|
+
*/
|
|
128
|
+
getIsVisible() {
|
|
129
|
+
return this.isVisible;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Cleanup
|
|
133
|
+
*/
|
|
134
|
+
destroy() {
|
|
135
|
+
document.removeEventListener('visibilitychange', this.boundHandleVisibilityChange);
|
|
136
|
+
this.callbacks.clear();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Singleton instance
|
|
140
|
+
let visibilityManager = null;
|
|
141
|
+
function getVisibilityManager() {
|
|
142
|
+
if (!visibilityManager) {
|
|
143
|
+
visibilityManager = new VisibilityManager();
|
|
144
|
+
}
|
|
145
|
+
return visibilityManager;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Utility functions
|
|
150
|
+
*/
|
|
151
|
+
/**
|
|
152
|
+
* Get or create favicon link element
|
|
153
|
+
*/
|
|
154
|
+
function getFaviconLink() {
|
|
155
|
+
let link = document.querySelector('link[rel="icon"]');
|
|
156
|
+
if (!link) {
|
|
157
|
+
link = document.createElement('link');
|
|
158
|
+
link.rel = 'icon';
|
|
159
|
+
link.type = 'image/png';
|
|
160
|
+
document.head.appendChild(link);
|
|
161
|
+
}
|
|
162
|
+
return link;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Create canvas element
|
|
166
|
+
*/
|
|
167
|
+
function createCanvas(size) {
|
|
168
|
+
const canvas = document.createElement('canvas');
|
|
169
|
+
canvas.width = size;
|
|
170
|
+
canvas.height = size;
|
|
171
|
+
return canvas;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Load image from URL
|
|
175
|
+
*/
|
|
176
|
+
function loadImage(url) {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
const img = new Image();
|
|
179
|
+
img.onload = () => resolve(img);
|
|
180
|
+
img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
|
|
181
|
+
img.src = url;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Detect image format from URL or data URL
|
|
186
|
+
*/
|
|
187
|
+
function detectImageFormat(url) {
|
|
188
|
+
if (url.startsWith('data:')) {
|
|
189
|
+
const mimeType = url.split(';')[0].split(':')[1];
|
|
190
|
+
if (mimeType.includes('gif'))
|
|
191
|
+
return 'gif';
|
|
192
|
+
if (mimeType.includes('webp'))
|
|
193
|
+
return 'webp';
|
|
194
|
+
if (mimeType.includes('svg'))
|
|
195
|
+
return 'svg';
|
|
196
|
+
if (mimeType.includes('png'))
|
|
197
|
+
return 'png';
|
|
198
|
+
if (mimeType.includes('icon'))
|
|
199
|
+
return 'ico';
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const ext = url.split('.').pop()?.toLowerCase();
|
|
203
|
+
if (ext === 'gif')
|
|
204
|
+
return 'gif';
|
|
205
|
+
if (ext === 'webp')
|
|
206
|
+
return 'webp';
|
|
207
|
+
if (ext === 'svg')
|
|
208
|
+
return 'svg';
|
|
209
|
+
if (ext === 'ico')
|
|
210
|
+
return 'ico';
|
|
211
|
+
if (ext === 'png')
|
|
212
|
+
return 'png';
|
|
213
|
+
}
|
|
214
|
+
return 'unknown';
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Check if browser supports Page Visibility API
|
|
218
|
+
*/
|
|
219
|
+
function supportsPageVisibility() {
|
|
220
|
+
return typeof document.hidden !== 'undefined';
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Debounce function
|
|
224
|
+
*/
|
|
225
|
+
function debounce(func, wait) {
|
|
226
|
+
let timeout;
|
|
227
|
+
return function executedFunction(...args) {
|
|
228
|
+
const later = () => {
|
|
229
|
+
clearTimeout(timeout);
|
|
230
|
+
func(...args);
|
|
231
|
+
};
|
|
232
|
+
clearTimeout(timeout);
|
|
233
|
+
timeout = setTimeout(later, wait);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Throttle function
|
|
238
|
+
*/
|
|
239
|
+
function throttle(func, limit) {
|
|
240
|
+
let inThrottle;
|
|
241
|
+
return function executedFunction(...args) {
|
|
242
|
+
if (!inThrottle) {
|
|
243
|
+
func(...args);
|
|
244
|
+
inThrottle = true;
|
|
245
|
+
setTimeout(() => (inThrottle = false), limit);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Convert canvas to data URL
|
|
251
|
+
*/
|
|
252
|
+
function canvasToDataUrl(canvas) {
|
|
253
|
+
return canvas.toDataURL('image/png');
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Check if running in browser environment
|
|
257
|
+
*/
|
|
258
|
+
function isBrowser() {
|
|
259
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Main FaviconAnimator class
|
|
264
|
+
*/
|
|
265
|
+
class FaviconAnimator {
|
|
266
|
+
constructor(options = {}) {
|
|
267
|
+
this.currentFavicon = null;
|
|
268
|
+
this.currentBadge = null;
|
|
269
|
+
this.isAnimating = false;
|
|
270
|
+
this.pauseOnHidden = true;
|
|
271
|
+
this.isVisible = true;
|
|
272
|
+
this.unsubscribeVisibility = null;
|
|
273
|
+
this.faviconSize = 32;
|
|
274
|
+
this.updateInterval = 100;
|
|
275
|
+
if (!isBrowser()) {
|
|
276
|
+
throw new Error('FaviconAnimator can only be used in browser environment');
|
|
277
|
+
}
|
|
278
|
+
this.faviconLink = getFaviconLink();
|
|
279
|
+
this.pauseOnHidden = options.pauseOnHidden !== false;
|
|
280
|
+
this.updateInterval = options.updateInterval || 100;
|
|
281
|
+
// Setup visibility manager
|
|
282
|
+
if (this.pauseOnHidden) {
|
|
283
|
+
const visibilityManager = getVisibilityManager();
|
|
284
|
+
this.unsubscribeVisibility = visibilityManager.onChange(isVisible => {
|
|
285
|
+
this.isVisible = isVisible;
|
|
286
|
+
if (isVisible) {
|
|
287
|
+
this.resume();
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
this.pause();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// Set initial favicon if provided
|
|
295
|
+
if (options.favicon) {
|
|
296
|
+
const faviconConfig = typeof options.favicon === 'string'
|
|
297
|
+
? { url: options.favicon }
|
|
298
|
+
: options.favicon;
|
|
299
|
+
this.setFavicon(faviconConfig);
|
|
300
|
+
}
|
|
301
|
+
// Set initial badge if provided
|
|
302
|
+
if (options.badge) {
|
|
303
|
+
this.setBadge(options.badge);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Set favicon
|
|
308
|
+
*/
|
|
309
|
+
async setFavicon(config) {
|
|
310
|
+
const faviconConfig = typeof config === 'string'
|
|
311
|
+
? { url: config }
|
|
312
|
+
: config;
|
|
313
|
+
const { url, size = 32 } = faviconConfig;
|
|
314
|
+
this.faviconSize = size;
|
|
315
|
+
try {
|
|
316
|
+
// Detect format
|
|
317
|
+
const format = detectImageFormat(url);
|
|
318
|
+
// Create favicon data
|
|
319
|
+
this.currentFavicon = {
|
|
320
|
+
url,
|
|
321
|
+
size,
|
|
322
|
+
isAnimated: format === 'gif' || format === 'webp',
|
|
323
|
+
format
|
|
324
|
+
};
|
|
325
|
+
// Update favicon link
|
|
326
|
+
await this.updateFaviconDisplay();
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
console.error('Error setting favicon:', error);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Set badge
|
|
334
|
+
*/
|
|
335
|
+
setBadge(config) {
|
|
336
|
+
try {
|
|
337
|
+
this.currentBadge = BadgeRenderer.validateConfig(config);
|
|
338
|
+
this.updateFaviconDisplay();
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
console.error('Error setting badge:', error);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Update badge
|
|
346
|
+
*/
|
|
347
|
+
updateBadge(number) {
|
|
348
|
+
if (!this.currentBadge) {
|
|
349
|
+
console.warn('No badge configured. Call setBadge first.');
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
this.currentBadge.number = number;
|
|
353
|
+
this.updateFaviconDisplay();
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Remove badge
|
|
357
|
+
*/
|
|
358
|
+
removeBadge() {
|
|
359
|
+
this.currentBadge = null;
|
|
360
|
+
this.updateFaviconDisplay();
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Update favicon display (with badge if configured)
|
|
364
|
+
*/
|
|
365
|
+
async updateFaviconDisplay() {
|
|
366
|
+
if (!this.currentFavicon)
|
|
367
|
+
return;
|
|
368
|
+
try {
|
|
369
|
+
let displayUrl = this.currentFavicon.url;
|
|
370
|
+
// If badge is configured, render it on canvas
|
|
371
|
+
if (this.currentBadge) {
|
|
372
|
+
const canvas = createCanvas(this.faviconSize);
|
|
373
|
+
const ctx = canvas.getContext('2d');
|
|
374
|
+
if (!ctx) {
|
|
375
|
+
throw new Error('Failed to get canvas context');
|
|
376
|
+
}
|
|
377
|
+
// Load and draw favicon image
|
|
378
|
+
const img = await loadImage(this.currentFavicon.url);
|
|
379
|
+
ctx.drawImage(img, 0, 0, this.faviconSize, this.faviconSize);
|
|
380
|
+
// Draw badge on top
|
|
381
|
+
BadgeRenderer.drawBadge(ctx, this.currentBadge, this.faviconSize);
|
|
382
|
+
// Convert to data URL
|
|
383
|
+
displayUrl = canvasToDataUrl(canvas);
|
|
384
|
+
}
|
|
385
|
+
// Update favicon link
|
|
386
|
+
this.faviconLink.href = displayUrl;
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
console.error('Error updating favicon display:', error);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Pause animation
|
|
394
|
+
*/
|
|
395
|
+
pause() {
|
|
396
|
+
this.isAnimating = false;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Resume animation
|
|
400
|
+
*/
|
|
401
|
+
resume() {
|
|
402
|
+
if (this.isVisible) {
|
|
403
|
+
this.isAnimating = true;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Get current favicon data
|
|
408
|
+
*/
|
|
409
|
+
getFavicon() {
|
|
410
|
+
return this.currentFavicon;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Get current badge config
|
|
414
|
+
*/
|
|
415
|
+
getBadge() {
|
|
416
|
+
return this.currentBadge;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Check if currently animating
|
|
420
|
+
*/
|
|
421
|
+
getIsAnimating() {
|
|
422
|
+
return this.isAnimating;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Cleanup
|
|
426
|
+
*/
|
|
427
|
+
destroy() {
|
|
428
|
+
this.pause();
|
|
429
|
+
if (this.unsubscribeVisibility) {
|
|
430
|
+
this.unsubscribeVisibility();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export { BadgeRenderer, FaviconAnimator, VisibilityManager, canvasToDataUrl, createCanvas, debounce, detectImageFormat, getFaviconLink, getVisibilityManager, isBrowser, loadImage, supportsPageVisibility, throttle };
|
|
436
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/badge.ts","../src/visibility.ts","../src/utils.ts","../src/animator.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Badge position options
|
|
3
|
+
*/
|
|
4
|
+
export type BadgePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center';
|
|
5
|
+
/**
|
|
6
|
+
* Badge configuration
|
|
7
|
+
*/
|
|
8
|
+
export interface BadgeConfig {
|
|
9
|
+
/** Number to display in the badge */
|
|
10
|
+
number: number | string;
|
|
11
|
+
/** Position of the badge */
|
|
12
|
+
position: BadgePosition;
|
|
13
|
+
/** Background color of the badge circle */
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
/** Text color of the badge number */
|
|
16
|
+
textColor?: string;
|
|
17
|
+
/** Size of the badge circle in pixels */
|
|
18
|
+
size?: number;
|
|
19
|
+
/** Font size of the badge number */
|
|
20
|
+
fontSize?: number;
|
|
21
|
+
/** Border radius (usually '50%' for circle) */
|
|
22
|
+
borderRadius?: string;
|
|
23
|
+
/** Offset from the edge in pixels */
|
|
24
|
+
offset?: number;
|
|
25
|
+
/** Font weight */
|
|
26
|
+
fontWeight?: string | number;
|
|
27
|
+
/** Font family */
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Favicon configuration
|
|
32
|
+
*/
|
|
33
|
+
export interface FaviconConfig {
|
|
34
|
+
/** URL or data URL of the favicon */
|
|
35
|
+
url: string;
|
|
36
|
+
/** Size of the favicon in pixels (default: 32) */
|
|
37
|
+
size?: number;
|
|
38
|
+
/** Whether to use the favicon as-is or process it */
|
|
39
|
+
raw?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Animator options
|
|
43
|
+
*/
|
|
44
|
+
export interface AnimatorOptions {
|
|
45
|
+
/** Initial favicon URL or config */
|
|
46
|
+
favicon?: FaviconConfig | string;
|
|
47
|
+
/** Initial badge configuration */
|
|
48
|
+
badge?: BadgeConfig;
|
|
49
|
+
/** Whether to pause animation when tab is not visible */
|
|
50
|
+
pauseOnHidden?: boolean;
|
|
51
|
+
/** Update interval for badge changes (ms) */
|
|
52
|
+
updateInterval?: number;
|
|
53
|
+
/** Custom favicon link element selector */
|
|
54
|
+
linkSelector?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Page visibility state
|
|
58
|
+
*/
|
|
59
|
+
export interface VisibilityState {
|
|
60
|
+
isVisible: boolean;
|
|
61
|
+
wasVisible: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Favicon data for rendering
|
|
65
|
+
*/
|
|
66
|
+
export interface FaviconData {
|
|
67
|
+
url: string;
|
|
68
|
+
size: number;
|
|
69
|
+
isAnimated: boolean;
|
|
70
|
+
format: 'gif' | 'webp' | 'png' | 'ico' | 'svg' | 'unknown';
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEjG;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,4BAA4B;IAC5B,QAAQ,EAAE,aAAa,CAAC;IACxB,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IACjC,kCAAkC;IAClC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;CAC5D"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Get or create favicon link element
|
|
6
|
+
*/
|
|
7
|
+
export declare function getFaviconLink(): HTMLLinkElement;
|
|
8
|
+
/**
|
|
9
|
+
* Create canvas element
|
|
10
|
+
*/
|
|
11
|
+
export declare function createCanvas(size: number): HTMLCanvasElement;
|
|
12
|
+
/**
|
|
13
|
+
* Load image from URL
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadImage(url: string): Promise<HTMLImageElement>;
|
|
16
|
+
/**
|
|
17
|
+
* Detect image format from URL or data URL
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectImageFormat(url: string): 'gif' | 'webp' | 'png' | 'ico' | 'svg' | 'unknown';
|
|
20
|
+
/**
|
|
21
|
+
* Check if browser supports Page Visibility API
|
|
22
|
+
*/
|
|
23
|
+
export declare function supportsPageVisibility(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Debounce function
|
|
26
|
+
*/
|
|
27
|
+
export declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Throttle function
|
|
30
|
+
*/
|
|
31
|
+
export declare function throttle<T extends (...args: any[]) => any>(func: T, limit: number): (...args: Parameters<T>) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Convert canvas to data URL
|
|
34
|
+
*/
|
|
35
|
+
export declare function canvasToDataUrl(canvas: HTMLCanvasElement): string;
|
|
36
|
+
/**
|
|
37
|
+
* Check if running in browser environment
|
|
38
|
+
*/
|
|
39
|
+
export declare function isBrowser(): boolean;
|
|
40
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,cAAc,IAAI,eAAe,CAWhD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAK5D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAOhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAiBjG;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAYlC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,MAAM,GACZ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAUlC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAEjE;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Visibility API wrapper for managing favicon animations
|
|
3
|
+
* when the tab is not in focus
|
|
4
|
+
*/
|
|
5
|
+
export type VisibilityChangeCallback = (isVisible: boolean) => void;
|
|
6
|
+
export declare class VisibilityManager {
|
|
7
|
+
private isVisible;
|
|
8
|
+
private callbacks;
|
|
9
|
+
private boundHandleVisibilityChange;
|
|
10
|
+
constructor();
|
|
11
|
+
private init;
|
|
12
|
+
private handleVisibilityChange;
|
|
13
|
+
private notifyCallbacks;
|
|
14
|
+
/**
|
|
15
|
+
* Subscribe to visibility changes
|
|
16
|
+
*/
|
|
17
|
+
onChange(callback: VisibilityChangeCallback): () => void;
|
|
18
|
+
/**
|
|
19
|
+
* Get current visibility state
|
|
20
|
+
*/
|
|
21
|
+
getIsVisible(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Cleanup
|
|
24
|
+
*/
|
|
25
|
+
destroy(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function getVisibilityManager(): VisibilityManager;
|
|
28
|
+
//# sourceMappingURL=visibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../src/visibility.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;AAEpE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,2BAA2B,CAAa;;IAOhD,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IASxD;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB;AAKD,wBAAgB,oBAAoB,IAAI,iBAAiB,CAKxD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "favicon-animate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight favicon animation library with badge support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "rollup -c",
|
|
20
|
+
"dev": "rollup -c -w",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"lint": "eslint src",
|
|
23
|
+
"format": "prettier --write src"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"favicon",
|
|
27
|
+
"animate",
|
|
28
|
+
"dynamic",
|
|
29
|
+
"badge",
|
|
30
|
+
"notification"
|
|
31
|
+
],
|
|
32
|
+
"author": "Daniel Belintani <daniel.belintani@hotmail.com>",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
36
|
+
"@types/node": "^20.0.0",
|
|
37
|
+
"rollup": "^4.0.0",
|
|
38
|
+
"typescript": "^5.3.0",
|
|
39
|
+
"vitest": "^1.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|