mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
feat(settings): add read-aloud filename token
Expose media-overlay-aware naming across file creation and delivery paths while preserving existing client contracts. Keep settings router mocks compatible with the application router exports. Closes #1590
This commit is contained in:
@@ -147,6 +147,18 @@ describe('FileNamingSettings', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('offers the readaloud token and previews read-aloud and plain EPUB outcomes', async () => {
|
||||
const wrapper = await mountPage()
|
||||
|
||||
expect(buttonWith(wrapper, '{readaloud}')).toBeDefined()
|
||||
await patternField(wrapper).setValue('{title}< ({readaloud})>')
|
||||
|
||||
expect(wrapper.text()).toContain('Read-aloud comparison')
|
||||
expect(wrapper.text()).toContain('Read-aloud EPUB')
|
||||
expect(wrapper.text()).toContain('Plain EPUB')
|
||||
expect(wrapper.text()).toContain('Neuromancer (readaloud)')
|
||||
})
|
||||
|
||||
it('saves the cross-platform toggle immediately without a separate save button', async () => {
|
||||
const wrapper = await mountPage()
|
||||
|
||||
|
||||
@@ -14,10 +14,14 @@ const routeState = { name: 'settings-appearance-theme' }
|
||||
|
||||
const push = vi.fn<(to: { name: string }) => void>()
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRoute: () => ({ name: routeState.name }),
|
||||
useRouter: () => ({ push }),
|
||||
}))
|
||||
vi.mock('vue-router', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('vue-router')>()
|
||||
return {
|
||||
...actual,
|
||||
useRoute: () => ({ name: routeState.name }),
|
||||
useRouter: () => ({ push }),
|
||||
}
|
||||
})
|
||||
|
||||
const navStatus = vi.hoisted(() => ({ scanning: false }))
|
||||
|
||||
|
||||
@@ -10,10 +10,14 @@ const permState = {
|
||||
demoRestricted: false,
|
||||
}
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRoute: () => ({ name: 'settings-appearance-theme' }),
|
||||
useRouter: () => ({ push: vi.fn<(to: { name: string }) => void>() }),
|
||||
}))
|
||||
vi.mock('vue-router', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('vue-router')>()
|
||||
return {
|
||||
...actual,
|
||||
useRoute: () => ({ name: 'settings-appearance-theme' }),
|
||||
useRouter: () => ({ push: vi.fn<(to: { name: string }) => void>() }),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@/features/auth/composables/usePermissions', () => ({
|
||||
usePermissions: () => ({
|
||||
|
||||
@@ -36,6 +36,8 @@ function resolve(metadata: Record<string, string>): string {
|
||||
}
|
||||
|
||||
const resolved = computed(() => resolve(EXAMPLE_PATTERN_METADATA))
|
||||
const usesReadaloudToken = computed(() => props.pattern.includes('{readaloud'))
|
||||
const plainEpubResult = computed(() => resolve({ ...EXAMPLE_PATTERN_METADATA, readaloud: '' }))
|
||||
|
||||
const fallbackCases = computed(() =>
|
||||
MISSING_METADATA_CASES.map((previewCase) => ({
|
||||
@@ -113,6 +115,22 @@ function handleSanitize(value: boolean) {
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div v-if="usesReadaloudToken" class="border-t border-border px-3.5 py-3">
|
||||
<h4 class="mb-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground">
|
||||
{{ t('settings.reader.fileNaming.readaloudComparison') }}
|
||||
</h4>
|
||||
<dl class="divide-y divide-dashed divide-border">
|
||||
<div class="py-1.5">
|
||||
<dt class="text-[11px] font-semibold text-muted-foreground">{{ t('settings.reader.fileNaming.readaloudEpub') }}</dt>
|
||||
<dd class="mt-0.5 break-words font-mono text-[11px] text-foreground">{{ resolved }}</dd>
|
||||
</div>
|
||||
<div class="py-1.5">
|
||||
<dt class="text-[11px] font-semibold text-muted-foreground">{{ t('settings.reader.fileNaming.plainEpub') }}</dt>
|
||||
<dd class="mt-0.5 break-words font-mono text-[11px] text-foreground">{{ plainEpubResult }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 border-t border-border bg-muted/30 px-3.5 py-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="settings-label">{{ t('settings.reader.fileNaming.crossPlatform') }}</p>
|
||||
|
||||
@@ -24,6 +24,7 @@ const TOKEN_DESCRIPTION_KEYS: Record<PatternToken, string> = {
|
||||
library: 'settings.reader.fileNaming.tokenLibrary',
|
||||
originalFilename: 'settings.reader.fileNaming.tokenOriginalFilename',
|
||||
extension: 'settings.reader.fileNaming.tokenExtension',
|
||||
readaloud: 'settings.reader.fileNaming.tokenReadaloud',
|
||||
}
|
||||
|
||||
const MODIFIER_DESCRIPTION_KEYS: Record<PatternModifier, string> = {
|
||||
|
||||
@@ -2324,6 +2324,7 @@
|
||||
"tokenLibrary": "Library name",
|
||||
"tokenOriginalFilename": "Original filename (without extension)",
|
||||
"tokenExtension": "File extension (without dot)",
|
||||
"tokenReadaloud": "The word readaloud for an EPUB with media overlays",
|
||||
"patternHelpIntro": "Patterns use tokens like {titleToken} and {authorsToken}, plus modifiers and optional segments.",
|
||||
"browseTokensAndExamples": "Browse tokens and examples",
|
||||
"subtitleShort": "Folder and filename patterns for stored files.",
|
||||
@@ -2386,6 +2387,9 @@
|
||||
"resultCopyFailed": "Failed to copy the resolved result",
|
||||
"sampleBookNote": "Sample book used for every preview",
|
||||
"missingMetadata": "If metadata is missing",
|
||||
"readaloudComparison": "Read-aloud comparison",
|
||||
"readaloudEpub": "Read-aloud EPUB",
|
||||
"plainEpub": "Plain EPUB",
|
||||
"caseNoSeriesLabel": "No series",
|
||||
"caseNoYearLabel": "No year",
|
||||
"caseNoAuthorLabel": "No author",
|
||||
|
||||
Reference in New Issue
Block a user