@happyvertical/images 0.74.8
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/AGENT.md +33 -0
- package/LICENSE +7 -0
- package/dist/adapters/imgproxy.d.ts +176 -0
- package/dist/adapters/imgproxy.d.ts.map +1 -0
- package/dist/adapters/jimp.d.ts +66 -0
- package/dist/adapters/jimp.d.ts.map +1 -0
- package/dist/adapters/sharp.d.ts +56 -0
- package/dist/adapters/sharp.d.ts.map +1 -0
- package/dist/cli/claude-context.d.ts +3 -0
- package/dist/cli/claude-context.d.ts.map +1 -0
- package/dist/cli/claude-context.js +21 -0
- package/dist/cli/claude-context.js.map +1 -0
- package/dist/headline-card.d.ts +136 -0
- package/dist/headline-card.d.ts.map +1 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1302 -0
- package/dist/index.js.map +1 -0
- package/dist/shared/errors.d.ts +50 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/factory.d.ts +42 -0
- package/dist/shared/factory.d.ts.map +1 -0
- package/dist/shared/types.d.ts +170 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/metadata.json +33 -0
- package/package.json +73 -0
package/AGENT.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @happyvertical/images
|
|
2
|
+
|
|
3
|
+
<!-- BEGIN AGENT:GENERATED -->
|
|
4
|
+
## Purpose
|
|
5
|
+
Image processing utilities with adapter pattern for scaling from static to enterprise
|
|
6
|
+
|
|
7
|
+
## Package Map
|
|
8
|
+
- Package: `@happyvertical/images`
|
|
9
|
+
- Hierarchy path: `@happyvertical/sdk > packages > images`
|
|
10
|
+
- Workspace position: `15 of 30` local packages
|
|
11
|
+
- Internal dependencies: none
|
|
12
|
+
- Internal dependents: `@happyvertical/video`
|
|
13
|
+
- Knowledge graph files: `AGENT.md`, `metadata.json`, `ecosystem-manifest.json`
|
|
14
|
+
|
|
15
|
+
## Build & Test
|
|
16
|
+
```bash
|
|
17
|
+
pnpm --filter @happyvertical/images build
|
|
18
|
+
pnpm --filter @happyvertical/images test
|
|
19
|
+
pnpm --filter @happyvertical/images clean
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Agent Correction Loops
|
|
23
|
+
- If Vite or TypeScript reports missing packages, run `pnpm install` at the repo root and rerun `pnpm --filter @happyvertical/images build`.
|
|
24
|
+
- If tests or exports fail after API, type, or bundle changes, run `pnpm --filter @happyvertical/images clean` followed by `pnpm --filter @happyvertical/images build` and `pnpm --filter @happyvertical/images test`.
|
|
25
|
+
- If failures span multiple packages or Turborepo ordering looks wrong, run `pnpm build` and `pnpm typecheck` from the repo root before retrying package-scoped commands.
|
|
26
|
+
|
|
27
|
+
## Ecosystem Relationships
|
|
28
|
+
- Provides: Image processing utilities with adapter pattern for scaling from static to enterprise
|
|
29
|
+
- Implements: none
|
|
30
|
+
- Requires: @resvg/resvg-js, jimp, satori, sharp
|
|
31
|
+
- Stability: stable (Primary package surface is described as implemented and production-oriented.)
|
|
32
|
+
<!-- END AGENT:GENERATED -->
|
|
33
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright <2025> <Happy Vertical Corporation>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { ConvertOptions, HashAlgorithm, ImageDimensions, ImageInput, ImageMetadata, ImageProcessorInterface, ImgproxyOptions, ResizeOptions, ThumbnailOptions } from '../shared/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for generating signed imgproxy URLs
|
|
4
|
+
*/
|
|
5
|
+
export interface SignedUrlOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Width in pixels (0 for auto)
|
|
8
|
+
*/
|
|
9
|
+
width?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Height in pixels (0 for auto)
|
|
12
|
+
*/
|
|
13
|
+
height?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Resize mode
|
|
16
|
+
* - 'cover': Fill dimensions, crop excess
|
|
17
|
+
* - 'contain': Fit within dimensions, may have empty space
|
|
18
|
+
* - 'fill': Force exact dimensions (may distort)
|
|
19
|
+
*/
|
|
20
|
+
fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
|
21
|
+
/**
|
|
22
|
+
* Output quality (1-100)
|
|
23
|
+
*/
|
|
24
|
+
quality?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Output format (jpeg, png, webp, avif, etc.)
|
|
27
|
+
*/
|
|
28
|
+
format?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generate a signed imgproxy URL
|
|
32
|
+
*
|
|
33
|
+
* Standalone function for generating signed URLs without creating an adapter instance.
|
|
34
|
+
* Useful for client-side usage or when you only need URL generation.
|
|
35
|
+
*
|
|
36
|
+
* @param source - Source image URL (must be http/https)
|
|
37
|
+
* @param config - imgproxy server configuration
|
|
38
|
+
* @param options - Processing options
|
|
39
|
+
* @returns Signed imgproxy URL
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* import { signImgproxyUrl } from '@happyvertical/images';
|
|
44
|
+
*
|
|
45
|
+
* const url = signImgproxyUrl(
|
|
46
|
+
* 'https://example.com/image.jpg',
|
|
47
|
+
* {
|
|
48
|
+
* baseUrl: 'https://imgproxy.example.com',
|
|
49
|
+
* key: 'hex-encoded-key',
|
|
50
|
+
* salt: 'hex-encoded-salt'
|
|
51
|
+
* },
|
|
52
|
+
* { width: 300, height: 200, format: 'webp' }
|
|
53
|
+
* );
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function signImgproxyUrl(source: string, config: {
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
key?: string;
|
|
59
|
+
salt?: string;
|
|
60
|
+
}, options?: SignedUrlOptions): string;
|
|
61
|
+
/**
|
|
62
|
+
* imgproxy adapter for remote image processing
|
|
63
|
+
*
|
|
64
|
+
* Offloads image processing to a self-hosted imgproxy server.
|
|
65
|
+
* Supports URL signing for secure access.
|
|
66
|
+
*
|
|
67
|
+
* Features:
|
|
68
|
+
* - Scalable remote processing
|
|
69
|
+
* - URL-based API
|
|
70
|
+
* - Signed URLs for security
|
|
71
|
+
* - CDN-friendly outputs
|
|
72
|
+
*
|
|
73
|
+
* Limitations:
|
|
74
|
+
* - Requires network access to imgproxy server
|
|
75
|
+
* - Input must be accessible via URL (not local paths)
|
|
76
|
+
* - Hash computation not directly supported (requires fetching image)
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const processor = new ImgproxyAdapter({
|
|
81
|
+
* type: 'imgproxy',
|
|
82
|
+
* baseUrl: 'https://imgproxy.example.com',
|
|
83
|
+
* key: 'hex-encoded-key',
|
|
84
|
+
* salt: 'hex-encoded-salt'
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* await processor.thumbnail(
|
|
88
|
+
* 'https://example.com/image.jpg',
|
|
89
|
+
* '/tmp/thumb.jpg',
|
|
90
|
+
* { maxWidth: 300 }
|
|
91
|
+
* );
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare class ImgproxyAdapter implements ImageProcessorInterface {
|
|
95
|
+
private baseUrl;
|
|
96
|
+
private key?;
|
|
97
|
+
private salt?;
|
|
98
|
+
constructor(options: ImgproxyOptions);
|
|
99
|
+
/**
|
|
100
|
+
* Generate a signed imgproxy URL without fetching the image
|
|
101
|
+
*
|
|
102
|
+
* This is useful for:
|
|
103
|
+
* - Client-side usage where you want to display images directly
|
|
104
|
+
* - Debugging URL signing issues
|
|
105
|
+
* - Pre-generating URLs for batch processing
|
|
106
|
+
*
|
|
107
|
+
* @param source - Source image URL (must be http/https)
|
|
108
|
+
* @param options - Processing options
|
|
109
|
+
* @returns Signed imgproxy URL
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const adapter = new ImgproxyAdapter({
|
|
114
|
+
* type: 'imgproxy',
|
|
115
|
+
* baseUrl: 'https://imgproxy.example.com',
|
|
116
|
+
* key: 'hex-key',
|
|
117
|
+
* salt: 'hex-salt'
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* // Generate thumbnail URL
|
|
121
|
+
* const url = adapter.getSignedUrl('https://example.com/image.jpg', {
|
|
122
|
+
* width: 300,
|
|
123
|
+
* height: 200,
|
|
124
|
+
* fit: 'cover',
|
|
125
|
+
* format: 'webp'
|
|
126
|
+
* });
|
|
127
|
+
* // Returns: https://imgproxy.example.com/SIGNATURE/rs:fill:300:200/aHR0cHM6Ly9.../image.webp
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
getSignedUrl(source: string, options?: {
|
|
131
|
+
width?: number;
|
|
132
|
+
height?: number;
|
|
133
|
+
fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
|
134
|
+
quality?: number;
|
|
135
|
+
format?: string;
|
|
136
|
+
}): string;
|
|
137
|
+
/**
|
|
138
|
+
* Sign a path using HMAC-SHA256
|
|
139
|
+
*/
|
|
140
|
+
private sign;
|
|
141
|
+
/**
|
|
142
|
+
* Encode source URL for imgproxy
|
|
143
|
+
*/
|
|
144
|
+
private encodeSource;
|
|
145
|
+
/**
|
|
146
|
+
* Build imgproxy URL
|
|
147
|
+
*/
|
|
148
|
+
private buildUrl;
|
|
149
|
+
/**
|
|
150
|
+
* Convert ImageInput to URL string
|
|
151
|
+
*/
|
|
152
|
+
private toUrl;
|
|
153
|
+
getDimensions(input: ImageInput): Promise<ImageDimensions>;
|
|
154
|
+
thumbnail(input: ImageInput, output: string, options?: ThumbnailOptions): Promise<void>;
|
|
155
|
+
convert(input: ImageInput, output: string, options?: ConvertOptions): Promise<void>;
|
|
156
|
+
getMetadata(input: ImageInput): Promise<ImageMetadata>;
|
|
157
|
+
hash(_input: ImageInput, _algorithm?: HashAlgorithm): Promise<string>;
|
|
158
|
+
resize(input: ImageInput, output: string, options: ResizeOptions): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Build imgproxy processing string
|
|
161
|
+
*/
|
|
162
|
+
private buildProcessingString;
|
|
163
|
+
/**
|
|
164
|
+
* Map fit mode to imgproxy resize type
|
|
165
|
+
*/
|
|
166
|
+
private mapFitToResizeType;
|
|
167
|
+
/**
|
|
168
|
+
* Fetch image from imgproxy and save to file
|
|
169
|
+
*/
|
|
170
|
+
private fetchAndSave;
|
|
171
|
+
/**
|
|
172
|
+
* Infer image format from file extension
|
|
173
|
+
*/
|
|
174
|
+
private inferFormat;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=imgproxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imgproxy.d.ts","sourceRoot":"","sources":["../../src/adapters/imgproxy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,eAAe,EAEf,UAAU,EACV,aAAa,EACb,uBAAuB,EACvB,eAAe,EACf,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAE1D;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IACN,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,EACD,OAAO,GAAE,gBAAqB,GAC7B,MAAM,CAQR;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,eAAgB,YAAW,uBAAuB;IAC7D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAC,CAAS;IACrB,OAAO,CAAC,IAAI,CAAC,CAAS;gBAEV,OAAO,EAAE,eAAe;IASpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,YAAY,CACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;QAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ,GACL,MAAM;IAsBT;;OAEG;IACH,OAAO,CAAC,IAAI;IAkBZ;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAYhB;;OAEG;IACH,OAAO,CAAC,KAAK;IAqBP,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC;IAoC1D,SAAS,CACb,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC;IA+BV,OAAO,CACX,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA2BV,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAwCtD,IAAI,CACR,MAAM,EAAE,UAAU,EAClB,UAAU,GAAE,aAA4B,GACvC,OAAO,CAAC,MAAM,CAAC;IASZ,MAAM,CACV,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC;IAkChB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA0B7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;YACW,YAAY;IAiB1B;;OAEG;IACH,OAAO,CAAC,WAAW;CAcpB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ConvertOptions, HashAlgorithm, ImageDimensions, ImageInput, ImageMetadata, ImageProcessorInterface, JimpOptions, ResizeOptions, ThumbnailOptions } from '../shared/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Jimp adapter for pure JavaScript image processing
|
|
4
|
+
*
|
|
5
|
+
* Provides a fallback option when Sharp (native library) is not available.
|
|
6
|
+
* Useful for:
|
|
7
|
+
* - Serverless environments (AWS Lambda, Vercel Edge)
|
|
8
|
+
* - Environments without native compilation support
|
|
9
|
+
* - Cross-platform compatibility
|
|
10
|
+
*
|
|
11
|
+
* Limitations compared to Sharp:
|
|
12
|
+
* - Slower processing speed
|
|
13
|
+
* - Limited EXIF/metadata extraction
|
|
14
|
+
* - No AVIF support
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const processor = new JimpAdapter();
|
|
19
|
+
* const dims = await processor.getDimensions('/path/to/image.jpg');
|
|
20
|
+
* console.log(dims); // { width: 1920, height: 1080 }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class JimpAdapter implements ImageProcessorInterface {
|
|
24
|
+
private jimpStatic;
|
|
25
|
+
private options;
|
|
26
|
+
/**
|
|
27
|
+
* Create a new Jimp adapter
|
|
28
|
+
* @param options - Jimp adapter options
|
|
29
|
+
*/
|
|
30
|
+
constructor(options?: JimpOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Lazily load Jimp module
|
|
33
|
+
*/
|
|
34
|
+
private getJimp;
|
|
35
|
+
getDimensions(input: ImageInput): Promise<ImageDimensions>;
|
|
36
|
+
thumbnail(input: ImageInput, output: string, options?: ThumbnailOptions): Promise<void>;
|
|
37
|
+
convert(input: ImageInput, output: string, options?: ConvertOptions): Promise<void>;
|
|
38
|
+
getMetadata(input: ImageInput): Promise<ImageMetadata>;
|
|
39
|
+
hash(input: ImageInput, algorithm?: HashAlgorithm): Promise<string>;
|
|
40
|
+
resize(input: ImageInput, output: string, options: ResizeOptions): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Write image with quality settings based on output format
|
|
43
|
+
*/
|
|
44
|
+
private writeWithOptions;
|
|
45
|
+
/**
|
|
46
|
+
* Infer image format from file extension
|
|
47
|
+
*/
|
|
48
|
+
private inferFormat;
|
|
49
|
+
/**
|
|
50
|
+
* Get MIME type from format
|
|
51
|
+
*/
|
|
52
|
+
private mimeFromFormat;
|
|
53
|
+
/**
|
|
54
|
+
* Get format from MIME type
|
|
55
|
+
*/
|
|
56
|
+
private formatFromMime;
|
|
57
|
+
/**
|
|
58
|
+
* Try to determine MIME type from input
|
|
59
|
+
*/
|
|
60
|
+
private getMimeFromInput;
|
|
61
|
+
/**
|
|
62
|
+
* Map Jimp errors to our error types
|
|
63
|
+
*/
|
|
64
|
+
private mapError;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=jimp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jimp.d.ts","sourceRoot":"","sources":["../../src/adapters/jimp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,eAAe,EAEf,UAAU,EACV,aAAa,EACb,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AA6B5B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,WAAY,YAAW,uBAAuB;IACzD,OAAO,CAAC,UAAU,CAA2B;IAE7C,OAAO,CAAC,OAAO,CAAc;IAE7B;;;OAGG;gBACS,OAAO,GAAE,WAA8B;IAInD;;OAEG;YACW,OAAO;IAgBf,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC;IAU1D,SAAS,CACb,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC;IAgCV,OAAO,CACX,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAUV,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAqBtD,IAAI,CACR,KAAK,EAAE,UAAU,EACjB,SAAS,GAAE,aAA4B,GACtC,OAAO,CAAC,MAAM,CAAC;IAwCZ,MAAM,CACV,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC;IA8BhB;;OAEG;YACW,gBAAgB;IAoB9B;;OAEG;IACH,OAAO,CAAC,WAAW;IAgBnB;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,OAAO,CAAC,QAAQ;CA0BjB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ConvertOptions, HashAlgorithm, ImageDimensions, ImageInput, ImageMetadata, ImageProcessorInterface, ResizeOptions, SharpOptions, ThumbnailOptions } from '../shared/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sharp adapter for high-performance image processing
|
|
4
|
+
*
|
|
5
|
+
* Uses the native sharp library which provides:
|
|
6
|
+
* - Fast image resizing and format conversion
|
|
7
|
+
* - Rich metadata extraction including EXIF, IPTC, XMP
|
|
8
|
+
* - Perceptual hashing for image deduplication
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const processor = new SharpAdapter();
|
|
13
|
+
* const dims = await processor.getDimensions('/path/to/image.jpg');
|
|
14
|
+
* console.log(dims); // { width: 1920, height: 1080 }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare class SharpAdapter implements ImageProcessorInterface {
|
|
18
|
+
private sharp;
|
|
19
|
+
private options;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new Sharp adapter
|
|
22
|
+
* @param options - Sharp adapter options
|
|
23
|
+
*/
|
|
24
|
+
constructor(options?: SharpOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Lazily load sharp module
|
|
27
|
+
*/
|
|
28
|
+
private getSharp;
|
|
29
|
+
getDimensions(input: ImageInput): Promise<ImageDimensions>;
|
|
30
|
+
thumbnail(input: ImageInput, output: string, options?: ThumbnailOptions): Promise<void>;
|
|
31
|
+
convert(input: ImageInput, output: string, options?: ConvertOptions): Promise<void>;
|
|
32
|
+
getMetadata(input: ImageInput): Promise<ImageMetadata>;
|
|
33
|
+
hash(input: ImageInput, algorithm?: HashAlgorithm): Promise<string>;
|
|
34
|
+
resize(input: ImageInput, output: string, options: ResizeOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Infer image format from file extension
|
|
37
|
+
*/
|
|
38
|
+
private inferFormat;
|
|
39
|
+
/**
|
|
40
|
+
* Parse EXIF buffer to object (basic parsing)
|
|
41
|
+
*/
|
|
42
|
+
private parseExifBuffer;
|
|
43
|
+
/**
|
|
44
|
+
* Parse IPTC buffer to object (basic parsing)
|
|
45
|
+
*/
|
|
46
|
+
private parseIptcBuffer;
|
|
47
|
+
/**
|
|
48
|
+
* Parse XMP buffer to object (basic parsing)
|
|
49
|
+
*/
|
|
50
|
+
private parseXmpBuffer;
|
|
51
|
+
/**
|
|
52
|
+
* Map sharp errors to our error types
|
|
53
|
+
*/
|
|
54
|
+
private mapError;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=sharp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharp.d.ts","sourceRoot":"","sources":["../../src/adapters/sharp.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,eAAe,EAEf,UAAU,EACV,aAAa,EACb,uBAAuB,EACvB,aAAa,EACb,YAAY,EACZ,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAa,YAAW,uBAAuB;IAC1D,OAAO,CAAC,KAAK,CAAuC;IAEpD,OAAO,CAAC,OAAO,CAAe;IAE9B;;;OAGG;gBACS,OAAO,GAAE,YAAiB;IAItC;;OAEG;YACW,QAAQ;IAehB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC;IAiB1D,SAAS,CACb,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC;IA0BV,OAAO,CACX,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAYV,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IA2BtD,IAAI,CACR,KAAK,EAAE,UAAU,EACjB,SAAS,GAAE,aAA4B,GACtC,OAAO,CAAC,MAAM,CAAC;IA6BZ,MAAM,CACV,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC;IAwBhB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAmBjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-context.d.ts","sourceRoot":"","sources":["../../src/cli/claude-context.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, copyFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const Dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const pkgRoot = join(Dirname, "../..");
|
|
7
|
+
const targetDir = join(process.cwd(), ".claude");
|
|
8
|
+
if (!existsSync(targetDir)) {
|
|
9
|
+
mkdirSync(targetDir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
const pkgName = "images";
|
|
12
|
+
const agentMdSrc = existsSync(join(pkgRoot, "AGENT.md")) ? join(pkgRoot, "AGENT.md") : join(pkgRoot, "CLAUDE.md");
|
|
13
|
+
const metaSrc = existsSync(join(pkgRoot, "metadata.json")) ? join(pkgRoot, "metadata.json") : join(pkgRoot, ".claude-meta.json");
|
|
14
|
+
if (existsSync(agentMdSrc)) {
|
|
15
|
+
copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));
|
|
16
|
+
}
|
|
17
|
+
if (existsSync(metaSrc)) {
|
|
18
|
+
copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));
|
|
19
|
+
}
|
|
20
|
+
console.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);
|
|
21
|
+
//# sourceMappingURL=claude-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-context.js","sources":["../../src/cli/claude-context.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI script to install agent context for @happyvertical/images\n * Run the published context installer binary for this package.\n */\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst Dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = join(Dirname, '../..');\nconst targetDir = join(process.cwd(), '.claude');\n\nif (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n}\n\nconst pkgName = 'images';\nconst agentMdSrc = existsSync(join(pkgRoot, 'AGENT.md'))\n ? join(pkgRoot, 'AGENT.md')\n : join(pkgRoot, 'CLAUDE.md');\nconst metaSrc = existsSync(join(pkgRoot, 'metadata.json'))\n ? join(pkgRoot, 'metadata.json')\n : join(pkgRoot, '.claude-meta.json');\n\nif (existsSync(agentMdSrc)) {\n copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));\n}\n\nif (existsSync(metaSrc)) {\n copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));\n}\n\nconsole.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);\n"],"names":[],"mappings":";;;;AASA,MAAM,UAAU,QAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,MAAM,UAAU,KAAK,SAAS,OAAO;AACrC,MAAM,YAAY,KAAK,QAAQ,IAAA,GAAO,SAAS;AAE/C,IAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,YAAU,WAAW,EAAE,WAAW,KAAA,CAAM;AAC1C;AAEA,MAAM,UAAU;AAChB,MAAM,aAAa,WAAW,KAAK,SAAS,UAAU,CAAC,IACnD,KAAK,SAAS,UAAU,IACxB,KAAK,SAAS,WAAW;AAC7B,MAAM,UAAU,WAAW,KAAK,SAAS,eAAe,CAAC,IACrD,KAAK,SAAS,eAAe,IAC7B,KAAK,SAAS,mBAAmB;AAErC,IAAI,WAAW,UAAU,GAAG;AAC1B,eAAa,YAAY,KAAK,WAAW,QAAQ,OAAO,KAAK,CAAC;AAChE;AAEA,IAAI,WAAW,OAAO,GAAG;AACvB,eAAa,SAAS,KAAK,WAAW,QAAQ,OAAO,YAAY,CAAC;AACpE;AAEA,QAAQ,IAAI,8BAA8B,OAAO,sBAAsB;"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headline Card Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates branded social media / OG images with article titles.
|
|
5
|
+
* Uses Satori (Vercel's JSX-to-SVG) and resvg for PNG output.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { generateHeadlineCard } from '@happyvertical/images';
|
|
10
|
+
*
|
|
11
|
+
* const result = await generateHeadlineCard('Breaking: Major Discovery', {
|
|
12
|
+
* brandColor: '#1a56db',
|
|
13
|
+
* subtitle: 'Science News',
|
|
14
|
+
* template: 'news'
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* await writeFile('og-image.png', result.buffer);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Template style for headline cards
|
|
22
|
+
*/
|
|
23
|
+
export type HeadlineCardTemplate = 'default' | 'news' | 'minimal';
|
|
24
|
+
/**
|
|
25
|
+
* Options for headline card generation
|
|
26
|
+
*/
|
|
27
|
+
export interface HeadlineCardOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Width in pixels
|
|
30
|
+
* @default 1200
|
|
31
|
+
*/
|
|
32
|
+
width?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Height in pixels
|
|
35
|
+
* @default 630
|
|
36
|
+
*/
|
|
37
|
+
height?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Primary brand color (hex)
|
|
40
|
+
* @default '#3b82f6'
|
|
41
|
+
*/
|
|
42
|
+
brandColor?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Background color (hex)
|
|
45
|
+
* @default '#ffffff'
|
|
46
|
+
*/
|
|
47
|
+
backgroundColor?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Secondary text color (hex)
|
|
50
|
+
* @default '#64748b'
|
|
51
|
+
*/
|
|
52
|
+
textColor?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Optional subtitle/category
|
|
55
|
+
*/
|
|
56
|
+
subtitle?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional logo URL (will be fetched and embedded)
|
|
59
|
+
*/
|
|
60
|
+
logoUrl?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Logo data as base64 or buffer (alternative to logoUrl)
|
|
63
|
+
*/
|
|
64
|
+
logoData?: string | Buffer;
|
|
65
|
+
/**
|
|
66
|
+
* Card template style
|
|
67
|
+
* @default 'default'
|
|
68
|
+
*/
|
|
69
|
+
template?: HeadlineCardTemplate;
|
|
70
|
+
/**
|
|
71
|
+
* Font family name (must be available via Google Fonts or provide fontData)
|
|
72
|
+
* @default 'Inter'
|
|
73
|
+
*/
|
|
74
|
+
fontFamily?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Custom font data (loaded font buffer)
|
|
77
|
+
*/
|
|
78
|
+
fontData?: ArrayBuffer;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Result from headline card generation
|
|
82
|
+
*/
|
|
83
|
+
export interface HeadlineCardResult {
|
|
84
|
+
/** PNG image buffer */
|
|
85
|
+
buffer: Buffer;
|
|
86
|
+
/** Image width */
|
|
87
|
+
width: number;
|
|
88
|
+
/** Image height */
|
|
89
|
+
height: number;
|
|
90
|
+
/** MIME type (always image/png) */
|
|
91
|
+
mimeType: 'image/png';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generate a headline card image
|
|
95
|
+
*
|
|
96
|
+
* Creates a branded social media / OG image with the article title.
|
|
97
|
+
* Supports multiple templates and customization options.
|
|
98
|
+
*
|
|
99
|
+
* @param title - The headline text to display
|
|
100
|
+
* @param options - Customization options
|
|
101
|
+
* @returns Promise resolving to PNG image result
|
|
102
|
+
*
|
|
103
|
+
* @example Basic usage
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const result = await generateHeadlineCard('Breaking News: AI Advances');
|
|
106
|
+
* await fs.writeFile('og-image.png', result.buffer);
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @example With branding
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const result = await generateHeadlineCard('Local Council Approves Budget', {
|
|
112
|
+
* brandColor: '#1a56db',
|
|
113
|
+
* subtitle: 'Town News',
|
|
114
|
+
* template: 'news'
|
|
115
|
+
* });
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @example Full customization
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const result = await generateHeadlineCard('The Future of Technology', {
|
|
121
|
+
* width: 1200,
|
|
122
|
+
* height: 630,
|
|
123
|
+
* brandColor: '#059669',
|
|
124
|
+
* backgroundColor: '#f8fafc',
|
|
125
|
+
* subtitle: 'Technology',
|
|
126
|
+
* template: 'minimal',
|
|
127
|
+
* fontFamily: 'Roboto'
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export declare function generateHeadlineCard(title: string, options?: HeadlineCardOptions): Promise<HeadlineCardResult>;
|
|
132
|
+
/**
|
|
133
|
+
* Reset cached font data (useful for testing)
|
|
134
|
+
*/
|
|
135
|
+
export declare function resetFontCache(): void;
|
|
136
|
+
//# sourceMappingURL=headline-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headline-card.d.ts","sourceRoot":"","sources":["../src/headline-card.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AA2BH;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAEhC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,QAAQ,EAAE,WAAW,CAAC;CACvB;AAoWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAsF7B;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
|