@autumnsgrove/gossamer 0.0.1 → 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/animation.d.ts +80 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/characters.d.ts +49 -0
- package/dist/characters.d.ts.map +1 -0
- package/dist/index.d.ts +37 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1284 -2
- package/dist/index.js.map +1 -1
- package/dist/patterns.d.ts +100 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/renderer.d.ts +113 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/style.css +124 -0
- package/dist/svelte/GossamerBorder.svelte.d.ts +1 -0
- package/dist/svelte/GossamerClouds.svelte.d.ts +1 -0
- package/dist/svelte/GossamerImage.svelte.d.ts +1 -0
- package/dist/svelte/GossamerOverlay.svelte.d.ts +1 -0
- package/dist/svelte/GossamerText.svelte.d.ts +1 -0
- package/dist/svelte/index.d.ts +20 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +3651 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/svelte/presets.d.ts +38 -0
- package/dist/svelte/presets.d.ts.map +1 -0
- package/dist/utils/canvas.d.ts +73 -0
- package/dist/utils/canvas.d.ts.map +1 -0
- package/dist/utils/image.d.ts +74 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/performance.d.ts +86 -0
- package/dist/utils/performance.d.ts.map +1 -0
- package/package.json +23 -5
- package/src/animation.test.ts +254 -0
- package/src/animation.ts +243 -0
- package/src/characters.test.ts +148 -0
- package/src/characters.ts +164 -0
- package/src/index.test.ts +115 -0
- package/src/index.ts +133 -11
- package/src/patterns.test.ts +273 -0
- package/src/patterns.ts +316 -0
- package/src/renderer.ts +309 -0
- package/src/svelte/GossamerBorder.svelte +326 -0
- package/src/svelte/GossamerClouds.svelte +269 -0
- package/src/svelte/GossamerImage.svelte +266 -0
- package/src/svelte/GossamerOverlay.svelte +232 -0
- package/src/svelte/GossamerText.svelte +239 -0
- package/src/svelte/index.ts +75 -0
- package/src/svelte/presets.ts +174 -0
- package/src/utils/canvas.ts +210 -0
- package/src/utils/image.ts +275 -0
- package/src/utils/performance.ts +282 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PresetConfig } from '../index';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Grove-themed presets
|
|
5
|
+
* Organic, nature-inspired effects
|
|
6
|
+
*/
|
|
7
|
+
export declare const grovePresets: Record<string, PresetConfig>;
|
|
8
|
+
/**
|
|
9
|
+
* Seasonal presets
|
|
10
|
+
* Effects themed around the four seasons
|
|
11
|
+
*/
|
|
12
|
+
export declare const seasonalPresets: Record<string, PresetConfig>;
|
|
13
|
+
/**
|
|
14
|
+
* Ambient presets
|
|
15
|
+
* Subtle background textures
|
|
16
|
+
*/
|
|
17
|
+
export declare const ambientPresets: Record<string, PresetConfig>;
|
|
18
|
+
/**
|
|
19
|
+
* All presets combined for easy access
|
|
20
|
+
*/
|
|
21
|
+
export declare const PRESETS: Record<string, PresetConfig>;
|
|
22
|
+
/**
|
|
23
|
+
* Get a preset by name
|
|
24
|
+
*/
|
|
25
|
+
export declare function getPreset(name: string): PresetConfig | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* List all available preset names
|
|
28
|
+
*/
|
|
29
|
+
export declare function getPresetNames(): string[];
|
|
30
|
+
/**
|
|
31
|
+
* List preset names by category
|
|
32
|
+
*/
|
|
33
|
+
export declare function getPresetsByCategory(): {
|
|
34
|
+
grove: string[];
|
|
35
|
+
seasonal: string[];
|
|
36
|
+
ambient: string[];
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/svelte/presets.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAyCrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAyCxD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA+BvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAIhD,CAAC;AAEF;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEhE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI;IACtC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAMA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for canvas creation, setup, and manipulation.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Options for canvas creation
|
|
8
|
+
*/
|
|
9
|
+
export interface CanvasOptions {
|
|
10
|
+
/** Canvas width in pixels */
|
|
11
|
+
width?: number;
|
|
12
|
+
/** Canvas height in pixels */
|
|
13
|
+
height?: number;
|
|
14
|
+
/** Whether to use high DPI scaling */
|
|
15
|
+
highDPI?: boolean;
|
|
16
|
+
/** CSS class to add to canvas */
|
|
17
|
+
className?: string;
|
|
18
|
+
/** Inline styles to apply */
|
|
19
|
+
style?: Partial<CSSStyleDeclaration>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a canvas element with optimal settings
|
|
23
|
+
*/
|
|
24
|
+
export declare function createCanvas(options?: CanvasOptions): HTMLCanvasElement;
|
|
25
|
+
/**
|
|
26
|
+
* Get the device pixel ratio
|
|
27
|
+
*/
|
|
28
|
+
export declare function getDevicePixelRatio(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Resize canvas to match container dimensions
|
|
31
|
+
*/
|
|
32
|
+
export declare function resizeCanvasToContainer(canvas: HTMLCanvasElement, container: HTMLElement, highDPI?: boolean): {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Create an offscreen canvas for buffer rendering
|
|
38
|
+
*/
|
|
39
|
+
export declare function createOffscreenCanvas(width: number, height: number): HTMLCanvasElement | OffscreenCanvas;
|
|
40
|
+
/**
|
|
41
|
+
* Clear a canvas
|
|
42
|
+
*/
|
|
43
|
+
export declare function clearCanvas(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, width: number, height: number, backgroundColor?: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get image data from a canvas region
|
|
46
|
+
*/
|
|
47
|
+
export declare function getImageData(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, x?: number, y?: number, width?: number, height?: number): ImageData;
|
|
48
|
+
/**
|
|
49
|
+
* Set optimal rendering context settings
|
|
50
|
+
*/
|
|
51
|
+
export declare function optimizeContext(ctx: CanvasRenderingContext2D): void;
|
|
52
|
+
/**
|
|
53
|
+
* Set text rendering options for ASCII display
|
|
54
|
+
*/
|
|
55
|
+
export declare function setupTextRendering(ctx: CanvasRenderingContext2D, fontSize: number, fontFamily?: string, color?: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Measure text width for a given font configuration
|
|
58
|
+
*/
|
|
59
|
+
export declare function measureTextWidth(ctx: CanvasRenderingContext2D, text: string, fontSize: number, fontFamily?: string): number;
|
|
60
|
+
/**
|
|
61
|
+
* Calculate optimal cell size for a given canvas and desired columns
|
|
62
|
+
*/
|
|
63
|
+
export declare function calculateCellSize(canvasWidth: number, canvasHeight: number, targetCols: number): {
|
|
64
|
+
cellWidth: number;
|
|
65
|
+
cellHeight: number;
|
|
66
|
+
cols: number;
|
|
67
|
+
rows: number;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Apply a CSS blend mode to canvas compositing
|
|
71
|
+
*/
|
|
72
|
+
export declare function setBlendMode(ctx: CanvasRenderingContext2D, mode: 'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn' | 'soft-light' | 'hard-light' | 'difference' | 'exclusion'): void;
|
|
73
|
+
//# sourceMappingURL=canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/utils/canvas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,aAAkB,GAAG,iBAAiB,CA8B3E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,WAAW,EACtB,OAAO,GAAE,OAAc,GACtB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAoBnC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,eAAe,CAUxG;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,wBAAwB,GAAG,iCAAiC,EACjE,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,MAAM,GACvB,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,wBAAwB,GAAG,iCAAiC,EACjE,CAAC,GAAE,MAAU,EACb,CAAC,GAAE,MAAU,EACb,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,SAAS,CAMX;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI,CAMnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,wBAAwB,EAC7B,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAAoB,EAChC,KAAK,GAAE,MAAkB,GACxB,IAAI,CAKN;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAAoB,GAC/B,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAQvE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,WAAW,GAClK,IAAI,CAEN"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Utilities
|
|
3
|
+
*
|
|
4
|
+
* Image loading, processing, and pixel manipulation for ASCII conversion.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Image loading options
|
|
8
|
+
*/
|
|
9
|
+
export interface ImageLoadOptions {
|
|
10
|
+
/** Cross-origin setting for external images */
|
|
11
|
+
crossOrigin?: 'anonymous' | 'use-credentials' | '';
|
|
12
|
+
/** Maximum width to scale image to */
|
|
13
|
+
maxWidth?: number;
|
|
14
|
+
/** Maximum height to scale image to */
|
|
15
|
+
maxHeight?: number;
|
|
16
|
+
/** Whether to preserve aspect ratio when scaling */
|
|
17
|
+
preserveAspectRatio?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load an image from a URL
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadImage(src: string, options?: ImageLoadOptions): Promise<HTMLImageElement>;
|
|
23
|
+
/**
|
|
24
|
+
* Load and scale an image to fit within bounds
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadAndScaleImage(src: string, maxWidth: number, maxHeight: number, options?: ImageLoadOptions): Promise<{
|
|
27
|
+
image: HTMLImageElement;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Draw an image to a canvas and get its pixel data
|
|
33
|
+
*/
|
|
34
|
+
export declare function imageToPixelData(image: HTMLImageElement | HTMLCanvasElement | ImageBitmap, width?: number, height?: number): ImageData;
|
|
35
|
+
/**
|
|
36
|
+
* Extract brightness values from image data
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractBrightness(imageData: ImageData, brightnessFunction?: (r: number, g: number, b: number) => number): number[];
|
|
39
|
+
/**
|
|
40
|
+
* Sample image data at cell-based intervals
|
|
41
|
+
*/
|
|
42
|
+
export declare function sampleImageCells(imageData: ImageData, cellWidth: number, cellHeight: number, brightnessFunction?: (r: number, g: number, b: number) => number): {
|
|
43
|
+
brightness: number;
|
|
44
|
+
color: string;
|
|
45
|
+
}[][];
|
|
46
|
+
/**
|
|
47
|
+
* Convert RGB to hex color string
|
|
48
|
+
*/
|
|
49
|
+
export declare function rgbToHex(r: number, g: number, b: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Convert hex color to RGB
|
|
52
|
+
*/
|
|
53
|
+
export declare function hexToRgb(hex: string): {
|
|
54
|
+
r: number;
|
|
55
|
+
g: number;
|
|
56
|
+
b: number;
|
|
57
|
+
} | null;
|
|
58
|
+
/**
|
|
59
|
+
* Adjust image brightness
|
|
60
|
+
*/
|
|
61
|
+
export declare function adjustBrightness(imageData: ImageData, amount: number): ImageData;
|
|
62
|
+
/**
|
|
63
|
+
* Adjust image contrast
|
|
64
|
+
*/
|
|
65
|
+
export declare function adjustContrast(imageData: ImageData, amount: number): ImageData;
|
|
66
|
+
/**
|
|
67
|
+
* Invert image colors
|
|
68
|
+
*/
|
|
69
|
+
export declare function invertColors(imageData: ImageData): ImageData;
|
|
70
|
+
/**
|
|
71
|
+
* Convert image to grayscale
|
|
72
|
+
*/
|
|
73
|
+
export declare function toGrayscale(imageData: ImageData): ImageData;
|
|
74
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,GAAG,EAAE,CAAC;IACnD,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAahG;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,WAAW,EACzD,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,SAAS,CAeX;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,EACpB,kBAAkB,GAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAoD,GAC5G,MAAM,EAAE,CASV;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,kBAAkB,GAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAoD,GAC5G;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,CAsB3C;AA6CD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAShF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAYhF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAa9E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAY5D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAa3D"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Utilities
|
|
3
|
+
*
|
|
4
|
+
* Visibility detection, resource management, and optimization helpers.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Visibility observer callback type
|
|
8
|
+
*/
|
|
9
|
+
export type VisibilityCallback = (isVisible: boolean, entry: IntersectionObserverEntry) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Create an IntersectionObserver for visibility-based animation control
|
|
12
|
+
*
|
|
13
|
+
* @param element - Element to observe
|
|
14
|
+
* @param callback - Called when visibility changes
|
|
15
|
+
* @param threshold - Visibility threshold (0-1, default: 0.1)
|
|
16
|
+
* @returns Cleanup function to disconnect observer
|
|
17
|
+
*/
|
|
18
|
+
export declare function createVisibilityObserver(element: Element, callback: VisibilityCallback, threshold?: number): () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Create a ResizeObserver for responsive canvas sizing
|
|
21
|
+
*
|
|
22
|
+
* @param element - Element to observe
|
|
23
|
+
* @param callback - Called on resize with new dimensions
|
|
24
|
+
* @param debounceMs - Debounce delay in ms (default: 100)
|
|
25
|
+
* @returns Cleanup function to disconnect observer
|
|
26
|
+
*/
|
|
27
|
+
export declare function createResizeObserver(element: Element, callback: (width: number, height: number, entry: ResizeObserverEntry) => void, debounceMs?: number): () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if the user prefers reduced motion
|
|
30
|
+
*/
|
|
31
|
+
export declare function prefersReducedMotion(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Create a listener for reduced motion preference changes
|
|
34
|
+
*
|
|
35
|
+
* @param callback - Called when preference changes
|
|
36
|
+
* @returns Cleanup function to remove listener
|
|
37
|
+
*/
|
|
38
|
+
export declare function onReducedMotionChange(callback: (prefersReduced: boolean) => void): () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Check if the browser is in a low-power mode or has reduced capabilities
|
|
41
|
+
*/
|
|
42
|
+
export declare function isLowPowerMode(): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Get recommended FPS based on device capabilities
|
|
45
|
+
*/
|
|
46
|
+
export declare function getRecommendedFPS(): number;
|
|
47
|
+
/**
|
|
48
|
+
* Performance monitoring for debugging
|
|
49
|
+
*/
|
|
50
|
+
export interface PerformanceMetrics {
|
|
51
|
+
fps: number;
|
|
52
|
+
frameTime: number;
|
|
53
|
+
droppedFrames: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a simple FPS counter
|
|
57
|
+
*/
|
|
58
|
+
export declare function createFPSCounter(): {
|
|
59
|
+
tick: () => void;
|
|
60
|
+
getFPS: () => number;
|
|
61
|
+
getMetrics: () => PerformanceMetrics;
|
|
62
|
+
reset: () => void;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Request idle callback with fallback
|
|
66
|
+
*/
|
|
67
|
+
export declare function requestIdleCallback(callback: () => void, options?: {
|
|
68
|
+
timeout?: number;
|
|
69
|
+
}): number;
|
|
70
|
+
/**
|
|
71
|
+
* Cancel idle callback with fallback
|
|
72
|
+
*/
|
|
73
|
+
export declare function cancelIdleCallback(id: number): void;
|
|
74
|
+
/**
|
|
75
|
+
* Check if we're running in a browser environment
|
|
76
|
+
*/
|
|
77
|
+
export declare function isBrowser(): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Check if Canvas is supported
|
|
80
|
+
*/
|
|
81
|
+
export declare function isCanvasSupported(): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Check if OffscreenCanvas is supported
|
|
84
|
+
*/
|
|
85
|
+
export declare function isOffscreenCanvasSupported(): boolean;
|
|
86
|
+
//# sourceMappingURL=performance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/utils/performance.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,yBAAyB,KAAK,IAAI,CAAC;AAEhG;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,GAAE,MAAY,GACtB,MAAM,IAAI,CAeZ;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,KAAK,IAAI,EAC7E,UAAU,GAAE,MAAY,GACvB,MAAM,IAAI,CAyBZ;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,CAAC,cAAc,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAmB7F;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAcxC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI;IAClC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,EAAE,MAAM,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,kBAAkB,CAAC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAqDA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,IAAI,EACpB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,MAAM,CAaR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAYnD;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAK3C;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autumnsgrove/gossamer",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "ASCII visual effects library - Threads of light for your web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"svelte": "./dist/svelte/index.js",
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
12
|
"types": "./dist/index.d.ts",
|
|
12
13
|
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./svelte": {
|
|
16
|
+
"types": "./dist/svelte/index.d.ts",
|
|
17
|
+
"svelte": "./dist/svelte/index.js",
|
|
18
|
+
"import": "./dist/svelte/index.js"
|
|
13
19
|
}
|
|
14
20
|
},
|
|
15
21
|
"files": [
|
|
@@ -18,7 +24,7 @@
|
|
|
18
24
|
],
|
|
19
25
|
"scripts": {
|
|
20
26
|
"dev": "vite build --watch",
|
|
21
|
-
"build": "
|
|
27
|
+
"build": "vite build",
|
|
22
28
|
"test": "vitest run",
|
|
23
29
|
"test:watch": "vitest",
|
|
24
30
|
"test:coverage": "vitest run --coverage",
|
|
@@ -34,7 +40,9 @@
|
|
|
34
40
|
"text-art",
|
|
35
41
|
"ambient",
|
|
36
42
|
"texture",
|
|
37
|
-
"pattern"
|
|
43
|
+
"pattern",
|
|
44
|
+
"svelte",
|
|
45
|
+
"svelte-5"
|
|
38
46
|
],
|
|
39
47
|
"author": "AutumnsGrove",
|
|
40
48
|
"license": "MIT",
|
|
@@ -44,11 +52,21 @@
|
|
|
44
52
|
},
|
|
45
53
|
"repository": {
|
|
46
54
|
"type": "git",
|
|
47
|
-
"url": "https://github.com/AutumnsGrove/Gossamer.git"
|
|
48
|
-
|
|
55
|
+
"url": "https://github.com/AutumnsGrove/Gossamer.git"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"svelte": "^5.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"svelte": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
49
64
|
},
|
|
50
65
|
"devDependencies": {
|
|
66
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
51
67
|
"@types/node": "^20.10.0",
|
|
68
|
+
"jsdom": "^27.4.0",
|
|
69
|
+
"svelte": "^5.0.0",
|
|
52
70
|
"typescript": "^5.3.0",
|
|
53
71
|
"vite": "^5.0.0",
|
|
54
72
|
"vite-plugin-dts": "^3.7.0",
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for animation utilities
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { throttle, debounce, calculateFPS, easings, createAnimationLoop } from './animation';
|
|
6
|
+
|
|
7
|
+
describe('throttle', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
vi.useFakeTimers();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should call function immediately on first call', () => {
|
|
17
|
+
const fn = vi.fn();
|
|
18
|
+
const throttled = throttle(fn, 100);
|
|
19
|
+
|
|
20
|
+
throttled();
|
|
21
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should throttle subsequent calls within limit', () => {
|
|
25
|
+
const fn = vi.fn();
|
|
26
|
+
const throttled = throttle(fn, 100);
|
|
27
|
+
|
|
28
|
+
throttled();
|
|
29
|
+
throttled();
|
|
30
|
+
throttled();
|
|
31
|
+
|
|
32
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should call function again after limit expires', () => {
|
|
36
|
+
const fn = vi.fn();
|
|
37
|
+
const throttled = throttle(fn, 100);
|
|
38
|
+
|
|
39
|
+
throttled();
|
|
40
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
41
|
+
|
|
42
|
+
vi.advanceTimersByTime(150);
|
|
43
|
+
throttled();
|
|
44
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should pass arguments to throttled function', () => {
|
|
48
|
+
const fn = vi.fn();
|
|
49
|
+
const throttled = throttle(fn, 100);
|
|
50
|
+
|
|
51
|
+
throttled('arg1', 'arg2');
|
|
52
|
+
expect(fn).toHaveBeenCalledWith('arg1', 'arg2');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('debounce', () => {
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
vi.useFakeTimers();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
vi.useRealTimers();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should not call function immediately', () => {
|
|
66
|
+
const fn = vi.fn();
|
|
67
|
+
const debounced = debounce(fn, 100);
|
|
68
|
+
|
|
69
|
+
debounced();
|
|
70
|
+
expect(fn).not.toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should call function after delay', () => {
|
|
74
|
+
const fn = vi.fn();
|
|
75
|
+
const debounced = debounce(fn, 100);
|
|
76
|
+
|
|
77
|
+
debounced();
|
|
78
|
+
vi.advanceTimersByTime(100);
|
|
79
|
+
|
|
80
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should reset timer on subsequent calls', () => {
|
|
84
|
+
const fn = vi.fn();
|
|
85
|
+
const debounced = debounce(fn, 100);
|
|
86
|
+
|
|
87
|
+
debounced();
|
|
88
|
+
vi.advanceTimersByTime(50);
|
|
89
|
+
debounced();
|
|
90
|
+
vi.advanceTimersByTime(50);
|
|
91
|
+
|
|
92
|
+
expect(fn).not.toHaveBeenCalled();
|
|
93
|
+
|
|
94
|
+
vi.advanceTimersByTime(50);
|
|
95
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should pass arguments to debounced function', () => {
|
|
99
|
+
const fn = vi.fn();
|
|
100
|
+
const debounced = debounce(fn, 100);
|
|
101
|
+
|
|
102
|
+
debounced('test', 123);
|
|
103
|
+
vi.advanceTimersByTime(100);
|
|
104
|
+
|
|
105
|
+
expect(fn).toHaveBeenCalledWith('test', 123);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('calculateFPS', () => {
|
|
110
|
+
it('should return 0 for empty array', () => {
|
|
111
|
+
expect(calculateFPS([])).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should return 0 for single frame time', () => {
|
|
115
|
+
expect(calculateFPS([1000])).toBe(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should calculate FPS correctly', () => {
|
|
119
|
+
// 60 frames over 1 second = 60 FPS
|
|
120
|
+
const frameTimes = Array.from({ length: 61 }, (_, i) => i * (1000 / 60));
|
|
121
|
+
const fps = calculateFPS(frameTimes);
|
|
122
|
+
|
|
123
|
+
expect(fps).toBeCloseTo(60, 0);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should handle 30 FPS', () => {
|
|
127
|
+
// 30 frames over 1 second = 30 FPS
|
|
128
|
+
const frameTimes = Array.from({ length: 31 }, (_, i) => i * (1000 / 30));
|
|
129
|
+
const fps = calculateFPS(frameTimes);
|
|
130
|
+
|
|
131
|
+
expect(fps).toBeCloseTo(30, 0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should use sample size parameter', () => {
|
|
135
|
+
// Create 100 frame times
|
|
136
|
+
const frameTimes = Array.from({ length: 100 }, (_, i) => i * 16.67);
|
|
137
|
+
|
|
138
|
+
// Should only use last 10 samples
|
|
139
|
+
const fps = calculateFPS(frameTimes, 10);
|
|
140
|
+
expect(fps).toBeCloseTo(60, 0);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('easings', () => {
|
|
145
|
+
describe('linear', () => {
|
|
146
|
+
it('should return input unchanged', () => {
|
|
147
|
+
expect(easings.linear(0)).toBe(0);
|
|
148
|
+
expect(easings.linear(0.5)).toBe(0.5);
|
|
149
|
+
expect(easings.linear(1)).toBe(1);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('easeIn', () => {
|
|
154
|
+
it('should start slow', () => {
|
|
155
|
+
expect(easings.easeIn(0)).toBe(0);
|
|
156
|
+
expect(easings.easeIn(0.5)).toBe(0.25);
|
|
157
|
+
expect(easings.easeIn(1)).toBe(1);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('easeOut', () => {
|
|
162
|
+
it('should end slow', () => {
|
|
163
|
+
expect(easings.easeOut(0)).toBe(0);
|
|
164
|
+
expect(easings.easeOut(0.5)).toBe(0.75);
|
|
165
|
+
expect(easings.easeOut(1)).toBe(1);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('easeInOut', () => {
|
|
170
|
+
it('should start and end slow', () => {
|
|
171
|
+
expect(easings.easeInOut(0)).toBe(0);
|
|
172
|
+
expect(easings.easeInOut(0.5)).toBe(0.5);
|
|
173
|
+
expect(easings.easeInOut(1)).toBe(1);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should be symmetric around 0.5', () => {
|
|
177
|
+
const val1 = easings.easeInOut(0.25);
|
|
178
|
+
const val2 = 1 - easings.easeInOut(0.75);
|
|
179
|
+
expect(val1).toBeCloseTo(val2, 5);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('sineIn', () => {
|
|
184
|
+
it('should return 0 at start and 1 at end', () => {
|
|
185
|
+
expect(easings.sineIn(0)).toBe(0);
|
|
186
|
+
expect(easings.sineIn(1)).toBeCloseTo(1, 5);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('sineOut', () => {
|
|
191
|
+
it('should return 0 at start and 1 at end', () => {
|
|
192
|
+
expect(easings.sineOut(0)).toBe(0);
|
|
193
|
+
expect(easings.sineOut(1)).toBeCloseTo(1, 5);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('sineInOut', () => {
|
|
198
|
+
it('should return 0 at start, 0.5 at middle, 1 at end', () => {
|
|
199
|
+
expect(easings.sineInOut(0)).toBeCloseTo(0, 5);
|
|
200
|
+
expect(easings.sineInOut(0.5)).toBeCloseTo(0.5, 5);
|
|
201
|
+
expect(easings.sineInOut(1)).toBeCloseTo(1, 5);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('bounceOut', () => {
|
|
206
|
+
it('should return 0 at start and 1 at end', () => {
|
|
207
|
+
expect(easings.bounceOut(0)).toBe(0);
|
|
208
|
+
expect(easings.bounceOut(1)).toBeCloseTo(1, 5);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should overshoot intermediate values', () => {
|
|
212
|
+
// bounceOut creates bouncing effect with values close to 1
|
|
213
|
+
const val = easings.bounceOut(0.9);
|
|
214
|
+
expect(val).toBeGreaterThan(0.9);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('createAnimationLoop', () => {
|
|
220
|
+
it('should return control functions', () => {
|
|
221
|
+
const loop = createAnimationLoop({
|
|
222
|
+
onFrame: () => {},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(typeof loop.start).toBe('function');
|
|
226
|
+
expect(typeof loop.stop).toBe('function');
|
|
227
|
+
expect(typeof loop.pause).toBe('function');
|
|
228
|
+
expect(typeof loop.resume).toBe('function');
|
|
229
|
+
expect(typeof loop.getState).toBe('function');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should initialize with correct default state', () => {
|
|
233
|
+
const loop = createAnimationLoop({
|
|
234
|
+
fps: 60,
|
|
235
|
+
onFrame: () => {},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const state = loop.getState();
|
|
239
|
+
expect(state.isRunning).toBe(false);
|
|
240
|
+
expect(state.frameId).toBeNull();
|
|
241
|
+
expect(state.frameInterval).toBeCloseTo(1000 / 60, 1);
|
|
242
|
+
expect(state.elapsedTime).toBe(0);
|
|
243
|
+
expect(state.frameCount).toBe(0);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should use default FPS of 30', () => {
|
|
247
|
+
const loop = createAnimationLoop({
|
|
248
|
+
onFrame: () => {},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const state = loop.getState();
|
|
252
|
+
expect(state.frameInterval).toBeCloseTo(1000 / 30, 1);
|
|
253
|
+
});
|
|
254
|
+
});
|