Add the art tray's online Generate: prompt → a GM-configured image-gen endpoint → new pencil image cached into a persisted, searchable library (offline thereafter). No endpoint or key is hardcoded — the GM pastes their gen URL (⚙ field, persisted). Degrades gracefully: unconfigured or offline → Generate disabled, library search fully works; network is never load-bearing. - src/art/generate.js: createArtGenerator (injectable fetch/navigator). - src/art/library.js: pure addGenerated (dedupe + cap 24). - art-tray: generate row, endpoint config, generated cache (gmd.art.*). Tests: generate.test.js, library.test.js, art-tray-generate.test.js; existing art-tray tests unchanged. 206 tests pass; build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
36 lines
1.6 KiB
JavaScript
36 lines
1.6 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { addGenerated, makeAssetId, slugifyLabel } from '../../src/art/library.js';
|
|
|
|
const BASE = [{ id: 'rain-pier', src: '/a.png', label: 'Pier in the rain', tags: ['rain'] }];
|
|
|
|
describe('art library helpers', () => {
|
|
it('slugifies labels', () => {
|
|
expect(slugifyLabel('Tired Detective — in the Rain')).toBe('tired-detective-in-the-rain');
|
|
});
|
|
|
|
it('makes ids unique against taken ids', () => {
|
|
expect(makeAssetId('Rainy Pier', [])).toBe('rainy-pier');
|
|
expect(makeAssetId('Rainy Pier', ['rainy-pier'])).toBe('rainy-pier-2');
|
|
});
|
|
|
|
it('prepends a generated asset with a unique id and generated flag', () => {
|
|
const out = addGenerated([], BASE, { src: 'data:x', label: 'Rainy Pier', tags: ['rain', 'pier'] });
|
|
expect(out).toHaveLength(1);
|
|
expect(out[0]).toEqual({ id: 'rainy-pier', src: 'data:x', label: 'Rainy Pier', tags: ['rain', 'pier'], generated: true });
|
|
});
|
|
|
|
it('avoids colliding with a base manifest id', () => {
|
|
const out = addGenerated([], BASE, { src: 'data:y', label: 'Pier in the rain' });
|
|
expect(out[0].id).toBe('pier-in-the-rain'); // differs from base 'rain-pier', so no suffix needed
|
|
const out2 = addGenerated(out, BASE, { src: 'data:z', label: 'Pier in the rain' });
|
|
expect(out2[0].id).toBe('pier-in-the-rain-2');
|
|
});
|
|
|
|
it('caps the generated list to bound storage (newest kept)', () => {
|
|
let gen = [];
|
|
for (let i = 0; i < 30; i++) gen = addGenerated(gen, BASE, { src: 'd' + i, label: 'img ' + i }, 24);
|
|
expect(gen).toHaveLength(24);
|
|
expect(gen[0].label).toBe('img 29'); // newest first
|
|
});
|
|
});
|