6 files changed
+38
-20
No files matched your search
@@ -119,11 +119,12 @@ interface CircleButtonProps {
|
||||
}
|
||||
|
||||
function CircleButton({ variant, enabled, onClick }: CircleButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
const isCancel = variant === 'cancel';
|
||||
return (
|
||||
<button
|
||||
onClick={enabled ? onClick : undefined}
|
||||
aria-label={isCancel ? 'cancel' : 'confirm'}
|
||||
aria-label={isCancel ? t('common.cancel') : t('settings.shortcuts.confirm')}
|
||||
disabled={!enabled}
|
||||
style={{
|
||||
width: 28,
|
||||
|
||||
@@ -24,6 +24,8 @@ export const en: typeof zhCN = {
|
||||
copied: 'Copied',
|
||||
operationFailed: 'Operation failed',
|
||||
add: 'Add',
|
||||
durationSeconds: '{{value}}s',
|
||||
durationMinutes: '{{value}}m',
|
||||
},
|
||||
capsule: {
|
||||
thinking: 'Thinking…',
|
||||
@@ -193,6 +195,12 @@ export const en: typeof zhCN = {
|
||||
},
|
||||
fillDefault: 'Fill default value',
|
||||
readFailed: 'Read failed',
|
||||
apiKeyLabel: 'API Key',
|
||||
baseUrlLabel: 'Base URL',
|
||||
modelLabel: 'Model',
|
||||
appKeyLabel: 'App Key',
|
||||
accessKeyLabel: 'Access Key',
|
||||
resourceIdLabel: 'Resource ID',
|
||||
},
|
||||
shortcuts: {
|
||||
title: 'Shortcut reference',
|
||||
|
||||
@@ -22,6 +22,8 @@ export const zhCN = {
|
||||
copied: '已复制',
|
||||
operationFailed: '操作失败',
|
||||
add: '添加',
|
||||
durationSeconds: '{{value}} 秒',
|
||||
durationMinutes: '{{value}} 分钟',
|
||||
},
|
||||
capsule: {
|
||||
thinking: '正在思考中',
|
||||
@@ -191,6 +193,12 @@ export const zhCN = {
|
||||
},
|
||||
fillDefault: '填入默认值',
|
||||
readFailed: '读取失败',
|
||||
apiKeyLabel: 'API 密钥',
|
||||
baseUrlLabel: '接口地址',
|
||||
modelLabel: '模型',
|
||||
appKeyLabel: 'App Key(应用密钥)',
|
||||
accessKeyLabel: 'Access Key(访问密钥)',
|
||||
resourceIdLabel: '资源 ID',
|
||||
},
|
||||
shortcuts: {
|
||||
title: '快捷键速查',
|
||||
|
||||
@@ -151,7 +151,7 @@ export function History() {
|
||||
{formatTime(s.createdAt)}
|
||||
</span>
|
||||
<span style={{ fontSize: 10, color: 'var(--ol-ink-4)', fontFamily: 'var(--ol-font-mono)' }}>
|
||||
{formatDuration(s.durationMs)}
|
||||
{formatDuration(s.durationMs, t)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--ol-ink-2)', lineHeight: 1.45, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
|
||||
@@ -170,7 +170,7 @@ export function History() {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<span style={{ fontSize: 13, fontFamily: 'var(--ol-font-mono)', color: 'var(--ol-ink-3)' }}>{formatTime(item.createdAt)}</span>
|
||||
<Pill size="sm" tone="default">{MODE_LABEL[item.mode]}</Pill>
|
||||
<span style={{ fontSize: 11, color: 'var(--ol-ink-4)' }}>{formatDuration(item.durationMs)}</span>
|
||||
<span style={{ fontSize: 11, color: 'var(--ol-ink-4)' }}>{formatDuration(item.durationMs, t)}</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<Btn icon="copy" variant="ghost" size="sm" onClick={onCopy}>{t('common.copy')}</Btn>
|
||||
@@ -227,9 +227,9 @@ function formatTime(iso: string): string {
|
||||
return `${d.getMonth() + 1}/${d.getDate()} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
}
|
||||
|
||||
function formatDuration(ms: number | null): string {
|
||||
function formatDuration(ms: number | null, t: ReturnType<typeof useTranslation>['t']): string {
|
||||
if (ms == null || ms <= 0) return '—';
|
||||
const sec = ms / 1000;
|
||||
if (sec < 60) return `${sec.toFixed(1)}s`;
|
||||
return `${(sec / 60).toFixed(1)}m`;
|
||||
if (sec < 60) return t('common.durationSeconds', { value: sec.toFixed(1) });
|
||||
return t('common.durationMinutes', { value: (sec / 60).toFixed(1) });
|
||||
}
|
||||
@@ -112,8 +112,8 @@ export function Overview({ onOpenHistory }: OverviewProps) {
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 18 }}>
|
||||
<Metric icon="hash" label={t('overview.metricChars')} value={metrics.charsToday.toLocaleString()} trend={t('overview.metricSegments', { count: metrics.segmentsToday })} />
|
||||
<Metric icon="mic" label={t('overview.metricDuration')} value={formatDuration(metrics.totalDurationMs)} trend="" />
|
||||
<Metric icon="clock" label={t('overview.metricAvg')} value={formatDuration(metrics.avgLatencyMs)} trend={metrics.segmentsToday > 0 ? t('overview.metricAvgTrend') : t('overview.metricNoData')} />
|
||||
<Metric icon="mic" label={t('overview.metricDuration')} value={formatDuration(metrics.totalDurationMs, t)} trend="" />
|
||||
<Metric icon="clock" label={t('overview.metricAvg')} value={formatDuration(metrics.avgLatencyMs, t)} trend={metrics.segmentsToday > 0 ? t('overview.metricAvgTrend') : t('overview.metricNoData')} />
|
||||
<Metric icon="bolt" label={t('overview.metricTotal')} value={String(history.length)} trend={t('overview.metricTotalTrend')} accent />
|
||||
</div>
|
||||
|
||||
@@ -241,6 +241,7 @@ function WeekChart({ data }: { data: number[] }) {
|
||||
}
|
||||
|
||||
function RecentRow({ session, modeLabel }: { session: DictationSession; modeLabel: Record<PolishMode, string> }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div style={{ padding: '12px 18px', borderBottom: '0.5px solid var(--ol-line-soft)', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 4, minWidth: 60 }}>
|
||||
@@ -253,7 +254,7 @@ function RecentRow({ session, modeLabel }: { session: DictationSession; modeLabe
|
||||
{session.finalText.split('\n')[0]}
|
||||
</div>
|
||||
<span style={{ fontSize: 10.5, color: 'var(--ol-ink-4)', fontFamily: 'var(--ol-font-mono)' }}>
|
||||
{formatDuration(session.durationMs ?? 0)}
|
||||
{formatDuration(session.durationMs ?? 0, t)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
@@ -269,10 +270,10 @@ function formatTime(iso: string): string {
|
||||
return `${d.getMonth() + 1}/${d.getDate()}`;
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
function formatDuration(ms: number, t: ReturnType<typeof useTranslation>['t']): string {
|
||||
if (ms <= 0) return '—';
|
||||
const sec = ms / 1000;
|
||||
if (sec < 60) return `${sec.toFixed(1)}s`;
|
||||
if (sec < 60) return t('common.durationSeconds', { value: sec.toFixed(1) });
|
||||
return `${Math.floor(sec / 60)}:${String(Math.floor(sec % 60)).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -295,10 +295,10 @@ function ProvidersSection() {
|
||||
))}
|
||||
</select>
|
||||
</SettingRow>
|
||||
<CredentialField key={`${llmProvider}:api_key`} label="API Key" account="ark.api_key" mono mask />
|
||||
<CredentialField key={`${llmProvider}:endpoint`} label="Base URL" account="ark.endpoint"
|
||||
<CredentialField key={`${llmProvider}:api_key`} label={t('settings.providers.apiKeyLabel')} account="ark.api_key" mono mask />
|
||||
<CredentialField key={`${llmProvider}:endpoint`} label={t('settings.providers.baseUrlLabel')} account="ark.endpoint"
|
||||
placeholder={preset.baseUrl || 'https://your-endpoint/v1'} />
|
||||
<CredentialField key={`${llmProvider}:model`} label="Model" account="ark.model_id"
|
||||
<CredentialField key={`${llmProvider}:model`} label={t('settings.providers.modelLabel')} account="ark.model_id"
|
||||
placeholder={preset.modelPlaceholder || 'model-name'} mono />
|
||||
</Card>
|
||||
|
||||
@@ -320,17 +320,17 @@ function ProvidersSection() {
|
||||
</SettingRow>
|
||||
{asrProvider === 'volcengine' ? (
|
||||
<>
|
||||
<CredentialField key={`${asrProvider}:app_key`} label="App Key" account="volcengine.app_key" mono mask />
|
||||
<CredentialField key={`${asrProvider}:access_key`} label="Access Key" account="volcengine.access_key" mono mask />
|
||||
<CredentialField key={`${asrProvider}:resource_id`} label="Resource ID" account="volcengine.resource_id" mono
|
||||
<CredentialField key={`${asrProvider}:app_key`} label={t('settings.providers.appKeyLabel')} account="volcengine.app_key" mono mask />
|
||||
<CredentialField key={`${asrProvider}:access_key`} label={t('settings.providers.accessKeyLabel')} account="volcengine.access_key" mono mask />
|
||||
<CredentialField key={`${asrProvider}:resource_id`} label={t('settings.providers.resourceIdLabel')} account="volcengine.resource_id" mono
|
||||
placeholder={ASR_DEFAULT_RESOURCE_ID} defaultValue={ASR_DEFAULT_RESOURCE_ID} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CredentialField key={`${asrProvider}:api_key`} label="API Key" account="asr.api_key" mono mask />
|
||||
<CredentialField key={`${asrProvider}:endpoint`} label="Base URL" account="asr.endpoint"
|
||||
<CredentialField key={`${asrProvider}:api_key`} label={t('settings.providers.apiKeyLabel')} account="asr.api_key" mono mask />
|
||||
<CredentialField key={`${asrProvider}:endpoint`} label={t('settings.providers.baseUrlLabel')} account="asr.endpoint"
|
||||
placeholder="https://api.openai.com/v1" defaultValue="https://api.openai.com/v1" />
|
||||
<CredentialField key={`${asrProvider}:model`} label="Model" account="asr.model"
|
||||
<CredentialField key={`${asrProvider}:model`} label={t('settings.providers.modelLabel')} account="asr.model"
|
||||
placeholder="whisper-1" />
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user