Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions openless-all/app/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ export const ja: typeof zhCN = {
muteDuringRecordingDesc: '音声入力開始時にシステム出力を一時的にミュートし、停止/キャンセル/エラー後に元のミュート状態を復元。スピーカーの音がマイクに入らないようにします。',
restoreClipboardLabel: '入力後にクリップボードを復元',
restoreClipboardDesc: 'Windows / Linux のみ:ペースト成功後に元のクリップボード内容を復元(既定 ON)。OFF にするとディクテーションテキストがクリップボードに残り、ペーストが効かなかった場合に手動で Ctrl+V できます。詳細は issue #111。',
comboRecordLabel: 'ショートカットを記録',
comboRecordDesc: 'クリック後、希望するキーの組み合わせ(例:⌘⇧D)を押してください。トグル / 押し続けの両方に対応。',
comboRecordBtn: 'ショートカットを記録',
comboRecordHint: 'ショートカットの組み合わせを押してください…',
comboRecorded: '記録済み',
comboClear: 'クリア',
comboConflict: 'このショートカットの組み合わせは使用できません',
allowNonTsfFallbackLabel: '非 TSF フォールバックを許可',
allowNonTsfFallbackDesc: 'Windows のみ:TSF による直接入力が失敗した場合、Unicode SendInput / ショートカットペースト / WM_PASTE への切り替えを許可。OFF にすると TSF 入力が実際に使われているか検証できます。',
startupAtBoot: '起動時に自動起動',
Expand Down Expand Up @@ -527,6 +534,7 @@ export const ja: typeof zhCN = {
rightCommand: '右 Command',
fn: 'Fn (地球キー)',
rightAlt: '右 Alt',
custom: 'カスタム',
},
fallback: 'グローバルショートカット',
modeHoldSuffix: '(押し続けて話す)',
Expand Down
8 changes: 8 additions & 0 deletions openless-all/app/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ export const ko: typeof zhCN = {
muteDuringRecordingDesc: '음성 입력 시작 시 시스템 출력을 일시적으로 음소거하고, 정지/취소/오류 후 원래 음소거 상태를 복원합니다. 스피커 소리가 마이크에 들어가지 않도록 합니다.',
restoreClipboardLabel: '입력 후 클립보드 복원',
restoreClipboardDesc: 'Windows / Linux 만: 붙여넣기 성공 후 원래 클립보드 내용을 복원합니다(기본 ON). OFF 시 받아쓰기 텍스트가 클립보드에 남아 붙여넣기가 실패한 경우 수동으로 Ctrl+V 할 수 있습니다. issue #111 참조.',
comboRecordLabel: '단축키 녹화',
comboRecordDesc: '클릭 후 원하는 단축키 조합(예: ⌘⇧D)을 누르세요. 토글 및 누르기 모드 모두 지원합니다.',
comboRecordBtn: '단축키 녹화',
comboRecordHint: '단축키 조합을 눌러 주세요…',
comboRecorded: '녹화됨',
comboClear: '지우기',
comboConflict: '이 단축키 조합은 사용할 수 없습니다',
allowNonTsfFallbackLabel: '비 TSF 폴백 허용',
allowNonTsfFallbackDesc: 'Windows 만: TSF 직접 입력이 실패할 경우 Unicode SendInput / 단축키 붙여넣기 / WM_PASTE 로 전환을 허용합니다. OFF 시 실제로 TSF 입력이 사용되는지 검증할 수 있습니다.',
startupAtBoot: '부팅 시 자동 시작',
Expand Down Expand Up @@ -527,6 +534,7 @@ export const ko: typeof zhCN = {
rightCommand: '오른쪽 Command',
fn: 'Fn (지구본 키)',
rightAlt: '오른쪽 Alt',
custom: '사용자 정의',
},
fallback: '전역 단축키',
modeHoldSuffix: '(눌러서 말하기)',
Expand Down
116 changes: 60 additions & 56 deletions openless-all/app/src/pages/SelectionAsk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,36 +58,18 @@ export function SelectionAsk() {

{/* 1. 触发快捷键 */}
<Card>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
<div style={{ fontSize: 13, fontWeight: 600 }}>{t('selectionAsk.hotkey.title')}</div>
<span
style={{
padding: '2px 8px',
fontSize: 10.5,
fontWeight: 600,
letterSpacing: '0.04em',
borderRadius: 999,
background: enabled ? 'rgba(37,99,235,0.10)' : 'rgba(0,0,0,0.05)',
color: enabled ? 'var(--ol-blue)' : 'var(--ol-ink-4)',
textTransform: 'uppercase',
}}
>
{enabled ? t('selectionAsk.statusEnabled') : t('selectionAsk.statusDisabled')}
</span>
</div>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginBottom: 12, lineHeight: 1.55 }}>
{t('selectionAsk.hotkey.desc', { recordHotkey: recordHotkeyLabel })}
</div>
<button
onClick={async () => {
<CardHeaderToggle
title={t('selectionAsk.hotkey.title')}
checked={enabled}
onToggle={async () => {
const nextHotkey = enabled ? null : defaultQaHotkey;
await setQaHotkey(nextHotkey);
await savePrefs({ ...prefs, qaHotkey: nextHotkey });
}}
style={{ fontSize: 12, padding: '5px 14px', background: enabled ? 'rgba(0,0,0,0.06)' : 'var(--ol-blue)', color: enabled ? 'var(--ol-ink-2)' : '#fff', border: 0, borderRadius: 6, fontFamily: 'inherit', fontWeight: 500, cursor: 'default', marginBottom: 10 }}
>
{enabled ? t('selectionAsk.hotkey.optionDisabled') : t('selectionAsk.statusEnabled')}
</button>
/>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginBottom: prefs.qaHotkey ? 12 : 0, lineHeight: 1.55 }}>
{t('selectionAsk.hotkey.desc', { recordHotkey: recordHotkeyLabel })}
</div>
{prefs.qaHotkey && (
<ShortcutRecorder
value={prefs.qaHotkey}
Expand All @@ -101,38 +83,14 @@ export function SelectionAsk() {

{/* 2. 历史保存 */}
<Card>
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>{t('selectionAsk.history.title')}</div>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginBottom: 12, lineHeight: 1.55 }}>
<CardHeaderToggle
title={t('selectionAsk.history.title')}
checked={prefs.qaSaveHistory}
onToggle={() => onSaveHistoryChange(!prefs.qaSaveHistory)}
/>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', lineHeight: 1.55 }}>
{t('selectionAsk.history.desc')}
</div>
<button
onClick={() => onSaveHistoryChange(!prefs.qaSaveHistory)}
style={{
position: 'relative',
width: 44,
height: 24,
borderRadius: 999,
border: 0,
background: prefs.qaSaveHistory ? 'var(--ol-blue)' : 'rgba(0,0,0,0.18)',
cursor: 'default',
transition: 'background 0.16s var(--ol-motion-quick)',
padding: 0,
}}
>
<span
style={{
position: 'absolute',
top: 2,
left: prefs.qaSaveHistory ? 22 : 2,
width: 20,
height: 20,
borderRadius: 999,
background: '#fff',
boxShadow: '0 1px 2px rgba(0,0,0,.18)',
transition: 'left .16s var(--ol-motion-spring)',
}}
/>
</button>
</Card>

{/* 3. 使用方法 */}
Expand Down Expand Up @@ -181,3 +139,49 @@ export function SelectionAsk() {
</>
);
}

// 卡片标题行右侧开关:与 Style 页面顶栏的 36×20 toggle 同款,保持全局视觉一致。
function CardHeaderToggle({
title,
checked,
onToggle,
}: {
title: string;
checked: boolean;
onToggle: () => void;
}) {
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
<div style={{ fontSize: 13, fontWeight: 600 }}>{title}</div>
<button
onClick={onToggle}
aria-pressed={checked}
style={{
position: 'relative',
width: 36,
height: 20,
borderRadius: 999,
border: 0,
background: checked ? 'var(--ol-blue)' : 'rgba(0,0,0,0.15)',
cursor: 'default',
transition: 'background 0.16s var(--ol-motion-quick)',
padding: 0,
}}
>
<span
style={{
position: 'absolute',
top: 2,
left: checked ? 18 : 2,
width: 16,
height: 16,
borderRadius: 999,
background: '#fff',
boxShadow: '0 1px 2px rgba(0,0,0,.2)',
transition: 'left .16s var(--ol-motion-spring)',
}}
/>
</button>
</div>
);
}
Loading