feat: 작업실 채팅에 경량 모델 pill 추가
메인 채팅의 전체 설정 모달을 그대로 들여오는 대신, 같은 백엔드 엔드포인트(/api/settings/model, /api/settings/provider, /api/models/test)만 재사용해 채팅 헤더에 작은 pill+드롭다운을 추가. 클릭하면 ollama provider가 보유한 로컬+클라우드 모델 태그 목록을 보여주고 선택 시 전역 models.primary를 바꾼다. curl로 GET/POST 전 구간 실측 확인(목록 12개 모델 조회, 전환→원복). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,16 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
|
||||
.rb-send:hover:not(:disabled){background:#9a5c03;}
|
||||
.rb-send:disabled{opacity:.5;cursor:not-allowed;}
|
||||
|
||||
.rb-model-pill{position:relative;flex-shrink:0;}
|
||||
.rb-model-pill-btn{background:none;border:1px solid var(--line);border-radius:6px;padding:2px 8px;font-size:10px;font-weight:700;color:var(--muted);cursor:pointer;font-family:inherit;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:.15s;}
|
||||
.rb-model-pill-btn:hover{border-color:#f59e0b;color:#f59e0b;}
|
||||
.rb-model-dropdown{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:var(--panel-2);border:1px solid var(--line);border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.35);min-width:200px;max-height:280px;overflow-y:auto;z-index:50;padding:4px;}
|
||||
.rb-model-dropdown.open{display:block;}
|
||||
.rb-model-opt{padding:6px 10px;font-size:12px;border-radius:5px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--text);}
|
||||
.rb-model-opt:hover{background:var(--panel);}
|
||||
.rb-model-opt.current{color:#f59e0b;font-weight:700;}
|
||||
.rb-model-dropdown-msg{padding:6px 10px;font-size:11px;color:var(--muted);}
|
||||
|
||||
@media (max-width:900px){
|
||||
.rb-layout{flex-direction:column;}
|
||||
.rb-resize{display:none;}
|
||||
@@ -224,6 +234,10 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
|
||||
<div class="rb-chat-hdr">
|
||||
<span class="rb-chat-label" id="chat-label">🛠️ 작업실 채팅</span>
|
||||
<span class="rb-hdr-sp"></span>
|
||||
<div class="rb-model-pill">
|
||||
<button class="rb-model-pill-btn" id="model-pill-btn" onclick="toggleModelDropdown(event)" title="모델 변경">-</button>
|
||||
<div class="rb-model-dropdown" id="model-dropdown"></div>
|
||||
</div>
|
||||
<button class="rb-chat-clear" onclick="clearChat()" title="대화 지우기">🗑️</button>
|
||||
</div>
|
||||
<div class="rb-msgs" id="chat-msgs"></div>
|
||||
@@ -847,12 +861,68 @@ function autoResize(el){el.style.height='auto';el.style.height=Math.min(el.scrol
|
||||
document.addEventListener('mouseup',()=>{if(!dragging)return;dragging=false;handle.classList.remove('dragging');document.body.style.userSelect='';document.body.style.cursor='';try{localStorage.setItem(CHAT_W,panel.offsetWidth);}catch{};});
|
||||
})();
|
||||
|
||||
// ── 모델 pill ─────────────────────────────────────────────────────────────────
|
||||
// 경량 버전: 메인 채팅의 설정 모달(settings-provider.js)을 통째로 들여오지 않고, 같은 백엔드
|
||||
// 엔드포인트(/api/settings/model, /api/settings/provider, /api/models/test)만 재사용해서
|
||||
// 전역 models.primary를 바꾼다. ollama provider(로컬+클라우드 모델 태그) 안에서만 전환됨 —
|
||||
// provider 자체(openai/anthropic/google 등)를 바꾸는 건 메인 설정에서 해야 한다.
|
||||
let _modelDropdownLoaded=false;
|
||||
async function loadModelPill(){
|
||||
try{
|
||||
const r=await fetch('/api/settings/model',{headers:authH()});
|
||||
if(!r.ok)return;
|
||||
const d=await r.json();
|
||||
const btn=document.getElementById('model-pill-btn');
|
||||
if(btn && d.primary) btn.textContent=d.primary;
|
||||
}catch{}
|
||||
}
|
||||
async function toggleModelDropdown(ev){
|
||||
if(ev) ev.stopPropagation();
|
||||
const dd=document.getElementById('model-dropdown');
|
||||
if(dd.classList.contains('open')){dd.classList.remove('open');return;}
|
||||
dd.classList.add('open');
|
||||
if(_modelDropdownLoaded)return;
|
||||
dd.innerHTML='<div class="rb-model-dropdown-msg">모델 불러오는 중…</div>';
|
||||
try{
|
||||
const pr=await fetch('/api/settings/provider',{headers:authH()});
|
||||
const pd=await pr.json();
|
||||
const tr=await fetch('/api/models/test',{method:'POST',headers:authH({'Content-Type':'application/json'}),body:JSON.stringify({llm:pd.llm})});
|
||||
const td=await tr.json();
|
||||
const models=(td.models||[]).map(m=>typeof m==='string'?m:(m.name||String(m)));
|
||||
if(!models.length){dd.innerHTML='<div class="rb-model-dropdown-msg">모델 목록을 가져오지 못했습니다.</div>';return;}
|
||||
const current=(document.getElementById('model-pill-btn').textContent||'').trim();
|
||||
dd.innerHTML=models.map(m=>`<div class="rb-model-opt${m===current?' current':''}" data-model="${escHtml(m)}">${escHtml(m)}</div>`).join('');
|
||||
dd.querySelectorAll('.rb-model-opt').forEach(el=>el.addEventListener('click',()=>selectModel(el.dataset.model)));
|
||||
_modelDropdownLoaded=true;
|
||||
}catch(e){dd.innerHTML='<div class="rb-model-dropdown-msg">오류: '+escHtml(e.message)+'</div>';}
|
||||
}
|
||||
async function selectModel(name){
|
||||
document.getElementById('model-dropdown').classList.remove('open');
|
||||
const btn=document.getElementById('model-pill-btn');
|
||||
const prev=btn.textContent;
|
||||
if(name===prev)return;
|
||||
btn.textContent=name;
|
||||
try{
|
||||
const r=await fetch('/api/settings/model',{method:'POST',headers:authH({'Content-Type':'application/json'}),body:JSON.stringify({primary:name})});
|
||||
if(!r.ok)throw new Error('저장 실패');
|
||||
_modelDropdownLoaded=false;
|
||||
}catch(e){
|
||||
btn.textContent=prev;
|
||||
addMsg('system','모델 변경 실패: '+e.message);
|
||||
}
|
||||
}
|
||||
document.addEventListener('click',(e)=>{
|
||||
const pill=document.querySelector('.rb-model-pill');
|
||||
if(pill && !pill.contains(e.target)) document.getElementById('model-dropdown')?.classList.remove('open');
|
||||
});
|
||||
|
||||
// ── Init ──────────────────────────────────────────────────────────────────────
|
||||
(async function init(){
|
||||
if(!(await checkAuth()))return;
|
||||
await loadWorkshopData();
|
||||
await initSessionId();
|
||||
await loadHistory();
|
||||
loadModelPill();
|
||||
addMsg('system','🛠️ 작업실 채팅입니다. 현재 프로젝트: "'+(currentProject()?.name||'')+'". 부품 구매/상태 변경, 작업 완료 체크, 예산 확인, 새 프로젝트 생성 등을 물어보세요.');
|
||||
document.getElementById('chat-input').focus();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user