Release v2.1.8: Company Agent roster overhaul and UI polish
This commit is contained in:
@@ -77,8 +77,8 @@
|
||||
|
||||
<!-- Brain -->
|
||||
<section class="section" data-section="brain">
|
||||
<h2>Brain</h2>
|
||||
<p class="hint">현재 활성 brain 프로필 정보입니다. 프로필 추가·수정은 사이드바의 Brain 메뉴 또는 VS Code Settings에서 처리합니다.</p>
|
||||
<h2>두뇌 (지식 폴더)</h2>
|
||||
<p class="hint">현재 활성 두뇌 프로필 정보입니다. 추가·수정은 사이드바의 [변경 ▾ → 두뇌] 또는 VS Code Settings에서 처리합니다.</p>
|
||||
<div class="row">
|
||||
<label>활성 프로필</label>
|
||||
<div class="readout" id="brainName">—</div>
|
||||
|
||||
+48
-1
@@ -358,9 +358,56 @@
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
.company-agent-card[data-active="false"] { opacity: 0.55; }
|
||||
/* 비활성 카드는 더 흐릿하게 + 좌측 액센트 바를 떨궈서 한 눈에 그룹 구분되도록. */
|
||||
.company-agent-card[data-active="false"] {
|
||||
opacity: 0.5;
|
||||
background: transparent;
|
||||
border-style: dashed;
|
||||
}
|
||||
.company-agent-card[data-active="true"] {
|
||||
border-left: 3px solid var(--accent);
|
||||
}
|
||||
.company-agent-card[data-locked="true"] {
|
||||
border-left-color: #FACC15; /* CEO는 골드 액센트 */
|
||||
background: linear-gradient(180deg, rgba(250,204,21,0.06) 0%, transparent 100%);
|
||||
}
|
||||
.company-agent-card[data-locked="true"] .company-agent-toggle { cursor: not-allowed; opacity: 0.4; }
|
||||
|
||||
/* 삭제 버튼 — 평소엔 다른 controls 톤과 동일, 파이프라인에서 사용 중이면 disabled. */
|
||||
.company-agent-delete { color: var(--text-dim); }
|
||||
.company-agent-delete:hover:not(.disabled) {
|
||||
color: var(--error); border-color: var(--error);
|
||||
}
|
||||
.company-agent-delete.disabled {
|
||||
opacity: 0.35; cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 숨겨진 빌트인 복원 영역 — 평소엔 시야에 안 들어오게 차분한 톤. */
|
||||
.company-agent-hidden-section {
|
||||
margin-top: 12px; padding: 10px 12px;
|
||||
border: 1px dashed var(--border); border-radius: 8px;
|
||||
background: transparent; list-style: none;
|
||||
}
|
||||
.company-agent-hidden-head {
|
||||
font-size: 11px; color: var(--text-dim); font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.company-agent-hidden-hint {
|
||||
font-size: 10px; color: var(--text-dim); opacity: 0.8;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.company-agent-hidden-list {
|
||||
display: flex; flex-wrap: wrap; gap: 6px;
|
||||
}
|
||||
.company-agent-hidden-chip {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
color: var(--text-primary); font-size: 10.5px;
|
||||
padding: 4px 9px; border-radius: 999px; cursor: pointer;
|
||||
}
|
||||
.company-agent-hidden-chip:hover {
|
||||
border-color: var(--accent); color: var(--accent);
|
||||
}
|
||||
|
||||
.company-agent-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
+74
-74
@@ -12,9 +12,9 @@
|
||||
<div class="header-top">
|
||||
<div class="brand"><div class="logo">✦</div> Astra</div>
|
||||
<div class="header-actions">
|
||||
<button class="icon-btn" id="newChatBtn" data-tooltip="New Chat">New</button>
|
||||
<button class="icon-btn toggle-chip active" id="brainTraceBtn" data-tooltip="Second Brain Trace Mode">Trace</button>
|
||||
<button class="icon-btn toggle-chip" id="internetBtn" data-tooltip="Internet Access">Web</button>
|
||||
<button class="icon-btn" id="newChatBtn" data-tooltip="새 대화 시작 (현재 대화는 기록에 저장)">새 대화</button>
|
||||
<button class="icon-btn toggle-chip active" id="brainTraceBtn" data-tooltip="답변에 사용된 두뇌(지식) 근거를 함께 표시">근거 추적</button>
|
||||
<button class="icon-btn toggle-chip" id="internetBtn" data-tooltip="필요할 때 웹에서 정보를 가져옵니다">웹 검색</button>
|
||||
<!--
|
||||
Corp toggle — replaces the old pill-shaped chip that lived
|
||||
in the records-line. Same rounded-rectangle style as the
|
||||
@@ -22,19 +22,19 @@
|
||||
the adjacent ▾ opens the manage overlay (agents · models ·
|
||||
prompt edits · Knowledge Mix sliders).
|
||||
-->
|
||||
<button class="icon-btn toggle-chip" id="companyChip" data-tooltip="1인 기업 모드 토글">Corp</button>
|
||||
<button class="icon-btn" id="companyManageBtn" data-tooltip="회사 관리 (에이전트·모델·prompt·Knowledge Mix)">▾</button>
|
||||
<button class="icon-btn toggle-chip" id="companyChip" data-tooltip="1인 기업 모드 — CEO가 요청을 분석해 전문 에이전트에게 분배">기업 모드</button>
|
||||
<button class="icon-btn" id="companyManageBtn" data-tooltip="기업 모드 관리 (에이전트 · 모델 · 프롬프트 · 지식 비중)">▾</button>
|
||||
<div class="hdr-dropdown" data-dd>
|
||||
<button class="icon-btn" id="toolsMenuBtn" data-dd-trigger data-tooltip="Tools">Tools ▾</button>
|
||||
<button class="icon-btn" id="toolsMenuBtn" data-dd-trigger data-tooltip="개발자 도구 모음">도구 ▾</button>
|
||||
<div class="hdr-menu" id="toolsMenu" data-dd-menu>
|
||||
<div class="hdr-menu-label">Tools</div>
|
||||
<button class="hdr-menu-item toggle-item" id="brainTraceDebugBtn" data-tooltip="Second Brain Debug JSON">Debug Trace JSON</button>
|
||||
<button class="hdr-menu-item" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">Save Wiki Raw</button>
|
||||
<button class="hdr-menu-item" id="brainBtn" data-tooltip="Sync Knowledge (commit + push)">Sync Knowledge</button>
|
||||
<div class="hdr-menu-label">도구</div>
|
||||
<button class="hdr-menu-item toggle-item" id="brainTraceDebugBtn" data-tooltip="근거 추적의 원본 JSON 표시 (개발자용)">근거 추적 JSON 보기</button>
|
||||
<button class="hdr-menu-item" id="saveWikiRawBtn" data-tooltip="현재 답변의 원본 마크다운을 두뇌(지식)에 저장">원본 답변을 두뇌에 저장</button>
|
||||
<button class="hdr-menu-item" id="brainBtn" data-tooltip="두뇌(지식) 폴더 변경사항을 git commit + push">두뇌 동기화</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="View History">History</button>
|
||||
<button class="icon-btn" id="settingsBtn" data-tooltip="Settings">Set</button>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="이전 대화 기록 보기">기록</button>
|
||||
<button class="icon-btn" id="settingsBtn" data-tooltip="설정 패널 열기">설정</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,62 +47,62 @@
|
||||
|
||||
<div class="context-bar">
|
||||
<div class="context-summary" id="contextSummary">
|
||||
<span class="cb-seg"><span class="cb-key">Brain</span> <span id="ctxBrainName" class="cb-val">—</span></span>
|
||||
<span class="cb-seg"><span class="cb-key">두뇌</span> <span id="ctxBrainName" class="cb-val">—</span></span>
|
||||
<span class="cb-sep">·</span>
|
||||
<span class="cb-seg"><span class="cb-key">Agent</span> <span id="ctxAgentName" class="cb-val">—</span></span>
|
||||
<span class="cb-seg"><span class="cb-key">에이전트</span> <span id="ctxAgentName" class="cb-val">—</span></span>
|
||||
<span class="cb-sep">·</span>
|
||||
<span class="cb-seg"><span class="cb-key">Project</span> <span id="ctxProjectName" class="cb-val">—</span></span>
|
||||
<span class="cb-seg"><span class="cb-key">프로젝트</span> <span id="ctxProjectName" class="cb-val">—</span></span>
|
||||
</div>
|
||||
<div class="hdr-dropdown context-edit-dd" data-dd>
|
||||
<button class="icon-btn" id="contextEditBtn" data-dd-trigger data-tooltip="Model / Brain / Agent / Knowledge Map / Project">Edit ▾</button>
|
||||
<button class="icon-btn" id="contextEditBtn" data-dd-trigger data-tooltip="모델 · 두뇌 · 에이전트 · 프로젝트 변경">변경 ▾</button>
|
||||
<div class="hdr-menu hdr-menu-wide" id="contextEditMenu" data-dd-menu>
|
||||
<div class="hdr-menu-label">Model</div>
|
||||
<div class="select-wrap"><select id="modelSel" title="Select Model"></select></div>
|
||||
<div class="hdr-menu-label">모델</div>
|
||||
<div class="select-wrap"><select id="modelSel" title="이 대화에 사용할 모델"></select></div>
|
||||
|
||||
<div class="hdr-menu-label">
|
||||
Knowledge Mix
|
||||
<span class="hdr-menu-hint" id="knowledgeMixHint">Model 50% · Brain 50%</span>
|
||||
지식 비중
|
||||
<span class="hdr-menu-hint" id="knowledgeMixHint">모델 50% · 두뇌 50%</span>
|
||||
</div>
|
||||
<div class="knowledge-mix-control" title="Slide left for more model knowledge, right for more Second Brain reliance">
|
||||
<span class="km-end-label">Model</span>
|
||||
<div class="knowledge-mix-control" title="왼쪽으로 갈수록 모델 자체 지식, 오른쪽으로 갈수록 내 두뇌(저장된 지식)에 의존합니다">
|
||||
<span class="km-end-label">모델</span>
|
||||
<input type="range" id="knowledgeMixSlider" min="0" max="100" step="5" value="50" class="km-slider">
|
||||
<span class="km-end-label">Brain</span>
|
||||
<span class="km-end-label">두뇌</span>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Brain</div>
|
||||
<div class="hdr-menu-label">두뇌 (지식 폴더)</div>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="brainSel" title="Select Brain"></select></div>
|
||||
<div class="tool-group" aria-label="Brain actions">
|
||||
<button class="icon-btn" id="addBrainBtn" data-tooltip="Add Brain">Add</button>
|
||||
<button class="icon-btn" id="editBrainBtn" data-tooltip="Edit Brain">Edit</button>
|
||||
<button class="icon-btn" id="deleteBrainBtn" data-tooltip="Delete Brain">Del</button>
|
||||
<div class="select-wrap"><select id="brainSel" title="사용할 두뇌(지식 폴더) 선택"></select></div>
|
||||
<div class="tool-group" aria-label="두뇌 관리">
|
||||
<button class="icon-btn" id="addBrainBtn" data-tooltip="새 두뇌 추가">추가</button>
|
||||
<button class="icon-btn" id="editBrainBtn" data-tooltip="선택한 두뇌 수정">수정</button>
|
||||
<button class="icon-btn" id="deleteBrainBtn" data-tooltip="선택한 두뇌 삭제">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Agent</div>
|
||||
<div class="hdr-menu-label">에이전트</div>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="agentSel" title="Select Agentic Skill"></select></div>
|
||||
<div class="tool-group" aria-label="Agent actions">
|
||||
<button class="icon-btn" id="addAgentBtn" data-tooltip="Create Agent">Add</button>
|
||||
<button class="icon-btn" id="editAgentBtn" data-tooltip="Edit Agent Skill">Edit</button>
|
||||
<button class="icon-btn" id="deleteAgentBtn" data-tooltip="Delete Agent Skill">Del</button>
|
||||
<div class="select-wrap"><select id="agentSel" title="대화에 적용할 에이전트(역할) 선택"></select></div>
|
||||
<div class="tool-group" aria-label="에이전트 관리">
|
||||
<button class="icon-btn" id="addAgentBtn" data-tooltip="새 에이전트 만들기">추가</button>
|
||||
<button class="icon-btn" id="editAgentBtn" data-tooltip="선택한 에이전트 수정">수정</button>
|
||||
<button class="icon-btn" id="deleteAgentBtn" data-tooltip="선택한 에이전트 삭제">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Agent ↔ Knowledge</div>
|
||||
<div class="hdr-menu-label">에이전트 ↔ 지식 연결</div>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="knowledgeScopeSel" title="Knowledge folders mapped to this agent"></select></div>
|
||||
<div class="tool-group" aria-label="Knowledge map actions">
|
||||
<button class="icon-btn" id="editKnowledgeMapBtn" data-tooltip="Edit Agent ↔ Knowledge Map">Map</button>
|
||||
<button class="icon-btn" id="reloadKnowledgeMapBtn" data-tooltip="Reload Knowledge Map">Rld</button>
|
||||
<div class="select-wrap"><select id="knowledgeScopeSel" title="이 에이전트가 참고할 지식 폴더 범위"></select></div>
|
||||
<div class="tool-group" aria-label="지식 연결 관리">
|
||||
<button class="icon-btn" id="editKnowledgeMapBtn" data-tooltip="에이전트별 지식 폴더 연결 편집">연결 편집</button>
|
||||
<button class="icon-btn" id="reloadKnowledgeMapBtn" data-tooltip="연결 정보를 디스크에서 다시 읽기">새로고침</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Project (Chronicle)</div>
|
||||
<div class="hdr-menu-label">프로젝트 (작업 기록)</div>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="designerSel" title="Select Designer Project"></select></div>
|
||||
<div class="tool-group" aria-label="Designer actions">
|
||||
<button class="icon-btn" id="addDesignerBtn" data-tooltip="Create Designer Project">Add</button>
|
||||
<div class="select-wrap"><select id="designerSel" title="작업 기록을 남길 프로젝트 선택"></select></div>
|
||||
<div class="tool-group" aria-label="프로젝트 관리">
|
||||
<button class="icon-btn" id="addDesignerBtn" data-tooltip="새 프로젝트 만들기">추가</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,19 +112,19 @@
|
||||
<div class="records-line">
|
||||
<div class="rl-summary">
|
||||
<span class="status-dot ready"></span>
|
||||
<span id="chronicleAutoStatus" title="Project records are saved automatically after meaningful project turns.">Auto Records</span>
|
||||
<span id="chronicleAutoStatus" title="의미 있는 대화 후 프로젝트 기록이 자동으로 저장됩니다.">자동 기록</span>
|
||||
<span class="rl-latest" id="recordsLatest"></span>
|
||||
</div>
|
||||
<!-- (Removed) Corp chip moved to the header toolbar above —
|
||||
see #companyChip / #companyManageBtn alongside New/Trace/Web. -->
|
||||
<div class="hdr-dropdown" data-dd>
|
||||
<button class="icon-btn" id="recordsMenuBtn" data-dd-trigger data-tooltip="Chronicle records">Records ▾</button>
|
||||
<button class="icon-btn" id="recordsMenuBtn" data-dd-trigger data-tooltip="저장된 작업 기록 열기">기록 ▾</button>
|
||||
<div class="hdr-menu hdr-menu-wide" id="recordsMenu" data-dd-menu>
|
||||
<div class="hdr-menu-label">Chronicle Records</div>
|
||||
<div class="select-wrap"><select id="chronicleRecordSel" title="Select Chronicle Record"></select></div>
|
||||
<button class="hdr-menu-item" id="openChronicleRecordBtn" data-tooltip="Open Selected Record">Open Selected Record</button>
|
||||
<button class="hdr-menu-item" id="refreshChronicleRecordsBtn" data-tooltip="Refresh Records">Refresh Records</button>
|
||||
<button class="hdr-menu-item" id="openDesignerBtn" data-tooltip="Open Record Folder">Open Record Folder</button>
|
||||
<div class="hdr-menu-label">작업 기록</div>
|
||||
<div class="select-wrap"><select id="chronicleRecordSel" title="열어볼 작업 기록 선택"></select></div>
|
||||
<button class="hdr-menu-item" id="openChronicleRecordBtn" data-tooltip="선택한 기록 열기">선택한 기록 열기</button>
|
||||
<button class="hdr-menu-item" id="refreshChronicleRecordsBtn" data-tooltip="기록 목록 다시 불러오기">기록 새로고침</button>
|
||||
<button class="hdr-menu-item" id="openDesignerBtn" data-tooltip="기록이 저장된 폴더 열기">기록 폴더 열기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -166,7 +166,7 @@
|
||||
<div class="map-section-hint">CEO는 항상 활성. 각 에이전트별로 모델을 따로 지정할 수 있습니다 — 다른 모델을 쓸 때만 LM Studio가 swap합니다.</div>
|
||||
</div>
|
||||
<div class="map-btn-group">
|
||||
<button class="secondary-btn" id="addCompanyAgentBtn" title="새 사용자 에이전트 추가">+ Agent</button>
|
||||
<button class="secondary-btn" id="addCompanyAgentBtn" title="새 사용자 에이전트 추가">+ 에이전트 추가</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="companyAgentList" class="map-list company-agent-list"></ul>
|
||||
@@ -228,7 +228,7 @@
|
||||
style="padding:3px 6px; font-size:10px; background:var(--surface); color:var(--text-primary); border:1px solid var(--border); border-radius:5px;">
|
||||
<option value="">📋 템플릿에서…</option>
|
||||
</select>
|
||||
<button class="secondary-btn" id="addCompanyPipelineBtn" title="빈 파이프라인부터 시작">+ 빈 Pipeline</button>
|
||||
<button class="secondary-btn" id="addCompanyPipelineBtn" title="빈 파이프라인부터 시작">+ 빈 파이프라인</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-row" style="margin-top:8px; gap:8px; align-items:center;">
|
||||
@@ -255,7 +255,7 @@
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-top:12px; margin-bottom:6px;">
|
||||
<div style="font-size:11px; font-weight:700; color:var(--text-bright);">단계 (Stages)</div>
|
||||
<button class="secondary-btn" id="addStageBtn" style="font-size:10px; padding:3px 8px;">+ Stage 추가</button>
|
||||
<button class="secondary-btn" id="addStageBtn" style="font-size:10px; padding:3px 8px;">+ 단계 추가</button>
|
||||
</div>
|
||||
<ul id="pipelineStageList" class="pipeline-stage-list"></ul>
|
||||
<div class="editor-actions" style="margin-top:10px;">
|
||||
@@ -354,7 +354,7 @@
|
||||
</div>
|
||||
|
||||
<div class="map-footer">
|
||||
<button class="secondary-btn" id="editAgentMapJsonBtn" title="고급 사용자용: JSON으로 직접 편집">JSON 편집</button>
|
||||
<button class="secondary-btn" id="editAgentMapJsonBtn" title="고급 사용자용: JSON으로 직접 편집">고급 (JSON)</button>
|
||||
<div style="flex:1"></div>
|
||||
<button class="secondary-btn" id="cancelAgentMapBtn">취소</button>
|
||||
<button class="send-btn" id="saveAgentMapBtn">저장</button>
|
||||
@@ -377,8 +377,8 @@
|
||||
<div class="chat" id="chat">
|
||||
<div class="welcome">
|
||||
<div class="welcome-logo">✦</div>
|
||||
<div class="welcome-title">Welcome to Astra</div>
|
||||
<p>Your premium local AI assistant.<br>Ready to analyze projects and build reports.</p>
|
||||
<div class="welcome-title">Astra에 오신 것을 환영합니다</div>
|
||||
<p>로컬에서 동작하는 개인 AI 비서입니다.<br>아래 입력창에 무엇을 도와줄지 적어보세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -406,39 +406,39 @@
|
||||
</div>
|
||||
<div class="arch-chip-actions" id="archChipActions">
|
||||
<!-- active state buttons -->
|
||||
<button class="arch-chip-btn arch-chip-active-only" id="archOpenBtn" title="Architecture 문서 열기">Open</button>
|
||||
<button class="arch-chip-btn arch-chip-active-only" id="archRefreshBtn" title="지금 다시 스캔">Refresh</button>
|
||||
<button class="arch-chip-btn arch-chip-active-only" id="archDetachBtn" title="자동 첨부 끄기">Detach</button>
|
||||
<button class="arch-chip-btn arch-chip-active-only" id="archOpenBtn" title="프로젝트 구조 문서 열기">열기</button>
|
||||
<button class="arch-chip-btn arch-chip-active-only" id="archRefreshBtn" title="프로젝트를 지금 다시 스캔">새로고침</button>
|
||||
<button class="arch-chip-btn arch-chip-active-only" id="archDetachBtn" title="이 프로젝트 컨텍스트 자동 첨부 끄기">분리</button>
|
||||
<!-- inactive state button -->
|
||||
<button class="arch-chip-btn arch-chip-inactive-only" id="archAttachBtn" title="이 프로젝트에 architecture 자동 첨부 켜기">Attach</button>
|
||||
<button class="arch-chip-btn arch-chip-inactive-only" id="archAttachBtn" title="이 프로젝트 컨텍스트를 대화에 자동 첨부">첨부</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="agentConfigPanel" class="panel">
|
||||
<div class="field-label">Agent Persona/Instructions</div>
|
||||
<textarea id="agentPrompt" rows="5" placeholder="Agent Persona & Instructions..."></textarea>
|
||||
<div class="field-label">에이전트 페르소나 / 지시사항</div>
|
||||
<textarea id="agentPrompt" rows="5" placeholder="이 에이전트가 어떤 역할로, 어떤 톤으로 답해야 하는지 적으세요..."></textarea>
|
||||
|
||||
<div class="field-label">Negative Prompt (Strict Rules)</div>
|
||||
<textarea id="negativePrompt" rows="2" placeholder="What NOT to do..."></textarea>
|
||||
<div class="field-label">금지 사항 (반드시 지킬 규칙)</div>
|
||||
<textarea id="negativePrompt" rows="2" placeholder="이 에이전트가 절대 하지 말아야 할 것..."></textarea>
|
||||
|
||||
<button id="updateAgentBtn" class="secondary-btn">Update Agent Skill</button>
|
||||
<button id="updateAgentBtn" class="secondary-btn">에이전트 저장</button>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<div id="attachPreview" class="attachment-preview"></div>
|
||||
<textarea id="input" rows="1" placeholder="Ask Astra..."></textarea>
|
||||
<textarea id="input" rows="1" placeholder="Astra에게 무엇이든 물어보세요..."></textarea>
|
||||
<div class="input-footer">
|
||||
<div class="footer-left">
|
||||
<button class="icon-btn" id="attachBtn" title="Attach Files">📎</button>
|
||||
<span class="model-pill" title="Switch model for this conversation">
|
||||
<span class="model-prefix">Model:</span>
|
||||
<select id="modelInlineSel" class="model-inline-sel" title="Switch model"></select>
|
||||
<button class="icon-btn" id="attachBtn" title="파일 첨부 (이미지 · 문서 · 코드)">📎</button>
|
||||
<span class="model-pill" title="이 대화에 사용할 모델 변경">
|
||||
<span class="model-prefix">모델:</span>
|
||||
<select id="modelInlineSel" class="model-inline-sel" title="모델 변경"></select>
|
||||
</span>
|
||||
<span id="statusLabel" class="status-label"></span>
|
||||
<span id="ctxBadge" class="ctx-badge" title="직전 요청에 실제로 들어간 컨텍스트 추정치"></span>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<button id="cancelBtn" class="cancel-btn" title="Clear draft" style="display:none;">✕ Clear</button>
|
||||
<button id="stopBtn" class="stop-btn" title="Stop generation" style="display:none;">■ Stop</button>
|
||||
<button id="sendBtn" class="send-btn">Send</button>
|
||||
<button id="cancelBtn" class="cancel-btn" title="입력 비우기" style="display:none;">✕ 비우기</button>
|
||||
<button id="stopBtn" class="stop-btn" title="생성 중단" style="display:none;">■ 중단</button>
|
||||
<button id="sendBtn" class="send-btn">보내기</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="toastNotif" class="toast-notif"></div>
|
||||
|
||||
+94
-22
@@ -179,7 +179,7 @@
|
||||
const recordsLatest = document.getElementById('recordsLatest');
|
||||
|
||||
function escAttr(t) { return String(t == null ? '' : t).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); }
|
||||
function fmtMixHint(w) { return `Model ${100 - w}% · Brain ${w}%`; }
|
||||
function fmtMixHint(w) { return `모델 ${100 - w}% · 두뇌 ${w}%`; }
|
||||
/** Compact relative-time formatter for chips (e.g. "2m ago", "just now"). */
|
||||
function formatRelativeTime(iso) {
|
||||
try {
|
||||
@@ -405,7 +405,7 @@
|
||||
: Math.max(0, Math.min(100, agentMapDraft.secondBrainWeight | 0));
|
||||
slider.value = String(value);
|
||||
hint.textContent = useGlobal
|
||||
? `Use global · ${fmtMixHint(value)}`
|
||||
? `전역 설정 사용 · ${fmtMixHint(value)}`
|
||||
: fmtMixHint(value);
|
||||
}
|
||||
|
||||
@@ -815,12 +815,24 @@
|
||||
case 'deleteCompanyAgentResult': {
|
||||
const v = msg.value || {};
|
||||
if (v.ok) {
|
||||
showToast(`🗑 '${v.agentId}' 에이전트 삭제됨`, 'warn');
|
||||
const msg2 = v.kind === 'builtin-hidden'
|
||||
? `🗑 기본 에이전트 '${v.agentId}' 숨김 처리 (목록 하단에서 복원 가능)`
|
||||
: `🗑 '${v.agentId}' 에이전트 삭제됨`;
|
||||
showToast(msg2, 'warn');
|
||||
} else {
|
||||
showToast(`삭제 실패: ${v.reason || '알 수 없음'}`, 'warn');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'restoreHiddenAgentResult': {
|
||||
const v = msg.value || {};
|
||||
if (v.ok) {
|
||||
showToast(`↩ '${v.agentId}' 에이전트 복원 완료`, 'info');
|
||||
} else {
|
||||
showToast(`복원 실패: ${v.reason || '알 수 없음'}`, 'warn');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'companyPipelines': {
|
||||
if (typeof window.__renderCompanyPipelines === 'function') {
|
||||
window.__renderCompanyPipelines(msg.value || {});
|
||||
@@ -2279,7 +2291,24 @@
|
||||
}
|
||||
if (payload && payload.roleCategoryLabels) _roleCategoryLabels = payload.roleCategoryLabels;
|
||||
if (payload && Array.isArray(payload.roleCategoryOrder)) _roleCategoryOrder = payload.roleCategoryOrder;
|
||||
const agents = (payload && Array.isArray(payload.agents)) ? payload.agents : [];
|
||||
const rawAgents = (payload && Array.isArray(payload.agents)) ? payload.agents : [];
|
||||
// 활성/비활성 정렬: CEO는 항상 최상단(alwaysOn), 그 다음 활성(켜짐) 에이전트,
|
||||
// 마지막으로 비활성. 같은 그룹 내에서는 백엔드가 보낸 원래 순서를 유지(stable).
|
||||
const agents = rawAgents
|
||||
.map((a, i) => ({ a, i }))
|
||||
.sort((x, y) => {
|
||||
// CEO/alwaysOn 최우선
|
||||
const xLocked = x.a.alwaysOn ? 0 : 1;
|
||||
const yLocked = y.a.alwaysOn ? 0 : 1;
|
||||
if (xLocked !== yLocked) return xLocked - yLocked;
|
||||
// 그 다음 active 여부
|
||||
const xAct = x.a.active ? 0 : 1;
|
||||
const yAct = y.a.active ? 0 : 1;
|
||||
if (xAct !== yAct) return xAct - yAct;
|
||||
// tiebreak: 원래 순서
|
||||
return x.i - y.i;
|
||||
})
|
||||
.map((p) => p.a);
|
||||
for (const a of agents) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'company-agent-card';
|
||||
@@ -2358,10 +2387,10 @@
|
||||
|
||||
const editBtn = document.createElement('button');
|
||||
editBtn.className = 'company-agent-edit';
|
||||
editBtn.textContent = '✎ Edit';
|
||||
editBtn.textContent = '✎ 편집';
|
||||
if (a.personaOverridden || a.specialtyOverridden || a.taglineOverridden) {
|
||||
editBtn.classList.add('dirty');
|
||||
editBtn.title = 'prompt 편집됨 (원본과 다름)';
|
||||
editBtn.title = '프롬프트가 사용자에 의해 편집되었습니다 (원본과 다름)';
|
||||
}
|
||||
editBtn.onclick = () => {
|
||||
const expanded = li.getAttribute('data-expanded') === 'true';
|
||||
@@ -2370,15 +2399,15 @@
|
||||
|
||||
const toggle = document.createElement('button');
|
||||
toggle.className = 'company-agent-toggle';
|
||||
toggle.textContent = a.active ? 'ON' : 'OFF';
|
||||
toggle.textContent = a.active ? '켜짐' : '꺼짐';
|
||||
if (a.alwaysOn) {
|
||||
toggle.disabled = true;
|
||||
toggle.textContent = 'LOCKED';
|
||||
toggle.textContent = '고정';
|
||||
} else {
|
||||
toggle.onclick = () => {
|
||||
const wantActive = !(li.getAttribute('data-active') === 'true');
|
||||
li.setAttribute('data-active', wantActive ? 'true' : 'false');
|
||||
toggle.textContent = wantActive ? 'ON' : 'OFF';
|
||||
toggle.textContent = wantActive ? '켜짐' : '꺼짐';
|
||||
const nextIds = Array.from(_companyAgentList.querySelectorAll('.company-agent-card'))
|
||||
.filter(el => el.getAttribute('data-active') === 'true')
|
||||
.map(el => el.dataset.agentId)
|
||||
@@ -2389,17 +2418,30 @@
|
||||
controls.appendChild(roleSelEl);
|
||||
controls.appendChild(modelSelEl);
|
||||
controls.appendChild(editBtn);
|
||||
// 사용자 추가 에이전트만 삭제 가능. 기본 9명은 코드에 박혀 있어
|
||||
// 백엔드도 거부하므로 UI에서도 버튼을 노출하지 않음.
|
||||
if (a.custom) {
|
||||
// 삭제 버튼 — CEO만 빼고 빌트인/커스텀 모두 노출. 단, 어떤 워크 파이프라인의
|
||||
// stage라도 이 에이전트를 참조하고 있으면 disabled로 처리하고 tooltip에
|
||||
// 사용 중인 파이프라인을 적어, 사용자가 어디로 가서 빼야 하는지 보이게 한다.
|
||||
if (!a.alwaysOn) {
|
||||
const delBtn = document.createElement('button');
|
||||
delBtn.className = 'company-agent-edit';
|
||||
delBtn.className = 'company-agent-edit company-agent-delete';
|
||||
delBtn.textContent = '🗑';
|
||||
delBtn.title = `'${a.name}' 에이전트 삭제`;
|
||||
delBtn.onclick = () => {
|
||||
if (!confirm(`'${a.name}' 에이전트를 삭제할까요? 이 에이전트의 모든 설정(모델·프롬프트·지식 믹스)도 함께 삭제됩니다.`)) return;
|
||||
vscode.postMessage({ type: 'deleteCompanyAgent', agentId: a.id });
|
||||
};
|
||||
const usedIn = Array.isArray(a.usedInPipelines) ? a.usedInPipelines : [];
|
||||
if (usedIn.length > 0) {
|
||||
delBtn.disabled = true;
|
||||
delBtn.classList.add('disabled');
|
||||
delBtn.title = `삭제 불가 — 다음 파이프라인에서 사용 중: ${usedIn.map((n) => `'${n}'`).join(', ')}. 파이프라인에서 먼저 빼거나 다른 에이전트로 교체하세요.`;
|
||||
} else {
|
||||
delBtn.title = a.custom
|
||||
? `'${a.name}' 에이전트 삭제 (모든 설정 함께 제거)`
|
||||
: `'${a.name}' 에이전트 숨기기 (기본 에이전트라 복원 가능)`;
|
||||
delBtn.onclick = () => {
|
||||
const confirmMsg = a.custom
|
||||
? `'${a.name}' 에이전트를 삭제할까요? 이 에이전트의 모든 설정(모델·프롬프트·지식 비중)도 함께 삭제됩니다.`
|
||||
: `기본 에이전트 '${a.name}'을(를) 목록에서 숨길까요? 나중에 [목록 맨 아래 → 삭제된 기본 에이전트] 영역에서 복원할 수 있습니다.`;
|
||||
if (!confirm(confirmMsg)) return;
|
||||
vscode.postMessage({ type: 'deleteCompanyAgent', agentId: a.id });
|
||||
};
|
||||
}
|
||||
controls.appendChild(delBtn);
|
||||
}
|
||||
controls.appendChild(toggle);
|
||||
@@ -2422,6 +2464,36 @@
|
||||
li.appendChild(_buildAgentPromptEditor(a));
|
||||
_companyAgentList.appendChild(li);
|
||||
}
|
||||
// ── 숨겨진 기본 에이전트 복원 영역 ──
|
||||
// 사용자가 "삭제"한 빌트인 에이전트를 다시 꺼낼 수 있는 출구. 빈 배열이면
|
||||
// 섹션 자체를 그리지 않아 평소엔 시야에서 사라진다.
|
||||
const hidden = Array.isArray(payload && payload.hiddenBuiltins) ? payload.hiddenBuiltins : [];
|
||||
if (hidden.length > 0) {
|
||||
const restoreLi = document.createElement('li');
|
||||
restoreLi.className = 'company-agent-hidden-section';
|
||||
const head = document.createElement('div');
|
||||
head.className = 'company-agent-hidden-head';
|
||||
head.textContent = `삭제된 기본 에이전트 (${hidden.length}명)`;
|
||||
restoreLi.appendChild(head);
|
||||
const hint = document.createElement('div');
|
||||
hint.className = 'company-agent-hidden-hint';
|
||||
hint.textContent = '복원하면 목록에 다시 추가됩니다. 설정은 모두 디폴트로 시작합니다.';
|
||||
restoreLi.appendChild(hint);
|
||||
const list = document.createElement('div');
|
||||
list.className = 'company-agent-hidden-list';
|
||||
for (const h of hidden) {
|
||||
const chip = document.createElement('button');
|
||||
chip.className = 'company-agent-hidden-chip';
|
||||
chip.textContent = `${h.emoji || '🙂'} ${h.name} · ${h.role} ↩ 복원`;
|
||||
chip.title = `'${h.name}' 복원`;
|
||||
chip.onclick = () => {
|
||||
vscode.postMessage({ type: 'restoreHiddenAgent', agentId: h.id });
|
||||
};
|
||||
list.appendChild(chip);
|
||||
}
|
||||
restoreLi.appendChild(list);
|
||||
_companyAgentList.appendChild(restoreLi);
|
||||
}
|
||||
if (_companyStatusEl) _companyStatusEl.textContent = '';
|
||||
}
|
||||
|
||||
@@ -2573,15 +2645,15 @@
|
||||
|
||||
const resetBtn = document.createElement('button');
|
||||
resetBtn.className = 'danger';
|
||||
resetBtn.textContent = 'Reset';
|
||||
resetBtn.title = '이 에이전트의 모든 override 제거 → 디폴트로 복귀 (이름·역할·프롬프트 전부)';
|
||||
resetBtn.textContent = '기본값으로';
|
||||
resetBtn.title = '이 에이전트의 모든 사용자 변경 사항 제거 → 디폴트로 복귀 (이름·역할·프롬프트 전부)';
|
||||
resetBtn.onclick = () => {
|
||||
vscode.postMessage({ type: 'setCompanyAgentPrompt', agentId: a.id, override: null });
|
||||
vscode.postMessage({ type: 'setCompanyAgentDisplay', agentId: a.id, override: null });
|
||||
};
|
||||
|
||||
const cancelBtn = document.createElement('button');
|
||||
cancelBtn.textContent = 'Cancel';
|
||||
cancelBtn.textContent = '취소';
|
||||
cancelBtn.onclick = () => {
|
||||
const card = editor.closest('.company-agent-card');
|
||||
if (card) card.setAttribute('data-expanded', 'false');
|
||||
@@ -2589,7 +2661,7 @@
|
||||
|
||||
const saveBtn = document.createElement('button');
|
||||
saveBtn.className = 'primary';
|
||||
saveBtn.textContent = 'Save';
|
||||
saveBtn.textContent = '저장';
|
||||
saveBtn.onclick = () => {
|
||||
// 두 개의 message로 분리 전송 — display(name/role/emoji/color)와
|
||||
// prompt(tagline/specialty/persona)는 백엔드에서 서로 다른
|
||||
|
||||
Reference in New Issue
Block a user