fix: 작업실 앱 버그 4종 — STL선택 프로젝트 오염, 부품 필드 인젝션, 파일탭 스테일, parts 섀도잉
- switchProject/createProject/deleteProject에서 stlSelection 초기화 누락 → 프로젝트 전환 후 슬라이서 전송 시 옛 프로젝트 relPath가 현재 프로젝트 경로로 재해석되어 같은 이름의 다른 파일이 슬라이싱될 수 있던 버그 - renderParts에서 status/qty/unitPrice 이스케이프 누락(채팅 도구가 임의 문자열 쓰면 HTML 주입 경로) → escAttr 적용 - 프로젝트 생성/삭제 시 파일 탭이 열려있으면 loadFiles 미호출로 옛 프로젝트 파일 목록 남던 버그 → switchProject와 동일 갱신 추가 - callChat 내 SSE 파싱 변수 parts가 전역 부품 배열 parts를 섀도잉 → sseParts로 개명 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -330,6 +330,18 @@ function currentProject(){
|
||||
return workshopData.projects.find(p=>p.id===workshopData.activeProjectId)||workshopData.projects[0];
|
||||
}
|
||||
|
||||
// STL 선택은 프로젝트 기준 relPath를 담는다 — 프로젝트가 바뀌면(수동 전환/생성/삭제,
|
||||
// 채팅 도구의 set_active_project 포함) 반드시 비운다. 안 그러면 옛 프로젝트 파일의
|
||||
// relPath가 workshopFilePath()에서 새 프로젝트 경로로 재해석되어, 같은 이름의 다른
|
||||
// 파일이 슬라이서로 잘못 전달될 수 있다.
|
||||
function clearStlSelection(){
|
||||
stlSelection.clear();
|
||||
const btn=document.getElementById('stl-slice-btn');
|
||||
const countEl=document.getElementById('stl-sel-count');
|
||||
if(countEl)countEl.textContent='0';
|
||||
if(btn)btn.disabled=true;
|
||||
}
|
||||
|
||||
function loadLocalFromProject(){
|
||||
const proj=currentProject();
|
||||
parts=proj?(proj.parts||[]):[];
|
||||
@@ -366,6 +378,7 @@ function switchProject(id){
|
||||
syncLocalToProject();
|
||||
workshopData.activeProjectId=id;
|
||||
loadLocalFromProject();
|
||||
clearStlSelection();
|
||||
refreshAllPanes();
|
||||
scheduleSave();
|
||||
if(document.getElementById('pane-files').classList.contains('active'))loadFiles();
|
||||
@@ -378,8 +391,10 @@ function createProjectPrompt(){
|
||||
workshopData.projects.push({id,name,parts:[],phases:[],notes:''});
|
||||
workshopData.activeProjectId=id;
|
||||
loadLocalFromProject();
|
||||
clearStlSelection();
|
||||
refreshAllPanes();
|
||||
scheduleSave();
|
||||
if(document.getElementById('pane-files').classList.contains('active'))loadFiles();
|
||||
}
|
||||
|
||||
function deleteProjectPrompt(){
|
||||
@@ -390,8 +405,10 @@ function deleteProjectPrompt(){
|
||||
workshopData.projects=workshopData.projects.filter(p=>p.id!==proj.id);
|
||||
workshopData.activeProjectId=workshopData.projects[0].id;
|
||||
loadLocalFromProject();
|
||||
clearStlSelection();
|
||||
refreshAllPanes();
|
||||
scheduleSave();
|
||||
if(document.getElementById('pane-files').classList.contains('active'))loadFiles();
|
||||
}
|
||||
|
||||
async function loadWorkshopData(){
|
||||
@@ -615,14 +632,14 @@ function renderParts(){
|
||||
<div class="rb-part-hdr" onclick="togglePartExpand('${p.id}')">
|
||||
<span class="rb-part-chevron">▶</span>
|
||||
<span class="rb-part-name">${escAttr(p.name)||'(이름 없음)'}</span>
|
||||
<span class="rb-part-badge st-${p.status}">${p.status}</span>
|
||||
<span class="rb-part-badge st-${escAttr(p.status)}">${escAttr(p.status)}</span>
|
||||
<span class="rb-part-sub">${fmtWon(p.qty*p.unitPrice)}</span>
|
||||
</div>
|
||||
<div class="rb-part-body" onclick="event.stopPropagation()">
|
||||
<div class="rb-part-row">
|
||||
<div class="rb-part-field grow"><label>이름</label><input type="text" value="${escAttr(p.name)}" oninput="updatePart('${p.id}','name',this.value)"></div>
|
||||
<div class="rb-part-field qty"><label>수량</label><input type="number" min="0" value="${p.qty}" oninput="updatePart('${p.id}','qty',this.value)"></div>
|
||||
<div class="rb-part-field price"><label>단가</label><input type="number" min="0" value="${p.unitPrice}" oninput="updatePart('${p.id}','unitPrice',this.value)"></div>
|
||||
<div class="rb-part-field qty"><label>수량</label><input type="number" min="0" value="${escAttr(p.qty)}" oninput="updatePart('${p.id}','qty',this.value)"></div>
|
||||
<div class="rb-part-field price"><label>단가</label><input type="number" min="0" value="${escAttr(p.unitPrice)}" oninput="updatePart('${p.id}','unitPrice',this.value)"></div>
|
||||
<div class="rb-part-field status"><label>상태</label>
|
||||
<select onchange="updatePart('${p.id}','status',this.value)">
|
||||
<option value="검토중" ${p.status==='검토중'?'selected':''}>검토중</option>
|
||||
@@ -1134,8 +1151,9 @@ async function callChat(message){
|
||||
while(true){
|
||||
const{done,value}=await reader.read();if(done)break;
|
||||
buf+=dec.decode(value,{stream:true});
|
||||
const parts=buf.split('\n\n');buf=parts.pop()||'';
|
||||
for(const part of parts){
|
||||
// 전역 부품 배열 parts를 가리지 않게 SSE 조각은 sseParts로.
|
||||
const sseParts=buf.split('\n\n');buf=sseParts.pop()||'';
|
||||
for(const part of sseParts){
|
||||
if(!part.startsWith('data: '))continue;
|
||||
let ev;try{ev=JSON.parse(part.slice(6));}catch{continue;}
|
||||
if(ev.type==='token'||ev.type==='text_delta'){
|
||||
|
||||
Reference in New Issue
Block a user