@autumnsgrove/gossamer 0.0.1 → 0.1.1

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.
Files changed (53) hide show
  1. package/dist/animation.d.ts +80 -0
  2. package/dist/animation.d.ts.map +1 -0
  3. package/dist/characters.d.ts +49 -0
  4. package/dist/characters.d.ts.map +1 -0
  5. package/dist/colors.d.ts +312 -0
  6. package/dist/colors.d.ts.map +1 -0
  7. package/dist/index.d.ts +39 -11
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1826 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/patterns.d.ts +217 -0
  12. package/dist/patterns.d.ts.map +1 -0
  13. package/dist/renderer.d.ts +140 -0
  14. package/dist/renderer.d.ts.map +1 -0
  15. package/dist/style.css +124 -0
  16. package/dist/svelte/GossamerBorder.svelte.d.ts +1 -0
  17. package/dist/svelte/GossamerClouds.svelte.d.ts +1 -0
  18. package/dist/svelte/GossamerImage.svelte.d.ts +1 -0
  19. package/dist/svelte/GossamerOverlay.svelte.d.ts +1 -0
  20. package/dist/svelte/GossamerText.svelte.d.ts +1 -0
  21. package/dist/svelte/index.d.ts +20 -0
  22. package/dist/svelte/index.d.ts.map +1 -0
  23. package/dist/svelte/index.js +3648 -0
  24. package/dist/svelte/index.js.map +1 -0
  25. package/dist/svelte/presets.d.ts +38 -0
  26. package/dist/svelte/presets.d.ts.map +1 -0
  27. package/dist/utils/canvas.d.ts +73 -0
  28. package/dist/utils/canvas.d.ts.map +1 -0
  29. package/dist/utils/image.d.ts +74 -0
  30. package/dist/utils/image.d.ts.map +1 -0
  31. package/dist/utils/performance.d.ts +86 -0
  32. package/dist/utils/performance.d.ts.map +1 -0
  33. package/package.json +34 -7
  34. package/src/animation.test.ts +254 -0
  35. package/src/animation.ts +243 -0
  36. package/src/characters.test.ts +148 -0
  37. package/src/characters.ts +219 -0
  38. package/src/colors.ts +234 -0
  39. package/src/index.test.ts +115 -0
  40. package/src/index.ts +164 -11
  41. package/src/patterns.test.ts +273 -0
  42. package/src/patterns.ts +760 -0
  43. package/src/renderer.ts +470 -0
  44. package/src/svelte/GossamerBorder.svelte +326 -0
  45. package/src/svelte/GossamerClouds.svelte +269 -0
  46. package/src/svelte/GossamerImage.svelte +266 -0
  47. package/src/svelte/GossamerOverlay.svelte +232 -0
  48. package/src/svelte/GossamerText.svelte +239 -0
  49. package/src/svelte/index.ts +75 -0
  50. package/src/svelte/presets.ts +174 -0
  51. package/src/utils/canvas.ts +210 -0
  52. package/src/utils/image.ts +275 -0
  53. 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,16 +1,24 @@
1
1
  {
2
2
  "name": "@autumnsgrove/gossamer",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
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"
13
- }
14
+ },
15
+ "./svelte": {
16
+ "types": "./dist/svelte/index.d.ts",
17
+ "svelte": "./dist/svelte/index.js",
18
+ "import": "./dist/svelte/index.js"
19
+ },
20
+ "./svelte/style.css": "./dist/style.css",
21
+ "./style.css": "./dist/style.css"
14
22
  },
15
23
  "files": [
16
24
  "dist",
@@ -18,11 +26,15 @@
18
26
  ],
19
27
  "scripts": {
20
28
  "dev": "vite build --watch",
21
- "build": "tsc && vite build",
29
+ "build": "vite build",
22
30
  "test": "vitest run",
23
31
  "test:watch": "vitest",
24
32
  "test:coverage": "vitest run --coverage",
25
- "clean": "rm -rf dist"
33
+ "clean": "rm -rf dist",
34
+ "prepublishOnly": "pnpm run clean && pnpm run build",
35
+ "export-gif": "tsx scripts/export-gif.ts",
36
+ "export-gif:all": "tsx scripts/export-gif.ts --all",
37
+ "export-gif:random": "tsx scripts/export-gif.ts --random"
26
38
  },
27
39
  "keywords": [
28
40
  "ascii",
@@ -34,7 +46,9 @@
34
46
  "text-art",
35
47
  "ambient",
36
48
  "texture",
37
- "pattern"
49
+ "pattern",
50
+ "svelte",
51
+ "svelte-5"
38
52
  ],
39
53
  "author": "AutumnsGrove",
40
54
  "license": "MIT",
@@ -44,11 +58,24 @@
44
58
  },
45
59
  "repository": {
46
60
  "type": "git",
47
- "url": "https://github.com/AutumnsGrove/Gossamer.git",
48
- "directory": "packages/core"
61
+ "url": "https://github.com/AutumnsGrove/Gossamer.git"
62
+ },
63
+ "peerDependencies": {
64
+ "svelte": "^5.0.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "svelte": {
68
+ "optional": true
69
+ }
49
70
  },
50
71
  "devDependencies": {
72
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
51
73
  "@types/node": "^20.10.0",
74
+ "canvas": "^3.2.1",
75
+ "gif-encoder-2": "^1.0.5",
76
+ "jsdom": "^27.4.0",
77
+ "svelte": "^5.0.0",
78
+ "tsx": "^4.21.0",
52
79
  "typescript": "^5.3.0",
53
80
  "vite": "^5.0.0",
54
81
  "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
+ });