Expand Yomitan external profile tilde paths to home directory

- Normalize `yomitan.externalProfilePath` so `~` and `~/...` resolve to the current user home directory
- Add coverage for tilde expansion in integration config tests
- Update Yomitan config docs and tighten settings opener test assertion
This commit is contained in:
2026-03-11 18:50:31 -07:00
parent 504793eaed
commit aa569272db
4 changed files with 33 additions and 5 deletions

View File

@@ -112,6 +112,7 @@ The configuration file includes several main sections:
- [**Jimaku**](#jimaku) - Jimaku API configuration and defaults
- [**Auto Subtitle Sync**](#auto-subtitle-sync) - Sync current subtitle with `alass`/`ffsubsync`
- [**AniList**](#anilist) - Optional post-watch progress updates
- [**Yomitan**](#yomitan) - Reuse an external read-only Yomitan profile via `yomitan.externalProfilePath`
- [**Jellyfin**](#jellyfin) - Optional Jellyfin auth, library listing, and playback launch
- [**Discord Rich Presence**](#discord-rich-presence) - Optional Discord activity card updates
- [**Immersion Tracking**](#immersion-tracking) - Track subtitle sessions and mining activity in SQLite

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;
@@ -202,7 +215,7 @@ export function applyIntegrationConfig(context: ResolveContext): void {
if (isObject(src.yomitan)) {
const externalProfilePath = asString(src.yomitan.externalProfilePath);
if (externalProfilePath !== undefined) {
resolved.yomitan.externalProfilePath = externalProfilePath.trim();
resolved.yomitan.externalProfilePath = normalizeExternalProfilePath(externalProfilePath);
} else if (src.yomitan.externalProfilePath !== undefined) {
warn(
'yomitan.externalProfilePath',

View File

@@ -1,5 +1,6 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import * as os from 'node:os';
import { createResolveContext } from './context';
import { applyIntegrationConfig } from './integrations';
@@ -127,3 +128,16 @@ test('yomitan externalProfilePath is trimmed and invalid values warn', () => {
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, `${homeDir}/.config/gsm_overlay`);
});

View File

@@ -22,12 +22,12 @@ test('yomitan opener warns when extension cannot be loaded', async () => {
});
test('yomitan opener opens settings window when extension is available', async () => {
let opened = false;
let forwardedSession: { id: string } | null | undefined;
const yomitanSession = { id: 'session' };
const openSettings = createOpenYomitanSettingsHandler({
ensureYomitanExtensionLoaded: async () => ({ id: 'ext' }),
openYomitanSettingsWindow: ({ yomitanSession: forwardedSession }) => {
opened = (forwardedSession as { id: string } | null)?.id === 'session';
openYomitanSettingsWindow: ({ yomitanSession: nextSession }) => {
forwardedSession = nextSession as { id: string } | null;
},
getExistingWindow: () => null,
setWindow: () => {},
@@ -39,5 +39,5 @@ test('yomitan opener opens settings window when extension is available', async (
openSettings();
await Promise.resolve();
await Promise.resolve();
assert.equal(opened, true);
assert.equal(forwardedSession, yomitanSession);
});