feat: Replace NEXT_PUBLIC_NODE_ENV with explicit feature flags for granular control over DOCX conversion, destructive actions, TTS defaults, and DeepInfra model visibility.
This commit is contained in:
parent
eeeceb8e54
commit
8f5e87ed4a
7 changed files with 110 additions and 79 deletions
13
.env.example
13
.env.example
|
|
@ -1,7 +1,3 @@
|
||||||
# (Optional) Client feature flags (does not work in Docker containers due to being build-time only)
|
|
||||||
NEXT_PUBLIC_NODE_ENV=development
|
|
||||||
NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=
|
|
||||||
NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=
|
|
||||||
|
|
||||||
# Local / OpenAI TTS API Configuration (default)
|
# Local / OpenAI TTS API Configuration (default)
|
||||||
# Suggest using https://github.com/remsky/Kokoro-FastAPI
|
# Suggest using https://github.com/remsky/Kokoro-FastAPI
|
||||||
|
|
@ -74,3 +70,12 @@ WHISPER_CPP_BIN=/whisper.cpp/build/bin/whisper-cli
|
||||||
|
|
||||||
# (Optional) Override ffmpeg binary path used for audiobook processing
|
# (Optional) Override ffmpeg binary path used for audiobook processing
|
||||||
FFMPEG_BIN=
|
FFMPEG_BIN=
|
||||||
|
|
||||||
|
# (Optional) Client feature flags (does not work in Docker containers due to being build-time only)
|
||||||
|
# NEXT_PUBLIC_ENABLE_DOCX_CONVERSION=true
|
||||||
|
# NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS=true
|
||||||
|
# NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=custom-openai
|
||||||
|
# NEXT_PUBLIC_DEFAULT_TTS_MODEL=kokoro
|
||||||
|
# NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS=true
|
||||||
|
# NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true
|
||||||
|
# NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=false
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ This guide covers deploying OpenReader WebUI to Vercel with external Postgres an
|
||||||
Recommended production setup (auth enabled):
|
Recommended production setup (auth enabled):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
API_BASE=https://api.deepinfra.com/v1/openai
|
||||||
|
API_KEY=your_deepinfra_key
|
||||||
POSTGRES_URL=postgres://...
|
POSTGRES_URL=postgres://...
|
||||||
USE_EMBEDDED_WEED_MINI=false
|
USE_EMBEDDED_WEED_MINI=false
|
||||||
S3_ACCESS_KEY_ID=...
|
S3_ACCESS_KEY_ID=...
|
||||||
|
|
@ -27,34 +29,29 @@ S3_REGION=us-east-1
|
||||||
S3_PREFIX=openreader
|
S3_PREFIX=openreader
|
||||||
BASE_URL=https://your-app.vercel.app
|
BASE_URL=https://your-app.vercel.app
|
||||||
AUTH_SECRET=...
|
AUTH_SECRET=...
|
||||||
NEXT_PUBLIC_NODE_ENV=production
|
# Optional client/runtime feature defaults:
|
||||||
# Optional client/runtime feature overrides:
|
NEXT_PUBLIC_ENABLE_DOCX_CONVERSION=false
|
||||||
# NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=false
|
NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS=false
|
||||||
# NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=true
|
NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=deepinfra
|
||||||
|
NEXT_PUBLIC_DEFAULT_TTS_MODEL=hexgrad/Kokoro-82M
|
||||||
|
NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS=false
|
||||||
|
NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true
|
||||||
|
NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=false
|
||||||
# Optional (non-AWS S3-compatible providers):
|
# Optional (non-AWS S3-compatible providers):
|
||||||
# S3_ENDPOINT=https://...
|
# S3_ENDPOINT=https://...
|
||||||
# S3_FORCE_PATH_STYLE=true
|
# S3_FORCE_PATH_STYLE=true
|
||||||
```
|
```
|
||||||
|
|
||||||
:::info `NEXT_PUBLIC_*` feature flags
|
:::info Production Configuration & Feature Flags
|
||||||
- `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=false`: hides audiobook export UI entry points.
|
We recommend setting these defaults for a production-like environment:
|
||||||
- `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=true`: enables word-highlight UI and timestamp alignment requests.
|
|
||||||
:::
|
|
||||||
|
|
||||||
:::warning `NEXT_PUBLIC_NODE_ENV` behavior
|
- `NEXT_PUBLIC_ENABLE_DOCX_CONVERSION=false`: Disables DOCX upload (requires external tools anyway)
|
||||||
Use `NEXT_PUBLIC_NODE_ENV=production` on Vercel unless you explicitly want dev-oriented client behavior.
|
- `NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS=false`: Hides destructive "Delete All" actions
|
||||||
|
- `NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=deepinfra`: Points default TTS to a scalable provider
|
||||||
With `production`:
|
- `NEXT_PUBLIC_DEFAULT_TTS_MODEL=hexgrad/Kokoro-82M`: Uses a high-quality default model
|
||||||
- Footer is shown in the app shell
|
- `NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS=false`: Restricts usage to free models if no key is provided
|
||||||
- DOCX upload/conversion option is hidden
|
- `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true`: (Optional) Controls audiobook export UI
|
||||||
- Default provider/model behavior is production-oriented
|
- `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=false`: (Optional) Controls word highlighting UI (requires timestamp backend)
|
||||||
- DeepInfra model picker is restricted without an API key
|
|
||||||
- Privacy modal shows hosted-service/operator wording
|
|
||||||
- Dev-only destructive document actions are hidden
|
|
||||||
|
|
||||||
With unset/non-`production`, the inverse dev behavior applies.
|
|
||||||
|
|
||||||
Full details: [Environment Variables](../reference/environment-variables#next_public_node_env).
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::warning Auth recommendation
|
:::warning Auth recommendation
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,11 @@ This is the single reference page for OpenReader WebUI environment variables.
|
||||||
|
|
||||||
| Variable | Area | Default | When to set |
|
| Variable | Area | Default | When to set |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `NEXT_PUBLIC_NODE_ENV` | Runtime mode | treated as `development` unless `production` | Set `production` for production client behavior |
|
| `NEXT_PUBLIC_ENABLE_DOCX_CONVERSION` | Client feature flags | `true` unless set to `false` | Set `false` to hide DOCX support |
|
||||||
|
| `NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS` | Client feature flags | `true` unless set to `false` | Set `false` to hide destructive actions |
|
||||||
|
| `NEXT_PUBLIC_DEFAULT_TTS_PROVIDER` | Client feature flags | `custom-openai` | Override default TTS provider |
|
||||||
|
| `NEXT_PUBLIC_DEFAULT_TTS_MODEL` | Client feature flags | `kokoro` | Override default TTS model |
|
||||||
|
| `NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS` | Client feature flags | `true` unless set to `false` | Set `false` to restrict DeepInfra models |
|
||||||
| `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT` | Client feature flags | `true` unless set to `false` | Set `false` to hide audiobook export UI |
|
| `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT` | Client feature flags | `true` unless set to `false` | Set `false` to hide audiobook export UI |
|
||||||
| `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT` | Client feature flags | `false` unless set to `true` | Set `true` to enable word highlight + alignment |
|
| `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT` | Client feature flags | `false` unless set to `true` | Set `true` to enable word highlight + alignment |
|
||||||
| `API_BASE` | TTS provider | none | Point to your OpenAI-compatible TTS base URL |
|
| `API_BASE` | TTS provider | none | Point to your OpenAI-compatible TTS base URL |
|
||||||
|
|
@ -50,41 +54,7 @@ This is the single reference page for OpenReader WebUI environment variables.
|
||||||
| `WHISPER_CPP_BIN` | Word timing | unset | Set to enable `whisper.cpp` timestamps |
|
| `WHISPER_CPP_BIN` | Word timing | unset | Set to enable `whisper.cpp` timestamps |
|
||||||
| `FFMPEG_BIN` | Audio runtime | auto-detected (`ffmpeg-static`) | Override ffmpeg binary path |
|
| `FFMPEG_BIN` | Audio runtime | auto-detected (`ffmpeg-static`) | Override ffmpeg binary path |
|
||||||
|
|
||||||
## Client Runtime and Feature Flags
|
|
||||||
|
|
||||||
### NEXT_PUBLIC_NODE_ENV
|
|
||||||
|
|
||||||
Controls development vs production behavior in client/server code paths.
|
|
||||||
|
|
||||||
- Typical values: `development`, `production`
|
|
||||||
- OpenReader `isDev` checks rely on this variable directly
|
|
||||||
- If this is not `production`, OpenReader treats the client as development mode
|
|
||||||
- In deployed environments, set `NEXT_PUBLIC_NODE_ENV=production` explicitly for predictable production behavior
|
|
||||||
- Affects:
|
|
||||||
- Footer visibility in the app shell
|
|
||||||
- DOCX upload/conversion availability in upload UI
|
|
||||||
- Default provider/model behavior for first-run TTS config
|
|
||||||
- DeepInfra model picker restrictions when no API key is set
|
|
||||||
- Dev-only destructive document actions in settings
|
|
||||||
|
|
||||||
### NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT
|
|
||||||
|
|
||||||
Controls whether audiobook export UI/actions are shown in the client.
|
|
||||||
|
|
||||||
- Default behavior: enabled unless explicitly set to `false`
|
|
||||||
- Applies in both development and production
|
|
||||||
- Affects export entry points in PDF/EPUB pages and document settings UI
|
|
||||||
|
|
||||||
### NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT
|
|
||||||
|
|
||||||
Controls word-by-word highlighting UI and timestamp-alignment behavior.
|
|
||||||
|
|
||||||
- Default behavior: disabled unless set to `true`
|
|
||||||
- Applies in both development and production
|
|
||||||
- Requires working timestamp generation (for example `WHISPER_CPP_BIN`)
|
|
||||||
- Affects:
|
|
||||||
- Word-highlight toggles in document settings
|
|
||||||
- Alignment requests during TTS playback
|
|
||||||
|
|
||||||
## TTS Provider and Request Behavior
|
## TTS Provider and Request Behavior
|
||||||
|
|
||||||
|
|
@ -367,3 +337,59 @@ Absolute path or executable name for the ffmpeg binary used by audiobook/process
|
||||||
|
|
||||||
- Resolution order: `FFMPEG_BIN` -> `ffmpeg-static`
|
- Resolution order: `FFMPEG_BIN` -> `ffmpeg-static`
|
||||||
- Example: `/var/task/node_modules/ffmpeg-static/ffmpeg`
|
- Example: `/var/task/node_modules/ffmpeg-static/ffmpeg`
|
||||||
|
|
||||||
|
## Client Runtime and Feature Flags
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_ENABLE_DOCX_CONVERSION
|
||||||
|
|
||||||
|
Controls whether the experimental DOCX-to-PDF conversion and upload feature is enabled.
|
||||||
|
|
||||||
|
- Default: `true` (enabled)
|
||||||
|
- Set `false` to hide DOCX support in the upload UI
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS
|
||||||
|
|
||||||
|
Controls whether the "Delete all user docs" and other bulk-delete buttons are shown in Settings.
|
||||||
|
|
||||||
|
- Default: `true` (enabled)
|
||||||
|
- Set `false` to hide destructive actions (recommended for production)
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_DEFAULT_TTS_PROVIDER
|
||||||
|
|
||||||
|
Sets the default TTS provider for new users.
|
||||||
|
|
||||||
|
- Default: `custom-openai`
|
||||||
|
- Example values: `deepinfra`, `openai`, `custom-openai`
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_DEFAULT_TTS_MODEL
|
||||||
|
|
||||||
|
Sets the default TTS model for new users.
|
||||||
|
|
||||||
|
- Default: `kokoro`
|
||||||
|
- Example values: `hexgrad/Kokoro-82M`, `tts-1`
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS
|
||||||
|
|
||||||
|
Controls whether the DeepInfra model list shows all models or just the free tier when no API key is set.
|
||||||
|
|
||||||
|
- Default: `true` (show all)
|
||||||
|
- Set `false` to restrict to free tier models when no API key is provided
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT
|
||||||
|
|
||||||
|
Controls whether audiobook export UI/actions are shown in the client.
|
||||||
|
|
||||||
|
- Default behavior: enabled unless explicitly set to `false`
|
||||||
|
- Applies in both development and production
|
||||||
|
- Affects export entry points in PDF/EPUB pages and document settings UI
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT
|
||||||
|
|
||||||
|
Controls word-by-word highlighting UI and timestamp-alignment behavior.
|
||||||
|
|
||||||
|
- Default behavior: disabled unless set to `true`
|
||||||
|
- Applies in both development and production
|
||||||
|
- Requires working timestamp generation (for example `WHISPER_CPP_BIN`)
|
||||||
|
- Affects:
|
||||||
|
- Word-highlight toggles in document settings
|
||||||
|
- Alignment requests during TTS playback
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import { UploadIcon } from '@/components/icons/Icons';
|
||||||
import { useDocuments } from '@/contexts/DocumentContext';
|
import { useDocuments } from '@/contexts/DocumentContext';
|
||||||
import { uploadDocxAsPdf } from '@/lib/client-documents';
|
import { uploadDocxAsPdf } from '@/lib/client-documents';
|
||||||
|
|
||||||
const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null;
|
const enableDocx = process.env.NEXT_PUBLIC_ENABLE_DOCX_CONVERSION !== 'false';
|
||||||
|
|
||||||
|
|
||||||
interface DocumentUploaderProps {
|
interface DocumentUploaderProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
@ -38,7 +39,7 @@ export function DocumentUploader({ className = '', variant = 'default' }: Docume
|
||||||
await addEPUB(file);
|
await addEPUB(file);
|
||||||
} else if (file.type === 'text/plain' || file.type === 'text/markdown' || file.name.endsWith('.md')) {
|
} else if (file.type === 'text/plain' || file.type === 'text/markdown' || file.name.endsWith('.md')) {
|
||||||
await addHTML(file);
|
await addHTML(file);
|
||||||
} else if (isDev && file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
} else if (enableDocx && file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
||||||
// Preserve prior UX: show "Converting DOCX..." state rather than generic uploading.
|
// Preserve prior UX: show "Converting DOCX..." state rather than generic uploading.
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
setIsConverting(true);
|
setIsConverting(true);
|
||||||
|
|
@ -65,7 +66,7 @@ export function DocumentUploader({ className = '', variant = 'default' }: Docume
|
||||||
'application/epub+zip': ['.epub'],
|
'application/epub+zip': ['.epub'],
|
||||||
'text/plain': ['.txt'],
|
'text/plain': ['.txt'],
|
||||||
'text/markdown': ['.md'],
|
'text/markdown': ['.md'],
|
||||||
...(isDev ? {
|
...(enableDocx ? {
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx']
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx']
|
||||||
} : {})
|
} : {})
|
||||||
},
|
},
|
||||||
|
|
@ -111,7 +112,7 @@ export function DocumentUploader({ className = '', variant = 'default' }: Docume
|
||||||
{isDragActive ? 'Drop your file(s) here' : 'Drop your file(s) here, or click to select'}
|
{isDragActive ? 'Drop your file(s) here' : 'Drop your file(s) here, or click to select'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs sm:text-sm text-muted">
|
<p className="text-xs sm:text-sm text-muted">
|
||||||
{isDev ? 'PDF, EPUB, TXT, MD, or DOCX files are accepted' : 'PDF, EPUB, TXT, or MD files are accepted'}
|
{enableDocx ? 'PDF, EPUB, TXT, MD, or DOCX files are accepted' : 'PDF, EPUB, TXT, or MD files are accepted'}
|
||||||
</p>
|
</p>
|
||||||
{error && <p className="mt-2 text-sm text-red-500">{error}</p>}
|
{error && <p className="mt-2 text-sm text-red-500">{error}</p>}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ import { deleteDocuments, mimeTypeForDoc, uploadDocuments } from '@/lib/client-d
|
||||||
import { cacheStoredDocumentFromBytes, clearDocumentCache } from '@/lib/document-cache';
|
import { cacheStoredDocumentFromBytes, clearDocumentCache } from '@/lib/document-cache';
|
||||||
import { clearAllDocumentPreviewCaches, clearInMemoryDocumentPreviewCache } from '@/lib/document-preview-cache';
|
import { clearAllDocumentPreviewCaches, clearInMemoryDocumentPreviewCache } from '@/lib/document-preview-cache';
|
||||||
|
|
||||||
const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null;
|
const enableDestructiveDelete = process.env.NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS !== 'false';
|
||||||
|
const showAllDeepInfra = process.env.NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS !== 'false';
|
||||||
|
|
||||||
|
|
||||||
const themes = THEMES.map(id => ({
|
const themes = THEMES.map(id => ({
|
||||||
id,
|
id,
|
||||||
|
|
@ -108,7 +110,7 @@ export function SettingsModal({ className = '' }: { className?: string }) {
|
||||||
];
|
];
|
||||||
case 'deepinfra':
|
case 'deepinfra':
|
||||||
// In production without an API key, limit to free tier model
|
// In production without an API key, limit to free tier model
|
||||||
if (!isDev && !localApiKey) {
|
if (!showAllDeepInfra && !localApiKey) {
|
||||||
return [
|
return [
|
||||||
{ id: 'hexgrad/Kokoro-82M', name: 'hexgrad/Kokoro-82M' }
|
{ id: 'hexgrad/Kokoro-82M', name: 'hexgrad/Kokoro-82M' }
|
||||||
];
|
];
|
||||||
|
|
@ -561,7 +563,7 @@ export function SettingsModal({ className = '' }: { className?: string }) {
|
||||||
type="password"
|
type="password"
|
||||||
value={localApiKey}
|
value={localApiKey}
|
||||||
onChange={(e) => handleInputChange('apiKey', e.target.value)}
|
onChange={(e) => handleInputChange('apiKey', e.target.value)}
|
||||||
placeholder={!isDev && localTTSProvider === 'deepinfra' ? "Deepinfra free or use your API key" : "Using environment variable"}
|
placeholder={!showAllDeepInfra && localTTSProvider === 'deepinfra' ? "Deepinfra free or use your API key" : "Using environment variable"}
|
||||||
className="w-full rounded-lg bg-background py-1.5 px-3 text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent"
|
className="w-full rounded-lg bg-background py-1.5 px-3 text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -782,7 +784,7 @@ export function SettingsModal({ className = '' }: { className?: string }) {
|
||||||
>
|
>
|
||||||
Clear cache
|
Clear cache
|
||||||
</Button>
|
</Button>
|
||||||
{isDev && (
|
{enableDestructiveDelete && (
|
||||||
<div className="flex w-full gap-2">
|
<div className="flex w-full gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ type OpenReaderDB = Dexie & {
|
||||||
|
|
||||||
export const db = new Dexie(DB_NAME) as OpenReaderDB;
|
export const db = new Dexie(DB_NAME) as OpenReaderDB;
|
||||||
|
|
||||||
const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null;
|
|
||||||
|
|
||||||
type DexieOpenStatus = 'opening' | 'opened' | 'blocked' | 'stalled' | 'error';
|
type DexieOpenStatus = 'opening' | 'opened' | 'blocked' | 'stalled' | 'error';
|
||||||
|
|
||||||
|
|
@ -113,8 +113,8 @@ function inferProviderAndBaseUrl(raw: RawConfigMap): { provider: string; baseUrl
|
||||||
const cachedBaseUrl = raw.baseUrl;
|
const cachedBaseUrl = raw.baseUrl;
|
||||||
let inferredProvider = raw.ttsProvider || '';
|
let inferredProvider = raw.ttsProvider || '';
|
||||||
|
|
||||||
if (!isDev && !raw.ttsProvider) {
|
if (!raw.ttsProvider) {
|
||||||
inferredProvider = 'deepinfra';
|
inferredProvider = process.env.NEXT_PUBLIC_DEFAULT_TTS_PROVIDER || 'custom-openai';
|
||||||
} else if (!inferredProvider) {
|
} else if (!inferredProvider) {
|
||||||
if (cachedBaseUrl) {
|
if (cachedBaseUrl) {
|
||||||
const baseUrlLower = cachedBaseUrl.toLowerCase();
|
const baseUrlLower = cachedBaseUrl.toLowerCase();
|
||||||
|
|
@ -194,8 +194,8 @@ function buildAppConfigFromRaw(raw: RawConfigMap): AppConfigRow {
|
||||||
(provider === 'openai'
|
(provider === 'openai'
|
||||||
? 'tts-1'
|
? 'tts-1'
|
||||||
: provider === 'deepinfra'
|
: provider === 'deepinfra'
|
||||||
? 'hexgrad/Kokoro-82M'
|
? 'hexgrad/Kokoro-82M'
|
||||||
: APP_CONFIG_DEFAULTS.ttsModel),
|
: APP_CONFIG_DEFAULTS.ttsModel),
|
||||||
ttsInstructions: raw.ttsInstructions ?? APP_CONFIG_DEFAULTS.ttsInstructions,
|
ttsInstructions: raw.ttsInstructions ?? APP_CONFIG_DEFAULTS.ttsInstructions,
|
||||||
savedVoices,
|
savedVoices,
|
||||||
pdfHighlightEnabled:
|
pdfHighlightEnabled:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { DocumentListState } from '@/types/documents';
|
import type { DocumentListState } from '@/types/documents';
|
||||||
|
|
||||||
const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null;
|
|
||||||
const wordHighlightEnabledByDefault = process.env.NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT === 'true';
|
const wordHighlightEnabledByDefault = process.env.NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT === 'true';
|
||||||
|
|
||||||
export type ViewType = 'single' | 'dual' | 'scroll';
|
export type ViewType = 'single' | 'dual' | 'scroll';
|
||||||
|
|
@ -48,8 +48,8 @@ export const APP_CONFIG_DEFAULTS: AppConfigValues = {
|
||||||
footerMargin: 0,
|
footerMargin: 0,
|
||||||
leftMargin: 0,
|
leftMargin: 0,
|
||||||
rightMargin: 0,
|
rightMargin: 0,
|
||||||
ttsProvider: isDev ? 'custom-openai' : 'deepinfra',
|
ttsProvider: process.env.NEXT_PUBLIC_DEFAULT_TTS_PROVIDER || 'custom-openai',
|
||||||
ttsModel: isDev ? 'kokoro' : 'hexgrad/Kokoro-82M',
|
ttsModel: process.env.NEXT_PUBLIC_DEFAULT_TTS_MODEL || 'kokoro',
|
||||||
ttsInstructions: '',
|
ttsInstructions: '',
|
||||||
savedVoices: {},
|
savedVoices: {},
|
||||||
smartSentenceSplitting: true,
|
smartSentenceSplitting: true,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue