Add inline character portraits and dictionary search workflow (#83)

This commit is contained in:
2026-05-25 03:16:25 -07:00
committed by GitHub
parent 7e6f9672cf
commit 807c0ff3db
54 changed files with 2306 additions and 178 deletions
@@ -0,0 +1,121 @@
import assert from 'node:assert/strict';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import test from 'node:test';
import { getSnapshotPath, writeSnapshot } from './cache';
import { CHARACTER_DICTIONARY_FORMAT_VERSION } from './constants';
import { buildCharacterNameImageIndexFromSnapshots } from './image-lookup';
import type { CharacterDictionarySnapshot } from './types';
const PNG_1X1_BASE64 =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+nmX8AAAAASUVORK5CYII=';
function makeTempDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-character-image-lookup-'));
}
test('buildCharacterNameImageIndexFromSnapshots maps name terms to character portrait data URLs', () => {
const outputDir = makeTempDir();
const snapshot: CharacterDictionarySnapshot = {
formatVersion: CHARACTER_DICTIONARY_FORMAT_VERSION,
mediaId: 130298,
mediaTitle: 'The Eminence in Shadow',
entryCount: 1,
updatedAt: 1_700_000_000_000,
termEntries: [
[
'アレクシア',
'あれくしあ',
'name primary',
'',
75,
[
{
type: 'structured-content',
content: {
tag: 'div',
content: [
{ tag: 'div', content: 'アレクシア・ミドガル' },
{
tag: 'div',
content: {
tag: 'img',
path: 'img/m130298-c123.png',
alt: 'アレクシア・ミドガル',
},
},
{
tag: 'details',
content: [
{ tag: 'summary', content: 'Voiced by' },
{
tag: 'div',
content: {
tag: 'img',
path: 'img/m130298-va456.png',
alt: 'VA',
},
},
],
},
],
},
},
],
0,
'',
],
],
images: [
{ path: 'img/m130298-c123.png', dataBase64: 'AAAA' },
{ path: 'img/m130298-va456.png', dataBase64: 'BBBB' },
],
};
writeSnapshot(getSnapshotPath(outputDir, snapshot.mediaId), snapshot);
const index = buildCharacterNameImageIndexFromSnapshots(outputDir);
assert.deepEqual(index.get('アレクシア'), {
src: 'data:image/png;base64,AAAA',
alt: 'アレクシア・ミドガル',
});
});
test('buildCharacterNameImageIndexFromSnapshots sniffs image MIME from bytes before path extension', () => {
const outputDir = makeTempDir();
const snapshot: CharacterDictionarySnapshot = {
formatVersion: CHARACTER_DICTIONARY_FORMAT_VERSION,
mediaId: 130298,
mediaTitle: 'The Eminence in Shadow',
entryCount: 1,
updatedAt: 1_700_000_000_000,
termEntries: [
[
'アレクシア',
'あれくしあ',
'name primary',
'',
75,
[
{
type: 'structured-content',
content: {
tag: 'img',
path: 'img/m130298-c123.jpg',
alt: 'アレクシア・ミドガル',
},
},
],
0,
'',
],
],
images: [{ path: 'img/m130298-c123.jpg', dataBase64: PNG_1X1_BASE64 }],
};
writeSnapshot(getSnapshotPath(outputDir, snapshot.mediaId), snapshot);
const index = buildCharacterNameImageIndexFromSnapshots(outputDir);
assert.equal(index.get('アレクシア')?.src, `data:image/png;base64,${PNG_1X1_BASE64}`);
});