diff --git a/src/components/practice/ProficientInputArea.tsx b/src/components/practice/ProficientInputArea.tsx
index 8f48e8e..8f753c3 100644
--- a/src/components/practice/ProficientInputArea.tsx
+++ b/src/components/practice/ProficientInputArea.tsx
@@ -7,7 +7,6 @@ interface ProficientInputAreaProps {
isComposing: boolean
inputLocked: boolean
hasError: boolean
- statusText?: string | null
onChange: (value: string) => void
onCompositionStart: () => void
onCompositionEnd: (value: string) => void
@@ -18,7 +17,6 @@ export function ProficientInputArea({
isComposing,
inputLocked,
hasError,
- statusText,
onChange,
onCompositionStart,
onCompositionEnd,
@@ -40,7 +38,7 @@ export function ProficientInputArea({
onCompositionStart={onCompositionStart}
onCompositionEnd={(e) => onCompositionEnd(e.currentTarget.value)}
disabled={inputLocked}
- placeholder="输入当前分组中的一个完整条目,停顿 800ms 自动核对"
+ placeholder="输入当前分组中的一个完整条目"
className={clsx(
'w-full rounded-xl border px-4 py-3 text-base md:text-lg',
'bg-white dark:bg-gray-800/80 text-gray-900 dark:text-gray-100',
@@ -56,33 +54,18 @@ export function ProficientInputArea({
spellCheck="false"
/>
-
-
- {inputLocked ? (
-
- 答案显示中,输入已锁定
-
- ) : statusText ? (
-
- {statusText}
-
- ) : null}
-
-
+
+ {inputLocked && (
+
+ 答案显示中,输入已锁定
+
+ )}
+
)
}
diff --git a/src/data/changelog.json b/src/data/changelog.json
index e715826..7aa3363 100644
--- a/src/data/changelog.json
+++ b/src/data/changelog.json
@@ -1,5 +1,19 @@
{
"changelogEntries": [
+ {
+ "id": "2026-03-24-process-detail-shortcut-restore",
+ "date": "2026-03-24",
+ "type": "fix",
+ "title": "恢复过程详情练习模式的 Ctrl+H 查看答案快捷键",
+ "scope": "过程详情"
+ },
+ {
+ "id": "2026-03-24-proficient-mode-status-cleanup",
+ "date": "2026-03-24",
+ "type": "fix",
+ "title": "移除熟练模式底部多余说明与命中提示,精简输入区反馈",
+ "scope": "过程详情"
+ },
{
"id": "2026-03-23-process-detail-proficient-mode",
"date": "2026-03-23",
diff --git a/src/pages/ProcessDetailPage.tsx b/src/pages/ProcessDetailPage.tsx
index 232b9c1..81a3d4a 100644
--- a/src/pages/ProcessDetailPage.tsx
+++ b/src/pages/ProcessDetailPage.tsx
@@ -169,7 +169,6 @@ export function ProcessDetailPage() {
const [currentSection, setCurrentSection] = useState('inputs')
const [proficientInput, setProficientInput] = useState('')
const [proficientHasError, setProficientHasError] = useState(false)
- const [proficientStatusText, setProficientStatusText] = useState(null)
const [inputLocked, setInputLocked] = useState(false)
const [lastErrorTimestamp, setLastErrorTimestamp] = useState(null)
@@ -253,7 +252,6 @@ export function ProcessDetailPage() {
setCurrentSection(getFirstAvailableSection(itemsBySection))
setProficientInput('')
setProficientHasError(false)
- setProficientStatusText(null)
setLastErrorTimestamp(null)
setShowAnswer(false)
setInputLocked(false)
@@ -292,12 +290,10 @@ export function ProcessDetailPage() {
setCurrentPracticeId(null)
setProficientInput('')
setProficientHasError(false)
- setProficientStatusText('当前分组内可乱序输入,停顿 800ms 自动核对。')
} else {
setCurrentPracticeId(practiceItems[0].id)
setProficientInput('')
setProficientHasError(false)
- setProficientStatusText(null)
}
}, [
practiceItems,
@@ -326,6 +322,48 @@ export function ProcessDetailPage() {
exitPractice()
}, [id, practiceMode, exitPractice])
+ useEffect(() => {
+ if (!isPracticeMode) return
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.ctrlKey && event.key.toLowerCase() === 'h') {
+ event.preventDefault()
+ clearLongPressTimer()
+ clearDebounceTimer()
+ clearAnswerRevealTimer()
+
+ if (practiceMode === 'proficient') {
+ setProficientInput('')
+ setProficientHasError(false)
+ }
+
+ setShowAnswer(true)
+ setInputLocked(true)
+ }
+ }
+
+ const handleKeyUp = (event: KeyboardEvent) => {
+ if (event.key === 'Control' || event.key.toLowerCase() === 'h') {
+ hideAnswerAndUnlock()
+ }
+ }
+
+ window.addEventListener('keydown', handleKeyDown)
+ window.addEventListener('keyup', handleKeyUp)
+
+ return () => {
+ window.removeEventListener('keydown', handleKeyDown)
+ window.removeEventListener('keyup', handleKeyUp)
+ }
+ }, [
+ isPracticeMode,
+ practiceMode,
+ hideAnswerAndUnlock,
+ clearLongPressTimer,
+ clearDebounceTimer,
+ clearAnswerRevealTimer,
+ ])
+
const revealAnswersForThreeSeconds = useCallback(() => {
if (!isPracticeMode) return
clearLongPressTimer()
@@ -464,7 +502,6 @@ export function ProcessDetailPage() {
if (!matchedItem) {
setProficientHasError(true)
- setProficientStatusText(`未匹配到当前${SECTION_LABELS[currentSection]}分组中的条目,请继续修改。`)
return
}
@@ -478,7 +515,6 @@ export function ProcessDetailPage() {
const allDone = practiceItems.every((item) => nextAnsweredItems.has(item.id))
if (allDone) {
- setProficientStatusText('全部条目已完成,正在结束练习。')
finishPractice()
return
}
@@ -487,15 +523,9 @@ export function ProcessDetailPage() {
const nextSection = getNextSection(currentSection, itemsBySection, nextAnsweredItems)
if (nextSection) {
setCurrentSection(nextSection)
- setProficientStatusText(`当前${SECTION_LABELS[currentSection]}已完成,已切换到${SECTION_LABELS[nextSection]}。`)
- } else {
- setProficientStatusText(`当前${SECTION_LABELS[currentSection]}已完成。`)
}
return
}
-
- const doneCount = itemsBySection[currentSection].filter((item) => nextAnsweredItems.has(item.id)).length
- setProficientStatusText(`已命中:${matchedItem.name}(${SECTION_LABELS[currentSection]} ${doneCount}/${itemsBySection[currentSection].length})`)
}, [answeredItems, currentSection, inputLocked, itemsBySection, practiceItems, finishPractice])
useEffect(() => {
@@ -682,7 +712,6 @@ export function ProcessDetailPage() {
setCurrentSection(section)
setProficientInput('')
setProficientHasError(false)
- setProficientStatusText(`已切换到${SECTION_LABELS[section]}分组。`)
}}
className={`flex items-center gap-2 text-left ${canSwitchSection ? 'cursor-pointer' : 'cursor-default'}`}
>
@@ -794,13 +823,9 @@ export function ProcessDetailPage() {
isComposing={isComposing}
inputLocked={inputLocked}
hasError={proficientHasError}
- statusText={proficientStatusText}
onChange={(value) => {
setProficientInput(value)
setProficientHasError(false)
- if (!value) {
- setProficientStatusText('当前分组内可乱序输入,停顿 800ms 自动核对。')
- }
}}
onCompositionStart={() => handleCompositionStart()}
onCompositionEnd={(value) => handleCompositionEnd(value)}