feat(yomitan): add read-only external profile support for shared dictionaries (#18)

This commit is contained in:
2026-03-12 01:17:34 -07:00
committed by GitHub
parent 68833c76c4
commit 1b56360a24
67 changed files with 1230 additions and 135 deletions

View File

@@ -1,6 +1,19 @@
import * as os from 'node:os';
import * as path from 'node:path';
import { ResolveContext } from './context';
import { asBoolean, asNumber, asString, isObject } from './shared';
function normalizeExternalProfilePath(value: string): string {
const trimmed = value.trim();
if (trimmed === '~') {
return os.homedir();
}
if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) {
return path.join(os.homedir(), trimmed.slice(2));
}
return trimmed;
}
export function applyIntegrationConfig(context: ResolveContext): void {
const { src, resolved, warn } = context;
@@ -199,6 +212,22 @@ export function applyIntegrationConfig(context: ResolveContext): void {
}
}
if (isObject(src.yomitan)) {
const externalProfilePath = asString(src.yomitan.externalProfilePath);
if (externalProfilePath !== undefined) {
resolved.yomitan.externalProfilePath = normalizeExternalProfilePath(externalProfilePath);
} else if (src.yomitan.externalProfilePath !== undefined) {
warn(
'yomitan.externalProfilePath',
src.yomitan.externalProfilePath,
resolved.yomitan.externalProfilePath,
'Expected string.',
);
}
} else if (src.yomitan !== undefined) {
warn('yomitan', src.yomitan, resolved.yomitan, 'Expected object.');
}
if (isObject(src.jellyfin)) {
const enabled = asBoolean(src.jellyfin.enabled);
if (enabled !== undefined) {

View File

@@ -1,5 +1,7 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import * as os from 'node:os';
import * as path from 'node:path';
import { createResolveContext } from './context';
import { applyIntegrationConfig } from './integrations';
@@ -104,3 +106,42 @@ test('anilist character dictionary fields are parsed, clamped, and enum-validate
warnedPaths.includes('anilist.characterDictionary.collapsibleSections.characterInformation'),
);
});
test('yomitan externalProfilePath is trimmed and invalid values warn', () => {
const { context, warnings } = createResolveContext({
yomitan: {
externalProfilePath: ' /tmp/gsm-profile ',
},
});
applyIntegrationConfig(context);
assert.equal(context.resolved.yomitan.externalProfilePath, '/tmp/gsm-profile');
const invalid = createResolveContext({
yomitan: {
externalProfilePath: 42 as never,
},
});
applyIntegrationConfig(invalid.context);
assert.equal(invalid.context.resolved.yomitan.externalProfilePath, '');
assert.ok(invalid.warnings.some((warning) => warning.path === 'yomitan.externalProfilePath'));
});
test('yomitan externalProfilePath expands leading tilde to the current home directory', () => {
const homeDir = os.homedir();
const { context } = createResolveContext({
yomitan: {
externalProfilePath: '~/.config/gsm_overlay',
},
});
applyIntegrationConfig(context);
assert.equal(
context.resolved.yomitan.externalProfilePath,
path.join(homeDir, '.config', 'gsm_overlay'),
);
});