@harpiaresearch/vref 0.0.0-stage → 1.0.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/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Harpia AI Research
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,3 +1,366 @@
1
- # Temporary Holding Version
1
+ # VREF — Visual Reference Format
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > **Standardized container for curated visual references in AI image generation**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@harpiaresearch/vref)](https://www.npmjs.com/package/@harpiaresearch/vref)
6
+ [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE)
7
+
8
+ Developed by [Harpia AI Research](https://harpiaresearch.cc) 🇧🇷
9
+
10
+ ---
11
+
12
+ ## What is VREF?
13
+
14
+ VREF (Visual Reference Format) is an open standard for packaging and sharing curated visual references for AI image generation platforms.
15
+
16
+ Instead of uploading multiple loose images or struggling to describe complex visual concepts in text, users can:
17
+
18
+ 1. **Package** reference images into a single `.vref` file
19
+ 2. **Upload** the `.vref` to any supporting AI platform
20
+ 3. **Select** how many random references to use (via slider)
21
+ 4. **Generate** images with visual context
22
+
23
+ ### The Problem
24
+
25
+ When generating images with AI, users often:
26
+ - ❌ Know what they want visually but can't describe it in words
27
+ - ❌ Need to upload 10-50 reference images individually
28
+ - ❌ Can't easily share or reuse reference collections
29
+ - ❌ Struggle to communicate style, mood, and composition
30
+
31
+ ### The Solution
32
+
33
+ VREF provides:
34
+ - ✅ **Single file** container for multiple images
35
+ - ✅ **Validated** image-only format
36
+ - ✅ **Structured metadata** (tags, description, license)
37
+ - ✅ **Portable** and shareable
38
+ - ✅ **Random sampling** built-in
39
+ - ✅ **Open standard** anyone can implement
40
+
41
+ ---
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ npm install @harpiaresearch/vref
47
+ ```
48
+
49
+ Or use directly with `npx`:
50
+
51
+ ```bash
52
+ npx create-vref
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Quick Start
58
+
59
+ ### Creating a .vref file (CLI)
60
+
61
+ ```bash
62
+ # From a directory of images
63
+ npx create-vref ./my-images -o references.vref
64
+
65
+ # With metadata
66
+ npx create-vref ./images -o pack.vref \
67
+ -n "My Reference Pack" \
68
+ -d "Collection of visual references for X" \
69
+ -t "style,mood,theme" \
70
+ -a "Your Name" \
71
+ -l "CC-BY-4.0"
72
+
73
+ # From a single image
74
+ npx create-vref image.png -o single.vref
75
+ ```
76
+
77
+ ### Using the library (JavaScript/TypeScript)
78
+
79
+ **Creating a .vref file:**
80
+
81
+ ```typescript
82
+ import { VREFWriter } from '@harpiaresearch/vref';
83
+
84
+ const writer = new VREFWriter({
85
+ fileName: 'my-pack.vref',
86
+ description: 'Collection of RPG character references',
87
+ tags: ['rpg', 'characters', 'fantasy'],
88
+ author: 'Your Name',
89
+ license: 'CC-BY-4.0',
90
+ });
91
+
92
+ // Add images from directory
93
+ writer.addImagesFromDirectory('./images', true);
94
+
95
+ // Or add individual images
96
+ writer.addImageFromFile('./image1.png');
97
+ writer.addImage('image2.jpg', imageBuffer);
98
+
99
+ // Write the .vref file
100
+ writer.write('output.vref');
101
+
102
+ console.log(`Created .vref with ${writer.getImageCount()} images`);
103
+ ```
104
+
105
+ **Reading a .vref file:**
106
+
107
+ ```typescript
108
+ import { VREFReader } from '@harpiaresearch/vref';
109
+
110
+ // Read the entire .vref file
111
+ const vref = VREFReader.read('references.vref', { loadImages: true });
112
+
113
+ console.log(`Loaded ${vref.manifest.imageCount} images`);
114
+ console.log(`Description: ${vref.manifest.metadata?.description}`);
115
+
116
+ // Get random sample of 3 images
117
+ const randomImages = vref.sample(3);
118
+ randomImages.forEach((buffer, idx) => {
119
+ console.log(`Image ${idx + 1}: ${buffer.length} bytes`);
120
+ });
121
+
122
+ // Get specific image
123
+ const image = vref.getImage('photo1.png');
124
+ if (image) {
125
+ // Do something with the image buffer
126
+ }
127
+ ```
128
+
129
+ **Read only the manifest:**
130
+
131
+ ```typescript
132
+ import { VREFReader } from '@harpiaresearch/vref';
133
+
134
+ const manifest = VREFReader.readManifest('references.vref');
135
+ console.log(manifest);
136
+ ```
137
+
138
+ **Validate a .vref file:**
139
+
140
+ ```typescript
141
+ import { VREFReader } from '@harpiaresearch/vref';
142
+
143
+ const result = VREFReader.validate('references.vref');
144
+ if (result.valid) {
145
+ console.log('✅ Valid .vref file');
146
+ } else {
147
+ console.error('❌ Invalid .vref file:');
148
+ result.errors.forEach(err => console.error(` - ${err}`));
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Use Cases
155
+
156
+ ### 1. **AI Image Generation Platforms**
157
+
158
+ Users upload `.vref` files to platforms like:
159
+ - Midjourney
160
+ - Stable Diffusion
161
+ - DALL-E
162
+ - Replicate
163
+ - Custom AI platforms
164
+
165
+ The platform extracts random images and sends them as visual context along with the user's text prompt.
166
+
167
+ ### 2. **Game Development**
168
+
169
+ Distribute reference packs:
170
+ - `rpg-sprites.vref` — Character sprites for RPG games
171
+ - `platformer-tiles.vref` — Tile sets for platformers
172
+ - `ui-elements.vref` — UI design references
173
+
174
+ ### 3. **Training ML Models**
175
+
176
+ Use `.vref` files as datasets:
177
+
178
+ ```typescript
179
+ import { VREFReader } from '@harpiaresearch/vref';
180
+ import { PixelGenModel } from '@harpiaresearch/pixelgen';
181
+
182
+ const dataset = VREFReader.read('training-data.vref', { loadImages: true });
183
+ const model = new PixelGenModel(3, 3);
184
+
185
+ // Train with VREF data
186
+ // ...
187
+ ```
188
+
189
+ ### 4. **Design & Creative Work**
190
+
191
+ Share mood boards and references:
192
+ - `architecture-modern.vref`
193
+ - `color-palettes-warm.vref`
194
+ - `typography-references.vref`
195
+
196
+ ---
197
+
198
+ ## File Format Specification
199
+
200
+ A `.vref` file is a ZIP container with:
201
+
202
+ ```
203
+ my-pack.vref (ZIP)
204
+ ├── manifest.json
205
+ ├── image1.png
206
+ ├── image2.jpg
207
+ ├── image3.webp
208
+ └── ...
209
+ ```
210
+
211
+ ### `manifest.json` Structure
212
+
213
+ ```json
214
+ {
215
+ "version": "1.0.0",
216
+ "format": "VREF",
217
+ "fileName": "my-pack.vref",
218
+ "imageCount": 24,
219
+ "images": [
220
+ {
221
+ "path": "image1.png",
222
+ "format": "png",
223
+ "size": 45678,
224
+ "width": 1920,
225
+ "height": 1080
226
+ }
227
+ ],
228
+ "createdAt": "2024-10-12T10:30:00.000Z",
229
+ "metadata": {
230
+ "description": "Collection of X references",
231
+ "tags": ["tag1", "tag2"],
232
+ "author": "Author Name",
233
+ "license": "CC-BY-4.0"
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### Supported Image Formats
239
+
240
+ - ✅ PNG (`.png`)
241
+ - ✅ JPEG (`.jpg`, `.jpeg`)
242
+ - ✅ WebP (`.webp`)
243
+ - ✅ GIF (`.gif`)
244
+ - ✅ AVIF (`.avif`)
245
+
246
+ Any other file type is **rejected** with an error.
247
+
248
+ ---
249
+
250
+ ## API Reference
251
+
252
+ ### `VREFWriter`
253
+
254
+ ```typescript
255
+ class VREFWriter {
256
+ constructor(options: VREFCreateOptions);
257
+
258
+ addImageFromFile(filePath: string): void;
259
+ addImage(fileName: string, buffer: Buffer): void;
260
+ addImagesFromDirectory(dirPath: string, recursive?: boolean): void;
261
+
262
+ write(outputPath: string): void;
263
+ getImageCount(): number;
264
+ clear(): void;
265
+ }
266
+ ```
267
+
268
+ ### `VREFReader`
269
+
270
+ ```typescript
271
+ class VREFReader {
272
+ static read(filePath: string, options?: VREFReadOptions): VREFReadResult;
273
+ static readManifest(filePath: string): VREFManifest;
274
+ static validate(filePath: string): { valid: boolean; errors: string[] };
275
+ }
276
+ ```
277
+
278
+ ### Types
279
+
280
+ See [src/types.ts](src/types.ts) for complete type definitions.
281
+
282
+ ---
283
+
284
+ ## Platform Integration
285
+
286
+ ### For AI Platform Developers
287
+
288
+ Integrating VREF support:
289
+
290
+ ```typescript
291
+ import { VREFReader } from '@harpiaresearch/vref';
292
+
293
+ // 1. User uploads .vref file
294
+ const vref = VREFReader.read(uploadedFile, { loadImages: true });
295
+
296
+ // 2. Show slider: "How many references? (1 - {vref.manifest.imageCount})"
297
+ const userSelectedCount = getUserSliderValue(); // e.g., 3
298
+
299
+ // 3. Sample random images
300
+ const selectedImages = vref.sample(userSelectedCount);
301
+
302
+ // 4. Get user's text prompt
303
+ const userPrompt = getUserPrompt(); // "A warrior character in pixel art style"
304
+
305
+ // 5. Send to your AI model
306
+ await generateImage({
307
+ prompt: userPrompt,
308
+ referenceImages: selectedImages,
309
+ });
310
+ ```
311
+
312
+ That's it! 🎉
313
+
314
+ ---
315
+
316
+ ## Examples
317
+
318
+ See the [examples/](examples/) directory for:
319
+ - Creating `.vref` files
320
+ - Reading and sampling images
321
+ - Platform integration examples
322
+ - PixelGen integration
323
+
324
+ ---
325
+
326
+ ## Contributing
327
+
328
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
329
+
330
+ ---
331
+
332
+ ## License
333
+
334
+ BSD 3-Clause License
335
+
336
+ Copyright (c) 2024, Harpia AI Research
337
+
338
+ See [LICENSE](LICENSE) for full text.
339
+
340
+ ---
341
+
342
+ ## Citation
343
+
344
+ If you use VREF in your research or project:
345
+
346
+ ```bibtex
347
+ @software{vref2024,
348
+ title = {VREF: Visual Reference Format for AI Image Generation},
349
+ author = {Harpia AI Research},
350
+ year = {2024},
351
+ url = {https://github.com/Harpia-AI-Research/vref}
352
+ }
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Links
358
+
359
+ - 🌐 Website: [harpiaresearch.cc](https://harpiaresearch.cc)
360
+ - 📦 npm: [@harpiaresearch/vref](https://www.npmjs.com/package/@harpiaresearch/vref)
361
+ - 📄 Specification: [docs/SPECIFICATION.md](docs/SPECIFICATION.md)
362
+ - 💬 Issues: [GitHub Issues](https://github.com/Harpia-AI-Research/vref/issues)
363
+
364
+ ---
365
+
366
+ **Made with 💚💛 by Harpia AI Research — São Paulo, Brazil** 🇧🇷
@@ -0,0 +1,22 @@
1
+ /**
2
+ * VREFReader - Reads .vref files
3
+ * Harpia AI Research
4
+ */
5
+ import { VREFManifest, VREFReadOptions, VREFReadResult } from './types';
6
+ export declare class VREFReader {
7
+ /**
8
+ * Read a .vref file
9
+ */
10
+ static read(filePath: string, options?: VREFReadOptions): VREFReadResult;
11
+ /**
12
+ * Read only the manifest without loading images
13
+ */
14
+ static readManifest(filePath: string): VREFManifest;
15
+ /**
16
+ * Validate a .vref file
17
+ */
18
+ static validate(filePath: string): {
19
+ valid: boolean;
20
+ errors: string[];
21
+ };
22
+ }
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * VREFReader - Reads .vref files
4
+ * Harpia AI Research
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.VREFReader = void 0;
44
+ const fs = __importStar(require("fs"));
45
+ const adm_zip_1 = __importDefault(require("adm-zip"));
46
+ class VREFReader {
47
+ /**
48
+ * Read a .vref file
49
+ */
50
+ static read(filePath, options = {}) {
51
+ if (!fs.existsSync(filePath)) {
52
+ throw new Error(`File not found: ${filePath}`);
53
+ }
54
+ const zip = new adm_zip_1.default(filePath);
55
+ const zipEntries = zip.getEntries();
56
+ // Find and parse manifest
57
+ const manifestEntry = zipEntries.find(entry => entry.entryName === 'manifest.json');
58
+ if (!manifestEntry) {
59
+ throw new Error('Invalid .vref file: manifest.json not found');
60
+ }
61
+ const manifestData = manifestEntry.getData().toString('utf8');
62
+ const manifest = JSON.parse(manifestData);
63
+ // Validate manifest
64
+ if (manifest.format !== 'VREF') {
65
+ throw new Error('Invalid .vref file: incorrect format identifier');
66
+ }
67
+ // Load images if requested
68
+ const images = new Map();
69
+ if (options.loadImages) {
70
+ for (const imageInfo of manifest.images) {
71
+ const entry = zipEntries.find(e => e.entryName === imageInfo.path);
72
+ if (entry) {
73
+ images.set(imageInfo.path, entry.getData());
74
+ }
75
+ }
76
+ }
77
+ // Helper function to get specific image
78
+ const getImage = (path) => {
79
+ if (images.has(path)) {
80
+ return images.get(path);
81
+ }
82
+ // Load on-demand if not already loaded
83
+ const entry = zipEntries.find(e => e.entryName === path);
84
+ if (entry) {
85
+ const buffer = entry.getData();
86
+ images.set(path, buffer);
87
+ return buffer;
88
+ }
89
+ return undefined;
90
+ };
91
+ // Helper function to sample N random images
92
+ const sample = (count) => {
93
+ const imagePaths = manifest.images.map(img => img.path);
94
+ const sampledPaths = [];
95
+ // Fisher-Yates shuffle for random sampling
96
+ const shuffled = [...imagePaths];
97
+ for (let i = shuffled.length - 1; i > 0; i--) {
98
+ const j = Math.floor(Math.random() * (i + 1));
99
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
100
+ }
101
+ // Take first N
102
+ const selected = shuffled.slice(0, Math.min(count, shuffled.length));
103
+ // Load and return buffers
104
+ return selected.map(path => {
105
+ const buffer = getImage(path);
106
+ if (!buffer) {
107
+ throw new Error(`Failed to load image: ${path}`);
108
+ }
109
+ return buffer;
110
+ });
111
+ };
112
+ return {
113
+ manifest,
114
+ images: options.loadImages ? images : undefined,
115
+ sample,
116
+ getImage,
117
+ };
118
+ }
119
+ /**
120
+ * Read only the manifest without loading images
121
+ */
122
+ static readManifest(filePath) {
123
+ const result = VREFReader.read(filePath, { loadImages: false });
124
+ return result.manifest;
125
+ }
126
+ /**
127
+ * Validate a .vref file
128
+ */
129
+ static validate(filePath) {
130
+ const errors = [];
131
+ try {
132
+ if (!fs.existsSync(filePath)) {
133
+ errors.push('File does not exist');
134
+ return { valid: false, errors };
135
+ }
136
+ const zip = new adm_zip_1.default(filePath);
137
+ const zipEntries = zip.getEntries();
138
+ // Check for manifest
139
+ const manifestEntry = zipEntries.find(entry => entry.entryName === 'manifest.json');
140
+ if (!manifestEntry) {
141
+ errors.push('manifest.json not found');
142
+ return { valid: false, errors };
143
+ }
144
+ // Parse manifest
145
+ let manifest;
146
+ try {
147
+ const manifestData = manifestEntry.getData().toString('utf8');
148
+ manifest = JSON.parse(manifestData);
149
+ }
150
+ catch (error) {
151
+ errors.push('Failed to parse manifest.json');
152
+ return { valid: false, errors };
153
+ }
154
+ // Validate format
155
+ if (manifest.format !== 'VREF') {
156
+ errors.push('Invalid format identifier (expected "VREF")');
157
+ }
158
+ // Validate version
159
+ if (!manifest.version) {
160
+ errors.push('Missing version field');
161
+ }
162
+ // Validate image count
163
+ const actualImageCount = zipEntries.filter(e => e.entryName !== 'manifest.json').length;
164
+ if (manifest.imageCount !== actualImageCount) {
165
+ errors.push(`Image count mismatch: manifest says ${manifest.imageCount}, found ${actualImageCount}`);
166
+ }
167
+ // Validate all images listed in manifest exist
168
+ for (const imageInfo of manifest.images) {
169
+ const entry = zipEntries.find(e => e.entryName === imageInfo.path);
170
+ if (!entry) {
171
+ errors.push(`Image listed in manifest not found: ${imageInfo.path}`);
172
+ }
173
+ }
174
+ return {
175
+ valid: errors.length === 0,
176
+ errors,
177
+ };
178
+ }
179
+ catch (error) {
180
+ errors.push(`Unexpected error: ${error}`);
181
+ return { valid: false, errors };
182
+ }
183
+ }
184
+ }
185
+ exports.VREFReader = VREFReader;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * VREFWriter - Creates .vref files
3
+ * Harpia AI Research
4
+ */
5
+ import { VREFCreateOptions } from './types';
6
+ export declare class VREFWriter {
7
+ private images;
8
+ private options;
9
+ constructor(options: VREFCreateOptions);
10
+ /**
11
+ * Add an image from a file path
12
+ */
13
+ addImageFromFile(filePath: string): void;
14
+ /**
15
+ * Add an image from a buffer
16
+ */
17
+ addImage(fileName: string, buffer: Buffer): void;
18
+ /**
19
+ * Add multiple images from a directory
20
+ */
21
+ addImagesFromDirectory(dirPath: string, recursive?: boolean): void;
22
+ /**
23
+ * Write the .vref file
24
+ */
25
+ write(outputPath: string): void;
26
+ /**
27
+ * Validate image format
28
+ */
29
+ private isValidImageFormat;
30
+ /**
31
+ * Validate image buffer by checking magic bytes
32
+ */
33
+ private isValidImageBuffer;
34
+ /**
35
+ * Get image dimensions (basic implementation for PNG and JPEG)
36
+ */
37
+ private getImageDimensions;
38
+ /**
39
+ * Get current image count
40
+ */
41
+ getImageCount(): number;
42
+ /**
43
+ * Clear all images
44
+ */
45
+ clear(): void;
46
+ }
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ /**
3
+ * VREFWriter - Creates .vref files
4
+ * Harpia AI Research
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.VREFWriter = void 0;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const adm_zip_1 = __importDefault(require("adm-zip"));
47
+ const types_1 = require("./types");
48
+ class VREFWriter {
49
+ constructor(options) {
50
+ this.images = new Map();
51
+ this.options = options;
52
+ }
53
+ /**
54
+ * Add an image from a file path
55
+ */
56
+ addImageFromFile(filePath) {
57
+ if (!fs.existsSync(filePath)) {
58
+ throw new Error(`File not found: ${filePath}`);
59
+ }
60
+ const buffer = fs.readFileSync(filePath);
61
+ const fileName = path.basename(filePath);
62
+ this.addImage(fileName, buffer);
63
+ }
64
+ /**
65
+ * Add an image from a buffer
66
+ */
67
+ addImage(fileName, buffer) {
68
+ const ext = path.extname(fileName).toLowerCase().slice(1);
69
+ if (!this.isValidImageFormat(ext)) {
70
+ throw new Error(`Unsupported format: ${ext}. Only images are allowed: ${types_1.SUPPORTED_IMAGE_FORMATS.join(', ')}`);
71
+ }
72
+ // Validate that it's actually an image by checking magic bytes
73
+ if (!this.isValidImageBuffer(buffer, ext)) {
74
+ throw new Error(`File ${fileName} does not appear to be a valid ${ext} image`);
75
+ }
76
+ this.images.set(fileName, buffer);
77
+ }
78
+ /**
79
+ * Add multiple images from a directory
80
+ */
81
+ addImagesFromDirectory(dirPath, recursive = false) {
82
+ if (!fs.existsSync(dirPath)) {
83
+ throw new Error(`Directory not found: ${dirPath}`);
84
+ }
85
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
86
+ for (const entry of entries) {
87
+ const fullPath = path.join(dirPath, entry.name);
88
+ if (entry.isDirectory() && recursive) {
89
+ this.addImagesFromDirectory(fullPath, recursive);
90
+ }
91
+ else if (entry.isFile()) {
92
+ const ext = path.extname(entry.name).toLowerCase().slice(1);
93
+ if (this.isValidImageFormat(ext)) {
94
+ try {
95
+ this.addImageFromFile(fullPath);
96
+ }
97
+ catch (error) {
98
+ console.warn(`Skipping ${entry.name}: ${error}`);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ /**
105
+ * Write the .vref file
106
+ */
107
+ write(outputPath) {
108
+ if (this.images.size === 0) {
109
+ throw new Error('No images added. Cannot create empty .vref file.');
110
+ }
111
+ const zip = new adm_zip_1.default();
112
+ const imageMetadataList = [];
113
+ // Add all images to ZIP
114
+ for (const [fileName, buffer] of this.images) {
115
+ zip.addFile(fileName, buffer);
116
+ const ext = path.extname(fileName).toLowerCase().slice(1);
117
+ const dimensions = this.getImageDimensions(buffer, ext);
118
+ imageMetadataList.push({
119
+ path: fileName,
120
+ format: ext,
121
+ size: buffer.length,
122
+ width: dimensions?.width,
123
+ height: dimensions?.height,
124
+ });
125
+ }
126
+ // Create manifest
127
+ const manifest = {
128
+ version: '1.0.0',
129
+ format: 'VREF',
130
+ fileName: path.basename(outputPath),
131
+ imageCount: this.images.size,
132
+ images: imageMetadataList,
133
+ createdAt: new Date().toISOString(),
134
+ metadata: {
135
+ description: this.options.description,
136
+ tags: this.options.tags,
137
+ author: this.options.author,
138
+ license: this.options.license,
139
+ ...this.options.metadata,
140
+ },
141
+ };
142
+ // Add manifest as JSON
143
+ zip.addFile('manifest.json', Buffer.from(JSON.stringify(manifest, null, 2)));
144
+ // Write ZIP file
145
+ zip.writeZip(outputPath);
146
+ }
147
+ /**
148
+ * Validate image format
149
+ */
150
+ isValidImageFormat(format) {
151
+ return types_1.SUPPORTED_IMAGE_FORMATS.includes(format);
152
+ }
153
+ /**
154
+ * Validate image buffer by checking magic bytes
155
+ */
156
+ isValidImageBuffer(buffer, format) {
157
+ if (buffer.length < 8)
158
+ return false;
159
+ switch (format.toLowerCase()) {
160
+ case 'png':
161
+ return buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47;
162
+ case 'jpg':
163
+ case 'jpeg':
164
+ return buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff;
165
+ case 'gif':
166
+ return buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46;
167
+ case 'webp':
168
+ return buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50;
169
+ case 'avif':
170
+ // AVIF detection is more complex, but we can check for 'ftyp' box
171
+ const ftypCheck = buffer.toString('ascii', 4, 8);
172
+ return ftypCheck === 'ftyp';
173
+ default:
174
+ return false;
175
+ }
176
+ }
177
+ /**
178
+ * Get image dimensions (basic implementation for PNG and JPEG)
179
+ */
180
+ getImageDimensions(buffer, format) {
181
+ try {
182
+ if (format === 'png' && buffer.length >= 24) {
183
+ const width = buffer.readUInt32BE(16);
184
+ const height = buffer.readUInt32BE(20);
185
+ return { width, height };
186
+ }
187
+ if ((format === 'jpg' || format === 'jpeg') && buffer.length >= 4) {
188
+ // Basic JPEG dimension extraction (simplified)
189
+ // This is a simplified version; production code might use a library
190
+ let offset = 2;
191
+ while (offset < buffer.length) {
192
+ if (buffer[offset] !== 0xff)
193
+ break;
194
+ const marker = buffer[offset + 1];
195
+ offset += 2;
196
+ if (marker === 0xc0 || marker === 0xc2) {
197
+ const height = buffer.readUInt16BE(offset + 3);
198
+ const width = buffer.readUInt16BE(offset + 5);
199
+ return { width, height };
200
+ }
201
+ const segmentLength = buffer.readUInt16BE(offset);
202
+ offset += segmentLength;
203
+ }
204
+ }
205
+ }
206
+ catch (error) {
207
+ // If dimension extraction fails, just return undefined
208
+ return undefined;
209
+ }
210
+ return undefined;
211
+ }
212
+ /**
213
+ * Get current image count
214
+ */
215
+ getImageCount() {
216
+ return this.images.size;
217
+ }
218
+ /**
219
+ * Clear all images
220
+ */
221
+ clear() {
222
+ this.images.clear();
223
+ }
224
+ }
225
+ exports.VREFWriter = VREFWriter;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * VREF CLI - Command line interface for creating .vref files
4
+ * Harpia AI Research
5
+ */
6
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * VREF CLI - Command line interface for creating .vref files
5
+ * Harpia AI Research
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const VREFWriter_1 = require("./VREFWriter");
44
+ function showHelp() {
45
+ console.log(`
46
+ ┌─────────────────────────────────────────────────┐
47
+ │ VREF CLI v1.0.0 │
48
+ │ by Harpia AI Research │
49
+ │ https://harpiaresearch.cc │
50
+ └─────────────────────────────────────────────────┘
51
+
52
+ Create .vref files from images
53
+
54
+ USAGE:
55
+ npx create-vref <input> [options]
56
+
57
+ ARGUMENTS:
58
+ <input> Path to image file or directory
59
+
60
+ OPTIONS:
61
+ -o, --output <path> Output .vref file path (default: output.vref)
62
+ -n, --name <name> Name for the reference pack
63
+ -d, --desc <text> Description of the reference pack
64
+ -t, --tags <tags> Comma-separated tags
65
+ -a, --author <name> Author name
66
+ -l, --license <lic> License (e.g., CC-BY-4.0, MIT)
67
+ -r, --recursive Include subdirectories
68
+ -h, --help Show this help
69
+
70
+ EXAMPLES:
71
+ # Create from single image
72
+ npx create-vref image.png -o references.vref
73
+
74
+ # Create from directory
75
+ npx create-vref ./images -o pack.vref -n "My Pack"
76
+
77
+ # With metadata
78
+ npx create-vref ./sprites -o rpg-sprites.vref \\
79
+ -n "RPG Sprites" \\
80
+ -d "Character sprites for RPG games" \\
81
+ -t "rpg,sprites,pixelart" \\
82
+ -a "Your Name" \\
83
+ -l "CC-BY-4.0"
84
+ `);
85
+ }
86
+ function parseArgs(args) {
87
+ const options = {
88
+ input: null,
89
+ output: 'output.vref',
90
+ name: null,
91
+ description: null,
92
+ tags: null,
93
+ author: null,
94
+ license: null,
95
+ recursive: false,
96
+ };
97
+ for (let i = 0; i < args.length; i++) {
98
+ const arg = args[i];
99
+ switch (arg) {
100
+ case '-h':
101
+ case '--help':
102
+ showHelp();
103
+ process.exit(0);
104
+ break;
105
+ case '-o':
106
+ case '--output':
107
+ options.output = args[++i];
108
+ break;
109
+ case '-n':
110
+ case '--name':
111
+ options.name = args[++i];
112
+ break;
113
+ case '-d':
114
+ case '--desc':
115
+ case '--description':
116
+ options.description = args[++i];
117
+ break;
118
+ case '-t':
119
+ case '--tags':
120
+ options.tags = args[++i].split(',').map((t) => t.trim());
121
+ break;
122
+ case '-a':
123
+ case '--author':
124
+ options.author = args[++i];
125
+ break;
126
+ case '-l':
127
+ case '--license':
128
+ options.license = args[++i];
129
+ break;
130
+ case '-r':
131
+ case '--recursive':
132
+ options.recursive = true;
133
+ break;
134
+ default:
135
+ if (!arg.startsWith('-')) {
136
+ options.input = arg;
137
+ }
138
+ break;
139
+ }
140
+ }
141
+ return options;
142
+ }
143
+ function main() {
144
+ const args = process.argv.slice(2);
145
+ if (args.length === 0) {
146
+ showHelp();
147
+ process.exit(0);
148
+ }
149
+ const options = parseArgs(args);
150
+ if (!options.input) {
151
+ console.error('❌ Error: No input specified');
152
+ console.log('Run "npx create-vref --help" for usage information');
153
+ process.exit(1);
154
+ }
155
+ if (!fs.existsSync(options.input)) {
156
+ console.error(`❌ Error: Input not found: ${options.input}`);
157
+ process.exit(1);
158
+ }
159
+ console.log('┌─────────────────────────────────────────────────┐');
160
+ console.log('│ VREF CLI v1.0.0 │');
161
+ console.log('│ by Harpia AI Research │');
162
+ console.log('└─────────────────────────────────────────────────┘\n');
163
+ try {
164
+ const fileName = options.name || path.basename(options.output, '.vref');
165
+ const writer = new VREFWriter_1.VREFWriter({
166
+ fileName: options.output,
167
+ description: options.description,
168
+ tags: options.tags,
169
+ author: options.author,
170
+ license: options.license,
171
+ });
172
+ console.log(`📂 Input: ${options.input}`);
173
+ const stat = fs.statSync(options.input);
174
+ if (stat.isDirectory()) {
175
+ console.log(`🔍 Scanning directory${options.recursive ? ' (recursive)' : ''}...`);
176
+ writer.addImagesFromDirectory(options.input, options.recursive);
177
+ }
178
+ else if (stat.isFile()) {
179
+ console.log('📄 Adding image...');
180
+ writer.addImageFromFile(options.input);
181
+ }
182
+ else {
183
+ throw new Error('Input must be a file or directory');
184
+ }
185
+ const imageCount = writer.getImageCount();
186
+ if (imageCount === 0) {
187
+ console.error('❌ Error: No valid images found');
188
+ process.exit(1);
189
+ }
190
+ console.log(`✅ Found ${imageCount} valid image${imageCount !== 1 ? 's' : ''}`);
191
+ console.log(`📦 Creating ${options.output}...`);
192
+ writer.write(options.output);
193
+ const stats = fs.statSync(options.output);
194
+ const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
195
+ console.log(`✅ Success! Created ${options.output} (${sizeMB} MB)`);
196
+ console.log('\n📋 Metadata:');
197
+ console.log(` Images: ${imageCount}`);
198
+ if (options.name)
199
+ console.log(` Name: ${options.name}`);
200
+ if (options.description)
201
+ console.log(` Description: ${options.description}`);
202
+ if (options.tags)
203
+ console.log(` Tags: ${options.tags.join(', ')}`);
204
+ if (options.author)
205
+ console.log(` Author: ${options.author}`);
206
+ if (options.license)
207
+ console.log(` License: ${options.license}`);
208
+ console.log('\n🚀 Share this file with AI platforms that support VREF!');
209
+ console.log(' Learn more: https://harpiaresearch.cc\n');
210
+ }
211
+ catch (error) {
212
+ console.error(`\n❌ Error: ${error.message}`);
213
+ process.exit(1);
214
+ }
215
+ }
216
+ main();
@@ -0,0 +1,10 @@
1
+ /**
2
+ * VREF (Visual Reference Format)
3
+ * Standardized container for curated visual references in AI image generation
4
+ *
5
+ * Harpia AI Research
6
+ * https://harpiaresearch.cc
7
+ */
8
+ export { VREFWriter } from './VREFWriter';
9
+ export { VREFReader } from './VREFReader';
10
+ export { VREFManifest, VREFCreateOptions, VREFReadOptions, VREFReadResult, ImageMetadata, SupportedImageFormat, SUPPORTED_IMAGE_FORMATS, } from './types';
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * VREF (Visual Reference Format)
4
+ * Standardized container for curated visual references in AI image generation
5
+ *
6
+ * Harpia AI Research
7
+ * https://harpiaresearch.cc
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.SUPPORTED_IMAGE_FORMATS = exports.VREFReader = exports.VREFWriter = void 0;
11
+ var VREFWriter_1 = require("./VREFWriter");
12
+ Object.defineProperty(exports, "VREFWriter", { enumerable: true, get: function () { return VREFWriter_1.VREFWriter; } });
13
+ var VREFReader_1 = require("./VREFReader");
14
+ Object.defineProperty(exports, "VREFReader", { enumerable: true, get: function () { return VREFReader_1.VREFReader; } });
15
+ var types_1 = require("./types");
16
+ Object.defineProperty(exports, "SUPPORTED_IMAGE_FORMATS", { enumerable: true, get: function () { return types_1.SUPPORTED_IMAGE_FORMATS; } });
@@ -0,0 +1,96 @@
1
+ /**
2
+ * VREF (Visual Reference Format) Type Definitions
3
+ * Harpia AI Research
4
+ */
5
+ /**
6
+ * VREF Manifest structure
7
+ * Automatically generated and included in every .vref file
8
+ */
9
+ export interface VREFManifest {
10
+ /** VREF format version (semver) */
11
+ version: string;
12
+ /** Format identifier */
13
+ format: 'VREF';
14
+ /** ZIP compression version (if applicable) */
15
+ zipVersion?: string;
16
+ /** Name of the .vref file */
17
+ fileName: string;
18
+ /** Total number of images in the container */
19
+ imageCount: number;
20
+ /** Array of image metadata */
21
+ images: ImageMetadata[];
22
+ /** ISO 8601 timestamp of creation */
23
+ createdAt: string;
24
+ /** Optional metadata */
25
+ metadata?: {
26
+ /** Description of the reference pack */
27
+ description?: string;
28
+ /** Tags for categorization */
29
+ tags?: string[];
30
+ /** Author information */
31
+ author?: string;
32
+ /** License information */
33
+ license?: string;
34
+ /** Any additional custom fields */
35
+ [key: string]: any;
36
+ };
37
+ }
38
+ /**
39
+ * Metadata for individual images within the VREF
40
+ */
41
+ export interface ImageMetadata {
42
+ /** Relative path within the .vref archive */
43
+ path: string;
44
+ /** Image format (png, jpg, jpeg, webp, gif, avif) */
45
+ format: string;
46
+ /** File size in bytes */
47
+ size: number;
48
+ /** Image width in pixels */
49
+ width?: number;
50
+ /** Image height in pixels */
51
+ height?: number;
52
+ }
53
+ /**
54
+ * Options for creating a VREF file
55
+ */
56
+ export interface VREFCreateOptions {
57
+ /** Name for the output .vref file */
58
+ fileName: string;
59
+ /** Optional description */
60
+ description?: string;
61
+ /** Optional tags */
62
+ tags?: string[];
63
+ /** Optional author */
64
+ author?: string;
65
+ /** Optional license */
66
+ license?: string;
67
+ /** Additional metadata */
68
+ metadata?: Record<string, any>;
69
+ }
70
+ /**
71
+ * Options for reading a VREF file
72
+ */
73
+ export interface VREFReadOptions {
74
+ /** Whether to extract images to memory */
75
+ loadImages?: boolean;
76
+ /** Validate image formats */
77
+ validate?: boolean;
78
+ }
79
+ /**
80
+ * Result of reading a VREF file
81
+ */
82
+ export interface VREFReadResult {
83
+ /** Parsed manifest */
84
+ manifest: VREFManifest;
85
+ /** Image buffers (if loadImages: true) */
86
+ images?: Map<string, Buffer>;
87
+ /** Get a random sample of N images */
88
+ sample: (count: number) => Buffer[];
89
+ /** Get specific image by path */
90
+ getImage: (path: string) => Buffer | undefined;
91
+ }
92
+ /**
93
+ * Supported image formats
94
+ */
95
+ export declare const SUPPORTED_IMAGE_FORMATS: readonly ["png", "jpg", "jpeg", "webp", "gif", "avif"];
96
+ export type SupportedImageFormat = typeof SUPPORTED_IMAGE_FORMATS[number];
package/dist/types.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * VREF (Visual Reference Format) Type Definitions
4
+ * Harpia AI Research
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SUPPORTED_IMAGE_FORMATS = void 0;
8
+ /**
9
+ * Supported image formats
10
+ */
11
+ exports.SUPPORTED_IMAGE_FORMATS = [
12
+ 'png',
13
+ 'jpg',
14
+ 'jpeg',
15
+ 'webp',
16
+ 'gif',
17
+ 'avif'
18
+ ];
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@harpiaresearch/vref",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Visual Reference Format - Standardized container for curated visual references in AI image generation",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/**/*",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "bin": {
13
+ "create-vref": "./dist/cli.js",
14
+ "vref": "./dist/cli.js"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsc --watch",
19
+ "test": "node --test",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "vref",
24
+ "visual-reference",
25
+ "ai",
26
+ "image-generation",
27
+ "midjourney",
28
+ "stable-diffusion",
29
+ "harpia"
30
+ ],
31
+ "author": "Harpia AI Research",
32
+ "license": "BSD-3-Clause",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/Harpia-AI-Research/vref"
36
+ },
37
+ "dependencies": {
38
+ "adm-zip": "^0.5.10"
39
+ },
40
+ "devDependencies": {
41
+ "@types/adm-zip": "^0.5.5",
42
+ "@types/node": "^20.0.0",
43
+ "typescript": "^5.3.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }