feat: 작업실 개요를 파일 폴더 대신 개요 탭으로 + /api/files 텍스트 charset 수정

- workshop-app: 메모 탭과 동일 구조의 "개요" 탭(textarea) 추가 — project.description
  직접 편집, 채팅 set_description으로 쓴 내용은 응답 후 softRefresh로 갱신
- readme.md 자동생성(syncProjectReadmes) 철회 — 파일탭이 산만해져서 사용자 지정으로
  탭 방식으로 전환, 생성된 개요 폴더들도 삭제. description/set_description은 유지
- server.ts IMAGE_TYPES: 텍스트 계열(.md/.txt/.csv 등) charset=utf-8 명시 — charset
  없이 text/plain으로 뱉으면 브라우저가 latin1로 해석해 한글이 전부 깨짐(실측)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
kim
2026-09-23 16:48:12 +09:00
co-authored by Claude Code
parent 91ed19bda0
commit 2da7b9fd98
8 changed files with 75 additions and 132 deletions
+21 -2
View File
@@ -218,6 +218,7 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
<button class="rb-tab-btn" data-tab="tasks" onclick="switchTab('tasks')">작업</button>
<button class="rb-tab-btn" data-tab="equip" onclick="switchTab('equip')">장비</button>
<button class="rb-tab-btn" data-tab="files" onclick="switchTab('files')">파일</button>
<button class="rb-tab-btn" data-tab="overview" onclick="switchTab('overview')">개요</button>
<button class="rb-tab-btn" data-tab="notes" onclick="switchTab('notes')">메모</button>
</div>
@@ -266,6 +267,10 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
<div id="files-list-container"></div>
</div>
<div class="rb-pane" id="pane-overview">
<textarea class="rb-notes" id="overview-textarea" placeholder="프로젝트 개요 — 무엇을 만드는지/목표/구성. 채팅에서 '개요 써줘'로도 쓸 수 있다." oninput="onOverviewChange()"></textarea>
</div>
<div class="rb-pane" id="pane-notes">
<textarea class="rb-notes" id="notes-textarea" placeholder="자유 메모..." oninput="onNotesChange()"></textarea>
</div>
@@ -323,6 +328,7 @@ let workshopData={projects:[],activeProjectId:''};
let parts=[];
let phases=[];
let notes='';
let overviewDesc='';
let saveTimer=null;
let saveDebounce=null;
@@ -347,6 +353,7 @@ function loadLocalFromProject(){
parts=proj?(proj.parts||[]):[];
phases=proj?(proj.phases||[]):[];
notes=proj?(proj.notes||''):'';
overviewDesc=proj?(proj.description||''):'';
}
function syncLocalToProject(){
@@ -355,6 +362,7 @@ function syncLocalToProject(){
proj.parts=parts;
proj.phases=phases;
proj.notes=notes;
proj.description=overviewDesc;
}
function renderProjectSelector(){
@@ -371,6 +379,7 @@ function refreshAllPanes(){
renderParts();
renderPhases();
document.getElementById('notes-textarea').value=notes;
document.getElementById('overview-textarea').value=overviewDesc;
updateBudget();
}
@@ -389,7 +398,7 @@ function createProjectPrompt(){
const name=(prompt('새 프로젝트 이름을 입력하세요:')||'').trim();
if(!name)return;
const id=genId('proj');
workshopData.projects.push({id,name,parts:[],phases:[],notes:''});
workshopData.projects.push({id,name,parts:[],phases:[],notes:'',description:''});
workshopData.activeProjectId=id;
loadLocalFromProject();
clearStlSelection();
@@ -425,7 +434,7 @@ async function loadWorkshopData(){
}catch{}
if(!workshopData.projects.length){
const id=genId('proj');
workshopData.projects=[{id,name:'새 프로젝트',parts:[],phases:[],notes:''}];
workshopData.projects=[{id,name:'새 프로젝트',parts:[],phases:[],notes:'',description:''}];
workshopData.activeProjectId=id;
}
loadLocalFromProject();
@@ -461,6 +470,8 @@ async function softRefreshWorkshop(){
renderPhases();
const ta=document.getElementById('notes-textarea');
if(ta&&ta.value!==notes)ta.value=notes;
const ota=document.getElementById('overview-textarea');
if(ota&&ota.value!==overviewDesc)ota.value=overviewDesc;
updateBudget();
}catch{}
}
@@ -1052,6 +1063,14 @@ function onNotesChange(){
scheduleSave();
}
// ── 개요 ──────────────────────────────────────────────────────────────────────
// 메모 탭과 같은 구조 — 프로젝트 서술형 설명(description). 채팅 도구 set_description
// 으로도 쓸 수 있고, 채팅 응답 후 softRefreshWorkshop이 갱신해준다.
function onOverviewChange(){
overviewDesc=document.getElementById('overview-textarea').value;
scheduleSave();
}
// ── 채팅 패널 ─────────────────────────────────────────────────────────────────
// 세션ID를 프로젝트별로 분리한다("ws_" 접두어는 유지 — tool-scope.ts가 이 세션에서는
// 키워드 없이도 workshop_project 도구를 항상 열어주고, 메인 사이드바 목록에서도 걸러진다).