[251016] fix: VUI Test-2
🕐 커밋 시간: 2025. 10. 16. 15:01:09 📊 변경 통계: • 총 파일: 5개 • 추가: +240줄 • 삭제: -48줄 📁 추가된 파일: + com.twin.app.shoptime/vui-test.1.md + com.twin.app.shoptime/web-speech.md 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.module.less ~ com.twin.app.shoptime/src/views/VoicePanel/VoicePanel.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/web-speech.md (md파일): ✅ Added: Framework(), Hook(), constructor(), checkSupport(), initialize(), SpeechRecognition(), setupEventHandlers(), onStart(), onResult(), onError(), getErrorMessage(), onEnd(), start(), abort(), cleanup(), WebSpeechService(), dispatch(), Date(), useDispatch(), useSelector(), useEffect(), initializeWebSpeech(), onSTTText(), useCallback(), SearchPanel(), useState(), setSearchQuery(), setTimeout(), setIsVoiceOverlayVisible(), useWebSpeech(), setVoiceMode(), stopListening(), handleSearchSubmit(), onSearchChange(), setCurrentMode(), onClose(), stopPropagation(), classNames(), renderModeContent(), async(), getUserMedia(), getTracks(), preventDefault(), startListening(), useSearchVoice() 🔧 주요 변경 내용: • 테스트 커버리지 및 안정성 향상 • 개발 문서 및 가이드 개선
This commit is contained in:
@@ -37,9 +37,16 @@ export const VOICE_MODES = {
|
||||
MODE_4: 'mode4', // 추후 추가
|
||||
};
|
||||
|
||||
// 음성인식 입력 모드 (VUI vs WebSpeech)
|
||||
export const VOICE_INPUT_MODE = {
|
||||
VUI: 'vui', // VUI (Voice UI Framework)
|
||||
WEBSPEECH: 'webspeech', // Web Speech API
|
||||
};
|
||||
|
||||
const OVERLAY_SPOTLIGHT_ID = 'voice-input-overlay-container';
|
||||
const INPUT_SPOTLIGHT_ID = 'voice-overlay-input-box';
|
||||
const MIC_SPOTLIGHT_ID = 'voice-overlay-mic-button';
|
||||
const MIC_WEBSPEECH_SPOTLIGHT_ID = 'voice-overlay-mic-webspeech-button';
|
||||
|
||||
const VoiceInputOverlay = ({
|
||||
isVisible,
|
||||
@@ -54,11 +61,15 @@ const VoiceInputOverlay = ({
|
||||
const lastFocusedElement = useRef(null);
|
||||
const [inputFocus, setInputFocus] = useState(false);
|
||||
const [micFocused, setMicFocused] = useState(false);
|
||||
const [micWebSpeechFocused, setMicWebSpeechFocused] = useState(false);
|
||||
// 내부 모드 상태 관리 (prompt -> listening -> close)
|
||||
const [currentMode, setCurrentMode] = useState(mode);
|
||||
// 음성인식 입력 모드 (VUI vs WebSpeech)
|
||||
const [voiceInputMode, setVoiceInputMode] = useState(null);
|
||||
|
||||
// ⛔ VUI 테스트 비활성화: VoicePanel 독립 테스트 시 충돌 방지
|
||||
// Redux에서 voice 상태 가져오기
|
||||
const { isRegistered, lastSTTText, sttTimestamp } = useSelector((state) => state.voice);
|
||||
// const { isRegistered, lastSTTText, sttTimestamp } = useSelector((state) => state.voice);
|
||||
|
||||
// Redux에서 shopperHouse 검색 결과 가져오기
|
||||
const shopperHouseData = useSelector((state) => state.search.shopperHouseData);
|
||||
@@ -78,25 +89,26 @@ const VoiceInputOverlay = ({
|
||||
}
|
||||
}, [shopperHouseData, isVisible, onClose]);
|
||||
|
||||
// ⛔ VUI 테스트 비활성화: STT 텍스트 수신 처리
|
||||
// STT 텍스트 수신 시 처리
|
||||
useEffect(() => {
|
||||
if (lastSTTText && sttTimestamp && isVisible) {
|
||||
console.log('[VoiceInputOverlay] STT text received in overlay:', lastSTTText);
|
||||
// useEffect(() => {
|
||||
// if (lastSTTText && sttTimestamp && isVisible) {
|
||||
// console.log('[VoiceInputOverlay] STT text received in overlay:', lastSTTText);
|
||||
|
||||
// 입력창에 텍스트 표시 (부모 컴포넌트로 전달)
|
||||
if (onSearchChange) {
|
||||
onSearchChange({ value: lastSTTText });
|
||||
}
|
||||
// // 입력창에 텍스트 표시 (부모 컴포넌트로 전달)
|
||||
// if (onSearchChange) {
|
||||
// onSearchChange({ value: lastSTTText });
|
||||
// }
|
||||
|
||||
// listening 모드로 전환 (시각적 피드백)
|
||||
setCurrentMode(VOICE_MODES.LISTENING);
|
||||
// // listening 모드로 전환 (시각적 피드백)
|
||||
// setCurrentMode(VOICE_MODES.LISTENING);
|
||||
|
||||
// 1초 후 자동 닫기 (선택사항)
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
}, 1000);
|
||||
}
|
||||
}, [lastSTTText, sttTimestamp, isVisible, onSearchChange, onClose]);
|
||||
// // 1초 후 자동 닫기 (선택사항)
|
||||
// setTimeout(() => {
|
||||
// onClose();
|
||||
// }, 1000);
|
||||
// }
|
||||
// }, [lastSTTText, sttTimestamp, isVisible, onSearchChange, onClose]);
|
||||
|
||||
// Overlay가 열릴 때 포커스를 overlay 내부로 이동
|
||||
useEffect(() => {
|
||||
@@ -106,13 +118,15 @@ const VoiceInputOverlay = ({
|
||||
|
||||
// 모드 초기화 (항상 prompt 모드로 시작)
|
||||
setCurrentMode(mode);
|
||||
setVoiceInputMode(null);
|
||||
|
||||
// Overlay 내부로 포커스 이동
|
||||
setTimeout(() => {
|
||||
Spotlight.focus(OVERLAY_SPOTLIGHT_ID);
|
||||
}, 100);
|
||||
} else {
|
||||
// Overlay가 닫힐 때 원래 포커스 복원
|
||||
// Overlay가 닫힐 때 원래 포커스 복원 및 상태 초기화
|
||||
setVoiceInputMode(null);
|
||||
if (lastFocusedElement.current) {
|
||||
setTimeout(() => {
|
||||
Spotlight.focus(lastFocusedElement.current);
|
||||
@@ -199,7 +213,7 @@ const VoiceInputOverlay = ({
|
||||
setInputFocus(false);
|
||||
}, []);
|
||||
|
||||
// 마이크 버튼 포커스 핸들러
|
||||
// 마이크 버튼 포커스 핸들러 (VUI)
|
||||
const handleMicFocus = useCallback(() => {
|
||||
setMicFocused(true);
|
||||
}, []);
|
||||
@@ -208,10 +222,55 @@ const VoiceInputOverlay = ({
|
||||
setMicFocused(false);
|
||||
}, []);
|
||||
|
||||
// 마이크 버튼 클릭 (모드 전환: prompt -> listening -> close)
|
||||
const handleMicClick = useCallback(
|
||||
// WebSpeech 마이크 버튼 포커스 핸들러
|
||||
const handleMicWebSpeechFocus = useCallback(() => {
|
||||
setMicWebSpeechFocused(true);
|
||||
}, []);
|
||||
|
||||
const handleMicWebSpeechBlur = useCallback(() => {
|
||||
setMicWebSpeechFocused(false);
|
||||
}, []);
|
||||
|
||||
// ⛔ VUI 테스트 비활성화: VUI 마이크 버튼 클릭 핸들러
|
||||
// VUI 마이크 버튼 클릭 (모드 전환: prompt -> listening -> close)
|
||||
// const handleVUIMicClick = useCallback(
|
||||
// (e) => {
|
||||
// console.log('[VoiceInputOverlay] handleVUIMicClick called, currentMode:', currentMode);
|
||||
|
||||
// // 이벤트 전파 방지 - dim 레이어의 onClick 실행 방지
|
||||
// if (e && e.stopPropagation) {
|
||||
// e.stopPropagation();
|
||||
// }
|
||||
// if (e && e.nativeEvent && e.nativeEvent.stopImmediatePropagation) {
|
||||
// e.nativeEvent.stopImmediatePropagation();
|
||||
// }
|
||||
|
||||
// if (currentMode === VOICE_MODES.PROMPT) {
|
||||
// // prompt 모드에서 클릭 시 -> VUI listening 모드로 전환
|
||||
// console.log('[VoiceInputOverlay] Switching to VUI LISTENING mode');
|
||||
// setVoiceInputMode(VOICE_INPUT_MODE.VUI);
|
||||
// setCurrentMode(VOICE_MODES.LISTENING);
|
||||
// // 이 시점에서 webOS Voice Framework가 자동으로 음성인식 시작
|
||||
// // (이미 registerVoiceFramework()로 등록되어 있으므로)
|
||||
// } else if (currentMode === VOICE_MODES.LISTENING && voiceInputMode === VOICE_INPUT_MODE.VUI) {
|
||||
// // VUI listening 모드에서 클릭 시 -> 종료
|
||||
// console.log('[VoiceInputOverlay] Closing from VUI LISTENING mode');
|
||||
// setVoiceInputMode(null);
|
||||
// onClose();
|
||||
// } else {
|
||||
// // 기타 모드에서는 바로 종료
|
||||
// console.log('[VoiceInputOverlay] Closing from other mode');
|
||||
// setVoiceInputMode(null);
|
||||
// onClose();
|
||||
// }
|
||||
// },
|
||||
// [currentMode, voiceInputMode, onClose]
|
||||
// );
|
||||
|
||||
// WebSpeech 마이크 버튼 클릭 (모드 전환: prompt -> listening -> close)
|
||||
const handleWebSpeechMicClick = useCallback(
|
||||
(e) => {
|
||||
console.log('[VoiceInputOverlay] handleMicClick called, currentMode:', currentMode);
|
||||
console.log('[VoiceInputOverlay] handleWebSpeechMicClick called, currentMode:', currentMode);
|
||||
|
||||
// 이벤트 전파 방지 - dim 레이어의 onClick 실행 방지
|
||||
if (e && e.stopPropagation) {
|
||||
@@ -222,28 +281,34 @@ const VoiceInputOverlay = ({
|
||||
}
|
||||
|
||||
if (currentMode === VOICE_MODES.PROMPT) {
|
||||
// prompt 모드에서 클릭 시 -> listening 모드로 전환
|
||||
console.log('[VoiceInputOverlay] Switching to LISTENING mode');
|
||||
// prompt 모드에서 클릭 시 -> WebSpeech listening 모드로 전환
|
||||
console.log('[VoiceInputOverlay] Switching to WebSpeech LISTENING mode');
|
||||
setVoiceInputMode(VOICE_INPUT_MODE.WEBSPEECH);
|
||||
setCurrentMode(VOICE_MODES.LISTENING);
|
||||
// 이 시점에서 webOS Voice Framework가 자동으로 음성인식 시작
|
||||
// (이미 registerVoiceFramework()로 등록되어 있으므로)
|
||||
} else if (currentMode === VOICE_MODES.LISTENING) {
|
||||
// listening 모드에서 클릭 시 -> 종료
|
||||
console.log('[VoiceInputOverlay] Closing from LISTENING mode');
|
||||
// TODO: Web Speech API 시작 로직 추가
|
||||
} else if (
|
||||
currentMode === VOICE_MODES.LISTENING &&
|
||||
voiceInputMode === VOICE_INPUT_MODE.WEBSPEECH
|
||||
) {
|
||||
// WebSpeech listening 모드에서 클릭 시 -> 종료
|
||||
console.log('[VoiceInputOverlay] Closing from WebSpeech LISTENING mode');
|
||||
setVoiceInputMode(null);
|
||||
onClose();
|
||||
} else {
|
||||
// 기타 모드에서는 바로 종료
|
||||
console.log('[VoiceInputOverlay] Closing from other mode');
|
||||
setVoiceInputMode(null);
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
[currentMode, onClose]
|
||||
[currentMode, voiceInputMode, onClose]
|
||||
);
|
||||
|
||||
// dim 레이어 클릭 핸들러 (마이크 버튼과 분리)
|
||||
const handleDimClick = useCallback(
|
||||
(e) => {
|
||||
console.log('[VoiceInputOverlay] dimBackground clicked');
|
||||
setVoiceInputMode(null);
|
||||
onClose();
|
||||
},
|
||||
[onClose]
|
||||
@@ -264,8 +329,8 @@ const VoiceInputOverlay = ({
|
||||
{/* 배경 dim 레이어 - 클릭하면 닫힘 */}
|
||||
<div className={css.dimBackground} onClick={handleDimClick} />
|
||||
|
||||
{/* Voice 등록 상태 표시 (디버깅용) */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
{/* ⛔ VUI 테스트 비활성화: Voice 등록 상태 표시 (디버깅용) */}
|
||||
{/* {process.env.NODE_ENV === 'development' && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -276,8 +341,10 @@ const VoiceInputOverlay = ({
|
||||
}}
|
||||
>
|
||||
Voice: {isRegistered ? '✓ Ready' : '✗ Not Ready'}
|
||||
<br />
|
||||
Mode: {voiceInputMode || 'None'}
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{/* 모드별 컨텐츠 영역 - Spotlight Container (self-only) */}
|
||||
<OverlayContainer
|
||||
@@ -300,19 +367,27 @@ const VoiceInputOverlay = ({
|
||||
onFocus={handleInputFocus}
|
||||
onBlur={handleInputBlur}
|
||||
/>
|
||||
{/* VUI 마이크 버튼 (⛔ 기능 비활성화: 클릭 핸들러만 무효화) */}
|
||||
<SpottableMicButton
|
||||
className={classNames(
|
||||
css.microphoneButton,
|
||||
css.active,
|
||||
currentMode === VOICE_MODES.LISTENING && css.listening,
|
||||
currentMode === VOICE_MODES.LISTENING &&
|
||||
voiceInputMode === VOICE_INPUT_MODE.VUI &&
|
||||
css.listening,
|
||||
micFocused && css.focused
|
||||
)}
|
||||
onClick={handleMicClick}
|
||||
onClick={(e) => {
|
||||
// ⛔ VUI 테스트 비활성화: handleVUIMicClick 호출 안 함
|
||||
e.stopPropagation();
|
||||
console.log('[VoiceInputOverlay] VUI mic clicked (disabled for testing)');
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleMicClick(e);
|
||||
// ⛔ VUI 테스트 비활성화: handleVUIMicClick 호출 안 함
|
||||
console.log('[VoiceInputOverlay] VUI mic Enter key (disabled for testing)');
|
||||
}
|
||||
}}
|
||||
onFocus={handleMicFocus}
|
||||
@@ -320,9 +395,10 @@ const VoiceInputOverlay = ({
|
||||
spotlightId={MIC_SPOTLIGHT_ID}
|
||||
>
|
||||
<div className={css.microphoneCircle}>
|
||||
<img src={micIcon} alt="Microphone" className={css.microphoneIcon} />
|
||||
<img src={micIcon} alt="Voice AI" className={css.microphoneIcon} />
|
||||
</div>
|
||||
{currentMode === VOICE_MODES.LISTENING && (
|
||||
{currentMode === VOICE_MODES.LISTENING &&
|
||||
voiceInputMode === VOICE_INPUT_MODE.VUI && (
|
||||
<svg className={css.rippleSvg} width="100" height="100">
|
||||
<circle
|
||||
className={css.rippleCircle}
|
||||
@@ -336,6 +412,47 @@ const VoiceInputOverlay = ({
|
||||
</svg>
|
||||
)}
|
||||
</SpottableMicButton>
|
||||
|
||||
{/* WebSpeech 마이크 버튼 */}
|
||||
<SpottableMicButton
|
||||
className={classNames(
|
||||
css.microphoneButtonWebSpeech,
|
||||
css.active,
|
||||
currentMode === VOICE_MODES.LISTENING &&
|
||||
voiceInputMode === VOICE_INPUT_MODE.WEBSPEECH &&
|
||||
css.listening,
|
||||
micWebSpeechFocused && css.focused
|
||||
)}
|
||||
onClick={handleWebSpeechMicClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleWebSpeechMicClick(e);
|
||||
}
|
||||
}}
|
||||
onFocus={handleMicWebSpeechFocus}
|
||||
onBlur={handleMicWebSpeechBlur}
|
||||
spotlightId={MIC_WEBSPEECH_SPOTLIGHT_ID}
|
||||
>
|
||||
<div className={css.microphoneCircle}>
|
||||
<img src={micIcon} alt="Voice Input" className={css.microphoneIcon} />
|
||||
</div>
|
||||
{currentMode === VOICE_MODES.LISTENING &&
|
||||
voiceInputMode === VOICE_INPUT_MODE.WEBSPEECH && (
|
||||
<svg className={css.rippleSvg} width="100" height="100">
|
||||
<circle
|
||||
className={css.rippleCircle}
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="47"
|
||||
fill="none"
|
||||
stroke="#4A90E2"
|
||||
strokeWidth="6"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</SpottableMicButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -197,6 +197,79 @@
|
||||
}
|
||||
}
|
||||
|
||||
// WebSpeech 마이크 버튼 (블루 계열)
|
||||
.microphoneButtonWebSpeech {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
z-index: 1003;
|
||||
|
||||
.microphoneCircle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
border-radius: 1000px;
|
||||
border: 5px solid #ccc;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.microphoneIcon {
|
||||
height: 50px;
|
||||
box-sizing: border-box;
|
||||
transition: filter 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.microphoneCircle {
|
||||
border-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
// active 상태 (음성 입력 모드 - 블루 색상)
|
||||
&.active {
|
||||
.microphoneCircle {
|
||||
background-color: #4A90E2;
|
||||
border-color: #4A90E2;
|
||||
box-shadow: 0 0 22px 0 rgba(74, 144, 226, 0.5);
|
||||
|
||||
.microphoneIcon {
|
||||
filter: brightness(0) invert(1); // 아이콘을 흰색으로 변경
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active.focused {
|
||||
.microphoneCircle {
|
||||
background-color: #4A90E2;
|
||||
border-color: white;
|
||||
box-shadow: 0 0 22px 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// listening 상태 (배경 투명, 테두리 ripple 애니메이션)
|
||||
&.listening {
|
||||
.microphoneCircle {
|
||||
background-color: transparent;
|
||||
border-color: transparent; // 테두리 투명
|
||||
box-shadow: none;
|
||||
|
||||
.microphoneIcon {
|
||||
filter: brightness(0) invert(1); // 아이콘은 흰색 유지
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ripple 애니메이션 (원형 테두리가 점에서 시작해서 그려짐)
|
||||
.rippleSvg {
|
||||
position: absolute;
|
||||
|
||||
@@ -29,6 +29,11 @@ export default function VoicePanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
useEffect(() => {
|
||||
if (isOnTop) {
|
||||
dispatch(sendLogGNB(LOG_MENU.SEARCH_SEARCH));
|
||||
} else {
|
||||
// ⭐ VoicePanel이 백그라운드로 가면 voice framework 자동 해제
|
||||
// (SearchPanel 등 다른 패널과 충돌 방지)
|
||||
console.log('[VoicePanel] Going to background, unregistering voice framework');
|
||||
dispatch(unregisterVoiceFramework());
|
||||
}
|
||||
}, [isOnTop, dispatch]);
|
||||
|
||||
|
||||
309
com.twin.app.shoptime/vui-test.1.md
Normal file
309
com.twin.app.shoptime/vui-test.1.md
Normal file
@@ -0,0 +1,309 @@
|
||||
1-1 Can you recommend a 4K TV with Dolby Atmos support under $1,500?
|
||||
|
||||
|
||||
오버레이 인식 : 확인
|
||||
|
||||
STT Text : Can you recommend a 4K TV with Dolby Atmos support under $1,500?
|
||||
|
||||
Event Logs :
|
||||
|
||||
RESPONSE :
|
||||
|
||||
{
|
||||
"subscribed": true,
|
||||
"command": "setContext",
|
||||
"returnValue": true,
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-0036"
|
||||
}
|
||||
|
||||
COMMAND :
|
||||
|
||||
{
|
||||
"command": "setContext",
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-0036"
|
||||
}
|
||||
|
||||
REQUEST :
|
||||
{
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-0036",
|
||||
"intentCount": 3,
|
||||
"intents": [
|
||||
{
|
||||
"intent": "UseIME",
|
||||
"supportAsrOnly": true
|
||||
},
|
||||
{
|
||||
"intent": "Select",
|
||||
"supportOrdinal": true,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "voice-search-button",
|
||||
"value": [
|
||||
"Search",
|
||||
"Search Products",
|
||||
"Find Items"
|
||||
],
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-cart-button",
|
||||
"value": [
|
||||
"Cart",
|
||||
"Shopping Cart",
|
||||
"My Cart"
|
||||
],
|
||||
"title": "Cart"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-home-button",
|
||||
"value": [
|
||||
"Home",
|
||||
"Go Home",
|
||||
"Main Page"
|
||||
],
|
||||
"title": "Home"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-mypage-button",
|
||||
"value": [
|
||||
"My Page",
|
||||
"Account",
|
||||
"Profile"
|
||||
],
|
||||
"title": "My Page"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"intent": "Scroll",
|
||||
"supportOrdinal": false,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "voice-scroll-up",
|
||||
"value": [
|
||||
"Scroll Up",
|
||||
"Page Up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": "voice-scroll-down",
|
||||
"value": [
|
||||
"Scroll Down",
|
||||
"Page Down"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
RESPONSE :
|
||||
|
||||
{
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-0036",
|
||||
"intentCount": 3,
|
||||
"intents": [
|
||||
{
|
||||
"intent": "UseIME",
|
||||
"supportAsrOnly": true
|
||||
},
|
||||
{
|
||||
"intent": "Select",
|
||||
"supportOrdinal": true,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "voice-search-button",
|
||||
"value": [
|
||||
"Search",
|
||||
"Search Products",
|
||||
"Find Items"
|
||||
],
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-cart-button",
|
||||
"value": [
|
||||
"Cart",
|
||||
"Shopping Cart",
|
||||
"My Cart"
|
||||
],
|
||||
"title": "Cart"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-home-button",
|
||||
"value": [
|
||||
"Home",
|
||||
"Go Home",
|
||||
"Main Page"
|
||||
],
|
||||
"title": "Home"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-mypage-button",
|
||||
"value": [
|
||||
"My Page",
|
||||
"Account",
|
||||
"Profile"
|
||||
],
|
||||
"title": "My Page"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"intent": "Scroll",
|
||||
"supportOrdinal": false,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "voice-scroll-up",
|
||||
"value": [
|
||||
"Scroll Up",
|
||||
"Page Up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": "voice-scroll-down",
|
||||
"value": [
|
||||
"Scroll Down",
|
||||
"Page Down"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
ACTION :
|
||||
|
||||
{
|
||||
"message": "Context set successfully. Press the MIC button on remote and speak.",
|
||||
"nextStep": "Waiting for performAction event...",
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-0036"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
그러나 두번째 시도에서
|
||||
|
||||
|
||||
|
||||
STT Text : No STT text received yet. Speak after registering to see the result.
|
||||
|
||||
RESPONSE :
|
||||
|
||||
{
|
||||
"subscribed": true,
|
||||
"command": "setContext",
|
||||
"returnValue": true,
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-003b"
|
||||
}
|
||||
|
||||
COMMAND :
|
||||
{
|
||||
"command": "setContext",
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-003b"
|
||||
}
|
||||
|
||||
|
||||
REQUEST :
|
||||
{
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-003b",
|
||||
"intentCount": 3,
|
||||
"intents": [
|
||||
{
|
||||
"intent": "UseIME",
|
||||
"supportAsrOnly": true
|
||||
},
|
||||
{
|
||||
"intent": "Select",
|
||||
"supportOrdinal": true,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "voice-search-button",
|
||||
"value": [
|
||||
"Search",
|
||||
"Search Products",
|
||||
"Find Items"
|
||||
],
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-cart-button",
|
||||
"value": [
|
||||
"Cart",
|
||||
"Shopping Cart",
|
||||
"My Cart"
|
||||
],
|
||||
"title": "Cart"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-home-button",
|
||||
"value": [
|
||||
"Home",
|
||||
"Go Home",
|
||||
"Main Page"
|
||||
],
|
||||
"title": "Home"
|
||||
},
|
||||
{
|
||||
"itemId": "voice-mypage-button",
|
||||
"value": [
|
||||
"My Page",
|
||||
"Account",
|
||||
"Profile"
|
||||
],
|
||||
"title": "My Page"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"intent": "Scroll",
|
||||
"supportOrdinal": false,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "voice-scroll-up",
|
||||
"value": [
|
||||
"Scroll Up",
|
||||
"Page Up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": "voice-scroll-down",
|
||||
"value": [
|
||||
"Scroll Down",
|
||||
"Page Down"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
RESPONSE :
|
||||
{
|
||||
"returnValue": true
|
||||
}
|
||||
|
||||
ACTION :
|
||||
{
|
||||
"message": "Context set successfully. Press the MIC button on remote and speak.",
|
||||
"nextStep": "Waiting for performAction event...",
|
||||
"voiceTicket": "13799ec6-fd1f-4cdd-b02e-34d58fe5f34b-68ef-003b"
|
||||
}
|
||||
|
||||
ERROR :
|
||||
{
|
||||
"message": "performAction event was not received within 15 seconds after setContext.",
|
||||
"possibleReasons": [
|
||||
"1. Did you press the MIC button on the remote control?",
|
||||
"2. Did you speak after pressing the MIC button?",
|
||||
"3. UseIME intent might not be supported on this webOS version",
|
||||
"4. Voice framework might not be routing events correctly"
|
||||
],
|
||||
"suggestion": "Try pressing the remote MIC button and speaking clearly. Check VoicePanel logs for performAction event."
|
||||
}
|
||||
1417
com.twin.app.shoptime/web-speech.md
Normal file
1417
com.twin.app.shoptime/web-speech.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user