12141214 }
12151215 .menu-translation-overlay {
12161216 position : absolute;
1217- left : 10px ;
1218- right : 10px ;
1219- bottom : 10px ;
1220- max-height : 58% ;
1217+ inset : 0 ;
12211218 display : none;
1222- gap : 5px ;
1223- padding : 9px ;
1224- overflow-y : auto;
1225- color : # fff ;
1226- background : rgba (3 , 25 , 43 , .84 );
1227- border : 1px solid rgba (255 , 255 , 255 , .28 );
1228- border-radius : 12px ;
1229- box-shadow : 0 8px 24px rgba (0 , 0 , 0 , .2 );
1230- backdrop-filter : blur (8px );
1231- }
1232- .menu-translation-overlay .show { display : grid; }
1233- .menu-translation-overlay strong {
1234- color : # 7dd3fc ;
1235- font-size : calc (10px + var (--font-add , 0px ));
1219+ pointer-events : none;
12361220 }
1221+ .menu-translation-overlay .show { display : block; }
12371222 .menu-overlay-line {
1238- padding : 5px 7px ;
1223+ position : absolute;
1224+ right : 5px ;
1225+ width : min (42% , 190px );
1226+ padding : 4px 7px ;
12391227 color : # fff ;
1240- background : rgba (255 , 255 , 255 , .1 );
1241- border-radius : 8px ;
1228+ background : rgba (2 , 132 , 199 , .92 );
1229+ border : 1px solid rgba (255 , 255 , 255 , .72 );
1230+ border-radius : 7px ;
1231+ box-shadow : 0 3px 10px rgba (2 , 45 , 75 , .24 );
1232+ font-size : calc (9px + var (--font-add , 0px ));
1233+ font-weight : 800 ;
1234+ line-height : 1.25 ;
1235+ transform : translateY (-50% );
1236+ text-align : left;
1237+ }
1238+ .menu-overlay-line ::before {
1239+ content : "" ;
1240+ position : absolute;
1241+ top : 50% ;
1242+ right : 100% ;
1243+ width : 10px ;
1244+ height : 1px ;
1245+ background : # 38bdf8 ;
1246+ }
1247+ .menu-translation-status {
1248+ display : none;
1249+ padding : 10px 12px ;
1250+ color : # 9a3412 ;
1251+ background : # fff7ed ;
1252+ border : 1px solid # fed7aa ;
1253+ border-radius : 12px ;
12421254 font-size : calc (11px + var (--font-add , 0px ));
1243- line-height : 1.35 ;
1255+ line-height : 1.45 ;
12441256 }
1257+ .menu-translation-status : not (: empty ) { display : block; }
12451258 .menu-camera-form { min-width : 0 ; }
12461259 .menu-camera-form > * { min-width : 0 ; }
12471260 .menu-camera-form .translation { overflow-wrap : anywhere; }
@@ -2413,8 +2426,7 @@ <h3 id="ribbonTitle" data-i18n="blueRibbon">블루리본 추천 맛집</h3>
24132426 <option value="zh">中文</option>
24142427 </select>
24152428 </div>
2416- <div class="tool-result translation" id="menuTranslationResult">${ label . translationResult } </div>
2417- <a class="map-link google" id="googleTranslateLink" target="_blank" rel="noopener" style="display:none"><span>${ feature . openFullTranslation } </span><span>↗</span></a>
2429+ <div class="menu-translation-status" id="menuTranslationResult" role="status" aria-live="polite"></div>
24182430 <p class="tool-note">${ label . menuHint } </p>
24192431 </div>` ;
24202432 document . getElementById ( "menuPhoto" ) . addEventListener ( "change" , previewMenuPhoto ) ;
@@ -2698,15 +2710,14 @@ <h3 id="ribbonTitle" data-i18n="blueRibbon">블루리본 추천 맛집</h3>
26982710 const normalizedSource = normalizeMenuText ( source ) ;
26992711 const target = document . getElementById ( "menuTarget" ) . value ;
27002712 const result = document . getElementById ( "menuTranslationResult" ) ;
2701- const googleLink = document . getElementById ( "googleTranslateLink" ) ;
27022713 if ( ! source ) {
27032714 result . textContent = label . emptyMenu ;
2704- googleLink . style . display = "none" ;
27052715 return ;
27062716 }
27072717 const keys = Object . keys ( menuTranslations ) . sort ( ( a , b ) => b . length - a . length ) ;
27082718 const translatedItems = [ ] ;
2709- normalizedSource . split ( / \n / ) . forEach ( rawLine => {
2719+ const sourceLines = normalizedSource . split ( / \n / ) ;
2720+ sourceLines . forEach ( ( rawLine , lineIndex ) => {
27102721 const line = repairOcrLine ( rawLine ) ;
27112722 const matchedKeys = keys . filter ( key => key . length >= 2 && line . includes ( key ) ) . filter ( ( key , index , list ) => {
27122723 return ! list . some ( ( other , otherIndex ) => otherIndex !== index && other . includes ( key ) ) ;
@@ -2715,19 +2726,20 @@ <h3 id="ribbonTitle" data-i18n="blueRibbon">블루리본 추천 맛집</h3>
27152726 if ( matchedKeys . length ) {
27162727 const names = matchedKeys . map ( key => menuTranslations [ key ] [ target ] ) ;
27172728 const priceText = prices . length ? ` · ${ prices . join ( " / " ) . replace ( / \s * (?: 원 | ₩ | K R W ) ? $ / i, "" ) } KRW` : "" ;
2718- translatedItems . push ( `${ [ ...new Set ( names ) ] . join ( " · " ) } ${ priceText } ` ) ;
2729+ translatedItems . push ( {
2730+ text : `${ [ ...new Set ( names ) ] . join ( " · " ) } ${ priceText } ` ,
2731+ lineIndex,
2732+ totalLines : sourceLines . length
2733+ } ) ;
27192734 }
27202735 } ) ;
27212736 if ( ! translatedItems . length ) {
27222737 result . textContent = feature . ocrError ;
27232738 renderImageTranslationOverlay ( [ ] , feature ) ;
2724- googleLink . style . display = "none" ;
27252739 return ;
27262740 }
2727- result . textContent = translatedItems . join ( "\n" ) ;
2741+ result . textContent = "" ;
27282742 renderImageTranslationOverlay ( translatedItems , feature ) ;
2729- googleLink . href = `https://translate.google.com/?sl=ko&tl=${ target } &text=${ encodeURIComponent ( source ) } &op=translate` ;
2730- googleLink . style . display = "flex" ;
27312743 }
27322744
27332745 function renderImageTranslationOverlay ( items , feature ) {
@@ -2736,13 +2748,23 @@ <h3 id="ribbonTitle" data-i18n="blueRibbon">블루리본 추천 맛집</h3>
27362748 overlay . replaceChildren ( ) ;
27372749 overlay . classList . toggle ( "show" , items . length > 0 ) ;
27382750 if ( ! items . length ) return ;
2739- const title = document . createElement ( "strong" ) ;
2740- title . textContent = feature . imageTranslation ;
2741- overlay . appendChild ( title ) ;
2742- items . forEach ( item => {
2751+ const positions = items . map ( item => {
2752+ const denominator = Math . max ( 1 , item . totalLines - 1 ) ;
2753+ return Math . max ( 10 , Math . min ( 90 , 12 + ( item . lineIndex / denominator ) * 76 ) ) ;
2754+ } ) ;
2755+ const minimumGap = Math . max ( 7 , Math . min ( 11 , 66 / Math . max ( 1 , items . length - 1 ) ) ) ;
2756+ for ( let index = 1 ; index < positions . length ; index += 1 ) {
2757+ positions [ index ] = Math . max ( positions [ index ] , positions [ index - 1 ] + minimumGap ) ;
2758+ }
2759+ if ( positions [ positions . length - 1 ] > 90 ) {
2760+ const shift = positions [ positions . length - 1 ] - 90 ;
2761+ positions . forEach ( ( position , index ) => { positions [ index ] = Math . max ( 8 , position - shift ) ; } ) ;
2762+ }
2763+ items . forEach ( ( item , index ) => {
27432764 const line = document . createElement ( "div" ) ;
27442765 line . className = "menu-overlay-line" ;
2745- line . textContent = item ;
2766+ line . textContent = item . text ;
2767+ line . style . top = `${ positions [ index ] } %` ;
27462768 overlay . appendChild ( line ) ;
27472769 } ) ;
27482770 }
0 commit comments