fix(important): 자동/구간/붙여넣기 탭 간 검토 상태(byIdx·sel·selCut) 오염 방지
세 result 핸들러(onPrepareResult/onYtResult/onPasteResult)가 byIdx는 전역 리셋하면서 sel/selCut은 자기 탭 키만 건드려, 한 세션에서 두 탭을 쓰면 칩이 다른 탭 댓글을 가리키거나 "ID n 사용중" 오표시가 뜨고, 이미 그려진 다른 탭 카드를 클릭하면 sel[hlId]가 undefined라 toggle()에서 TypeError가 났다(const selCut 재할당 시도 자체도 원래 런타임 에러였음 — let로 변경). "검토는 한 번에 하나" 원칙으로 세 핸들러 모두 byIdx· sel·selCut을 전역 리셋하고, 다른 탭의 검토 DOM·빌드 버튼도 clearOtherPanels() 로 함께 지워 클릭 경로 자체를 없앴다. grep 대조: byIdx/sel/selCut을 읽고 쓰는 곳(toggle/sortSel/cutFull/ refreshSel/wrapOf/usedByOthers/applyUsedMarks/buildAll류)은 모두 hlId로 키를 참조하므로 이번 전역 리셋과 일관되게 동작한다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6ab3d998e9
commit
e88571e5e9
@ -244,7 +244,7 @@ function renderCutPanel(box,panelId,data,opts){
|
|||||||
/* ── 컷별 선택 ── 카드는 '어느 컷 소속'인지가 배치를 정한다.
|
/* ── 컷별 선택 ── 카드는 '어느 컷 소속'인지가 배치를 정한다.
|
||||||
selCut[hlId][idx] = 컷 인덱스. sel[hlId] 는 항상 컷 순서로 정렬해 둔다
|
selCut[hlId][idx] = 컷 인덱스. sel[hlId] 는 항상 컷 순서로 정렬해 둔다
|
||||||
(업로드 순서 = 배치 순서라서). */
|
(업로드 순서 = 배치 순서라서). */
|
||||||
const selCut={};
|
let selCut={};
|
||||||
function sortSel(hlId){
|
function sortSel(hlId){
|
||||||
const m=selCut[hlId]||{};
|
const m=selCut[hlId]||{};
|
||||||
sel[hlId].sort((a,b)=>(m[a]??999)-(m[b]??999));
|
sel[hlId].sort((a,b)=>(m[a]??999)-(m[b]??999));
|
||||||
@ -732,10 +732,30 @@ function updatePrepStep(ev){
|
|||||||
if(ev.detail){const d=el.querySelector(".sdetail");d.hidden=false;d.textContent=ev.detail;}}
|
if(ev.detail){const d=el.querySelector(".sdetail");d.hidden=false;d.textContent=ev.detail;}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── 검토는 한 번에 하나 ──
|
||||||
|
byIdx/sel/selCut 는 탭(auto/yt/paste) 구분 없는 전역 상태다. 세 result 핸들러가
|
||||||
|
전부 byIdx={} 로 전역 리셋하면서 sel/selCut 은 자기 키만 건드리면, 다른 탭이
|
||||||
|
그려둔 카드가 방금 리셋된 byIdx/sel 을 참조해 "엉뚱한 댓글을 가리킴"·"ID n 사용중"
|
||||||
|
오표시·클릭 시 TypeError(toggle()의 list.indexOf, sel[hlId]가 undefined)가 난다.
|
||||||
|
→ 새 결과가 오면 sel/selCut 도 전역 리셋하고, 다른 탭의 검토 DOM·빌드 버튼까지
|
||||||
|
지워 클릭 경로 자체를 없앤다. */
|
||||||
|
const PANEL_DOM={auto:{review:"#autoReview",build:"#autoBuild"},
|
||||||
|
yt:{review:"#ytReview",build:"#ytBuild"},
|
||||||
|
paste:{review:"#pasteReview",build:"#pasteBuild"}};
|
||||||
|
function clearOtherPanels(exceptId){
|
||||||
|
for(const id in PANEL_DOM){
|
||||||
|
if(id===exceptId) continue;
|
||||||
|
const d=PANEL_DOM[id];
|
||||||
|
const r=$(d.review); if(r) r.innerHTML="";
|
||||||
|
const b=$(d.build); if(b) b.style.display="none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ── 2차 검토 화면 — 지금까지와 같은 ID별 탭 + renderCutPanel(컷별 댓글 섹션). ──
|
/* ── 2차 검토 화면 — 지금까지와 같은 ID별 탭 + renderCutPanel(컷별 댓글 섹션). ──
|
||||||
준비(prepare)를 마친 ID만 온다 — 실패한 ID는 경고 로그로만 알린다(탭 자체가 없음). */
|
준비(prepare)를 마친 ID만 온다 — 실패한 ID는 경고 로그로만 알린다(탭 자체가 없음). */
|
||||||
function onPrepareResult(ev){
|
function onPrepareResult(ev){
|
||||||
P=ev;byIdx={};sel={};selCut={};curId=null;
|
P=ev;byIdx={};sel={};selCut={};curId=null;
|
||||||
|
clearOtherPanels("auto");
|
||||||
(ev.comments||[]).forEach(c=>{byIdx[c.idx]=c;});
|
(ev.comments||[]).forEach(c=>{byIdx[c.idx]=c;});
|
||||||
(ev.warnings||[]).forEach(w=>prepLog("⚠️ "+w));
|
(ev.warnings||[]).forEach(w=>prepLog("⚠️ "+w));
|
||||||
const R=$("#autoReview");R.innerHTML="";
|
const R=$("#autoReview");R.innerHTML="";
|
||||||
@ -978,7 +998,9 @@ function ytFail(m){ytLog("⚠️ "+m);ytDone();}
|
|||||||
(channel 은 유튜브에서 자동인식됐을 수 있음) 여기서 입력칸에 반영해 보여준다 —
|
(channel 은 유튜브에서 자동인식됐을 수 있음) 여기서 입력칸에 반영해 보여준다 —
|
||||||
사용자가 여기서 또 고치면 그 값이 생성(ytBuildAll)에 그대로 실린다. */
|
사용자가 여기서 또 고치면 그 값이 생성(ytBuildAll)에 그대로 실린다. */
|
||||||
function onYtResult(ev){
|
function onYtResult(ev){
|
||||||
byIdx={};(ev.comments||[]).forEach(c=>{byIdx[c.idx]=c;});
|
byIdx={};sel={};selCut={};curId=null; // 검토는 한 번에 하나 — 다른 탭 선택 상태까지 전부 리셋
|
||||||
|
clearOtherPanels("yt");
|
||||||
|
(ev.comments||[]).forEach(c=>{byIdx[c.idx]=c;});
|
||||||
(ev.warnings||[]).forEach(w=>ytLog("⚠️ "+w));
|
(ev.warnings||[]).forEach(w=>ytLog("⚠️ "+w));
|
||||||
if(ev.title_top!=null) $("#yttop").value=ev.title_top;
|
if(ev.title_top!=null) $("#yttop").value=ev.title_top;
|
||||||
if(ev.title_main!=null) $("#ytmain").value=ev.title_main;
|
if(ev.title_main!=null) $("#ytmain").value=ev.title_main;
|
||||||
@ -1136,7 +1158,9 @@ function pasteFail(m){pasteLog("⚠️ "+m);pasteDone();}
|
|||||||
expandAllCuts·refreshSel 이 전부 "#hlbox-"+panelId 로 찾으므로 id를 정확히 맞춰야 한다
|
expandAllCuts·refreshSel 이 전부 "#hlbox-"+panelId 로 찾으므로 id를 정확히 맞춰야 한다
|
||||||
(yt 탭의 #hlbox-yt 와 동일한 방식). */
|
(yt 탭의 #hlbox-yt 와 동일한 방식). */
|
||||||
function onPasteResult(ev){
|
function onPasteResult(ev){
|
||||||
byIdx={};(ev.comments||[]).forEach(c=>{byIdx[c.idx]=c;});
|
byIdx={};sel={};selCut={};curId=null; // 검토는 한 번에 하나 — 다른 탭 선택 상태까지 전부 리셋
|
||||||
|
clearOtherPanels("paste");
|
||||||
|
(ev.comments||[]).forEach(c=>{byIdx[c.idx]=c;});
|
||||||
(ev.warnings||[]).forEach(w=>pasteLog("⚠️ "+w));
|
(ev.warnings||[]).forEach(w=>pasteLog("⚠️ "+w));
|
||||||
PASTE_HL={id:"paste",need:ev.need,cuts:ev.cuts,paste:{cuts:ev.cutRanges}};
|
PASTE_HL={id:"paste",need:ev.need,cuts:ev.cuts,paste:{cuts:ev.cutRanges}};
|
||||||
sel["paste"]=[];selCut["paste"]={};
|
sel["paste"]=[];selCut["paste"]={};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user