Files
connectai/media/sidebar.html
T
g1nation 0712014fcb chore: v2.2.73 — ASTRA-DEBUG 로그 레벨 + webview CSP font-src 보강
- ASTRA-DEBUG 정상 흐름 로그를 console.error → logInfo/console.log 로 강등
  (chatHandlers, extension, slashRouter): DevTools에 ERR로 찍히던 오탐 제거
- sidebar webview에 명시적 CSP meta 추가 + font-src에 data: 허용
  (sidebar.html, sidebarProvider._getHtml): VS Code outer iframe이 codicon.ttf를
  data:font/ttf 로 inject하면서 기본 CSP에 막혀 매 prompt 마다 violation
  경고가 찍히던 문제 해소
- 누적된 LM Studio / agent / 컨텍스트 매니저 / 테스트 갱신 동반

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 15:52:19 +09:00

539 lines
34 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="__CSP__">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Astra</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<link rel="stylesheet" href="__STYLES_URI__">
</head>
<body>
<div class="header">
<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="새 대화 시작 (현재 대화는 기록에 저장)">새 대화</button>
<button class="icon-btn toggle-chip active" id="brainTraceBtn" data-tooltip="답변에 사용된 두뇌(지식) 근거를 함께 표시">근거 추적</button>
<!--
Corp toggle — replaces the old pill-shaped chip that lived
in the records-line. Same rounded-rectangle style as the
other header buttons. Clicking toggles 1인 기업 모드 on/off;
the adjacent ▾ opens the manage overlay (agents · models ·
prompt edits · Knowledge Mix sliders).
-->
<button class="icon-btn toggle-chip" id="companyChip" data-tooltip="1인 기업 모드 — CEO가 요청을 분석해 전문 에이전트에게 분배">기업 모드</button>
<!--
스코프 프리셋 segmented control. 기업 모드 ON 일 때만 표시
(companyChip.active 시 .scope-seg.visible). 각 버튼 = 1개 template:
· 기획만 → plan-only (3 stages)
· 개발까지 → dev-only (10 stages, dev-impl 까지)
· 풀 → full-product-dev (13 stages, 배포까지)
클릭 → setCompanyScopePreset 로 backend stamp + activate. 현재
activePipelineId 가 어느 template 의 suggestedPipelineId 와
일치하느냐로 active 표시 결정.
-->
<div class="scope-seg" id="companyScopeSeg" hidden>
<button class="scope-seg-btn" data-scope="plan-only" data-tooltip="기획서까지만 — 시장조사 → 방향성 → 기획문서 (3단계)">기획만</button>
<button class="scope-seg-btn" data-scope="dev-only" data-tooltip="개발까지만 — 기획 → 설계 → 코드 구현 (10단계, QA·배포 제외)">개발까지</button>
<button class="scope-seg-btn" data-scope="full-product-dev" data-tooltip="풀 파이프라인 — 기획부터 배포까지 (13단계)"></button>
</div>
<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="개발자 도구 모음">도구 ▾</button>
<div class="hdr-menu hdr-menu-wide" id="toolsMenu" data-dd-menu>
<div class="hdr-menu-label">자동 기록</div>
<button class="hdr-menu-item toggle-item" id="chronicleAutoRecordBtn" data-tooltip="의미 있는 대화 turn 을 활성 프로젝트의 Chronicle 폴더에 자동 저장">자동 기록: 켜짐</button>
<div class="hdr-menu-hint" id="chronicleAutoStatus" title="가장 최근에 자동 저장된 기록"><span class="status-dot ready"></span> <span id="recordsLatest"></span></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 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="이전 대화 기록 보기">기록</button>
<button class="icon-btn" id="settingsBtn" data-tooltip="설정 패널 열기">설정</button>
</div>
</div>
</div>
<div id="readyBar" class="ready-bar" title="현재 준비 상태 — 엔진 / 모델 / 컨텍스트 창 / 메모리">
<span class="rb-dot" id="rbDot"></span>
<span class="rb-content" id="rbContent">준비 상태 확인 중…</span>
<span id="statusDot" class="status-dot" hidden></span><span id="engineStatusText" hidden></span>
</div>
<div class="context-bar">
<div class="context-summary" id="contextSummary">
<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">에이전트</span> <span id="ctxAgentName" class="cb-val"></span></span>
<span class="cb-sep">·</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="모델 · 두뇌 · 에이전트 · 프로젝트 변경">변경 ▾</button>
<div class="hdr-menu hdr-menu-wide" id="contextEditMenu" data-dd-menu>
<div class="hdr-menu-label">모델</div>
<div class="select-wrap"><select id="modelSel" title="이 대화에 사용할 모델"></select></div>
<div class="hdr-menu-label">
지식 비중
<span class="hdr-menu-hint" id="knowledgeMixHint">모델 50% · 두뇌 50%</span>
</div>
<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">두뇌</span>
</div>
<div class="hdr-menu-label">두뇌 (지식 폴더)</div>
<div class="control-row">
<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">에이전트</div>
<div class="control-row">
<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">에이전트 ↔ 지식 연결</div>
<div class="control-row">
<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">프로젝트 (작업 기록)</div>
<div class="control-row">
<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>
</div>
</div>
<!-- v2.2.71 — records-line 전체를 도구 ▾ 드롭다운 안으로 이동. 사이드바 본체엔 더 이상 자동 기록
라벨/selector/기록 ▾ 가 노출되지 않는다. 모든 자동 기록 UI 는 도구 ▾ 메뉴 첫 섹션 (자동 기록) 에서 접근. -->
<!--
Company manage overlay. Uses the same overlay framework as the agent
knowledge map modal (`.history-overlay` / `.visible`) so styling and
keyboard dismissal stay consistent.
-->
<div id="companyOverlay" class="history-overlay">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px;">
<div>
<h2 style="color:var(--text-bright); margin:0;">🏢 1인 기업 모드</h2>
<p style="margin:4px 0 0; font-size:11px; color:var(--text-dim);">
CEO가 사용자의 요청을 분석하고 활성화된 specialist에게 순차 dispatch합니다.
동시에 메모리에 올라가는 모델은 항상 1개입니다.
</p>
</div>
<button class="icon-btn" id="closeCompanyOverlayBtn"></button>
</div>
<!-- 이어서 진행할 수 있는 미완 작업. 빈 목록이면 JS가 섹션 자체를 숨겨서
평소에는 시야에 들어오지 않는다. 각 카드는 [이어서 진행] / [버리기]
두 버튼만 노출. -->
<div id="companyResumableSection" class="map-section company-resumable-section" data-empty="true">
<div class="map-section-head">
<div>
<div class="map-section-title">이어서 진행할 수 있는 작업</div>
<div class="map-section-hint">중간에 멈춘 작업이 있을 때만 노출됩니다. 누른 시점부터 다음 단계가 같은 세션 폴더에 이어 기록됩니다.</div>
</div>
</div>
<ul id="companyResumableList" class="map-list company-resumable-list"></ul>
</div>
<div class="map-section">
<div class="map-section-head">
<div>
<div class="map-section-title">회사 정보</div>
<div class="map-section-hint">CEO와 보고서에 사용되는 회사명. 한국어/영어 모두 가능.</div>
</div>
</div>
<div class="control-row" style="margin-top:8px;">
<input id="companyNameInput" type="text" class="company-name-input" placeholder="회사명 (예: My Company)" />
<button class="secondary-btn" id="saveCompanyNameBtn">저장</button>
</div>
</div>
<div class="map-section">
<div class="map-section-head">
<div>
<div class="map-section-title">AI 팀 구성</div>
<div class="map-section-hint">이번 작업에 참여할 팀원을 고릅니다. 대표는 항상 참여하고, 모델·지식 반영은 필요할 때만 조정하세요.</div>
</div>
<div class="map-btn-group">
<button class="secondary-btn" id="addCompanyAgentBtn" title="새 사용자 에이전트 추가">+ 팀원 추가</button>
</div>
</div>
<ul id="companyAgentList" class="map-list company-agent-list"></ul>
</div>
<!-- Inline form for adding a new custom agent. Hidden by default; the
"+ Agent" button toggles `data-open` to show it. Kept in-overlay
(no separate modal) so the user can see existing agents while
editing — easier to spot id collisions. -->
<div id="addCompanyAgentForm" class="map-section company-agent-add-form" data-open="false">
<div class="map-section-title" style="margin-bottom:8px;">새 팀원 추가</div>
<div class="company-agent-add-grid">
<label class="field-label">내부 ID (고급 · 소문자/숫자/-/_, 예: marketer)
<input type="text" id="newAgentId" placeholder="marketer" />
</label>
<label class="field-label">이름
<input type="text" id="newAgentName" placeholder="현수" />
</label>
<label class="field-label">역할
<input type="text" id="newAgentRole" placeholder="Marketing Lead" />
</label>
<label class="field-label">이모지
<input type="text" id="newAgentEmoji" placeholder="📣" maxlength="4" />
</label>
<label class="field-label">테마 색상 (#hex)
<input type="text" id="newAgentColor" placeholder="#3B82F6" />
</label>
<label class="field-label" style="grid-column:1/-1;">역할 그룹
<select id="newAgentRoleCategory"></select>
</label>
<label class="field-label" style="grid-column:1/-1;">한 줄 태그라인
<input type="text" id="newAgentTagline" placeholder="브랜드 메시지·캠페인을 설계합니다" />
</label>
<label class="field-label" style="grid-column:1/-1;">잘하는 일
<textarea id="newAgentSpecialty" rows="2" placeholder="캠페인 기획, 메시지 설계, 채널 분석"></textarea>
</label>
<label class="field-label" style="grid-column:1/-1;">응답 스타일 (선택)
<textarea id="newAgentPersona" rows="2" placeholder="데이터 기반·간결한 톤. 가설 검증 사이클을 좋아함."></textarea>
</label>
</div>
<div class="editor-actions" style="margin-top:10px;">
<button id="cancelAddAgentBtn">취소</button>
<button class="primary" id="saveAddAgentBtn">추가</button>
</div>
<div id="addAgentError" class="map-status" style="color:var(--error); margin-top:6px;"></div>
</div>
<!--
Work Pipeline editor. 기본은 *접혀 있음* — CEO가 사용자 의도를 보고
자동으로 적합한 작업 흐름을 선택하므로 일반 사용자는 만질 일이 없다.
"고급: 작업 흐름 직접 편집" 토글을 열어야만 보이는 영역으로 옮김.
기능과 데이터는 그대로 유지 (롤백 안전 · 고급 사용자용).
-->
<details class="map-section pipeline-advanced">
<summary class="pipeline-advanced-summary">
<span class="pa-icon">🔧</span>
<span class="pa-title">고급: 작업 흐름 직접 편집</span>
<span class="pa-hint">평소엔 대표가 자동 분배 — 직접 정의하고 싶을 때만 펼치세요.</span>
</summary>
<div class="map-section-head" style="margin-top:8px;">
<div>
<div class="map-section-title">작업 흐름</div>
<div class="map-section-hint">대표에게 맡기거나, 사용자가 정한 순서대로 팀원이 이어서 작업하게 만듭니다.</div>
</div>
<div class="map-btn-group">
<select id="pipelineTemplateSel" title="템플릿에서 새 작업 흐름 만들기"
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="빈 작업 흐름부터 시작">+ 직접 만들기</button>
</div>
</div>
<div class="control-row" style="margin-top:8px; gap:8px; align-items:center;">
<label style="font-size:10px; color:var(--text-dim);">현재 흐름:</label>
<select id="activePipelineSel" style="flex:1; padding:4px 8px; background:var(--bg); color:var(--text-primary); border:1px solid var(--border); border-radius:6px; font-size:11px;">
<option value="">대표가 알아서 분배 (권장)</option>
</select>
</div>
<ul id="companyPipelineList" class="map-list" style="margin-top:8px;"></ul>
</details>
<!-- Pipeline editor — 카드형 단계 에디터.
각 단계는 역할 그룹 → 담당 cascading + 지시 텍스트 + 재시도 옵션을
가진 카드 한 장으로 표현. JSON 직접 편집은 더 이상 불필요. -->
<div id="pipelineEditForm" class="map-section company-agent-add-form" data-open="false">
<div class="map-section-title" style="margin-bottom:8px;">작업 흐름 편집</div>
<div class="company-agent-add-grid">
<label class="field-label">내부 ID (고급 · 한 번 정하면 변경 불가)
<input type="text" id="pipelineEditId" placeholder="product-dev-v1" />
</label>
<label class="field-label">이름
<input type="text" id="pipelineEditName" placeholder="제품 개발 v1" />
</label>
</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);">작업 단계</div>
<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;">
<button id="cancelPipelineEditBtn">취소</button>
<button class="primary" id="savePipelineEditBtn">저장</button>
</div>
<div id="pipelineEditError" class="map-status" style="color:var(--error); margin-top:6px;"></div>
</div>
<div class="map-footer">
<button class="secondary-btn" id="openCompanySessionsBtn" title="이번 회사가 만든 세션 폴더 열기">세션 폴더 열기</button>
<div style="flex:1"></div>
<button class="send-btn" id="closeCompanyOverlayBtn2">닫기</button>
</div>
<div id="companyStatus" class="map-status"></div>
</div>
<div id="historyOverlay" class="history-overlay">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h2 style="color:var(--text-bright);">Chat History</h2>
<button class="icon-btn" id="closeHistoryBtn"></button>
</div>
<div id="historyList" style="flex:1; overflow-y:auto;"></div>
</div>
<div id="agentMapOverlay" class="history-overlay">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px;">
<div>
<h2 style="color:var(--text-bright); margin:0;">Agent Mapping</h2>
<p style="margin:4px 0 0; font-size:11px; color:var(--text-dim);">
선택한 에이전트에 <strong>지식 폴더</strong><strong>외부 스킬 폴더/파일</strong>을 연결합니다.
</p>
</div>
<button class="icon-btn" id="closeAgentMapBtn"></button>
</div>
<div class="map-agent-row">
<div class="field-label">Agent</div>
<div id="agentMapAgentName" class="map-agent-name">(선택된 에이전트가 없습니다)</div>
</div>
<div class="map-section">
<div class="map-section-head">
<div>
<div class="map-section-title">🤖 Model for this agent</div>
<div class="map-section-hint">이 에이전트를 선택했을 때 사용할 모델을 지정합니다. <strong>Use current model</strong>을 선택하면 상단에서 고른 기본 모델을 그대로 사용합니다.</div>
</div>
</div>
<div class="select-wrap" style="margin-top:8px;">
<select id="agentMapModelSel" title="Model override for this agent"></select>
</div>
</div>
<div class="map-section">
<div class="map-section-head">
<div>
<div class="map-section-title">🎚 Knowledge Mix for this agent</div>
<div class="map-section-hint">에이전트별 의존도. <strong>Use global setting</strong>을 켜두면 상단 슬라이더 값을 그대로 따릅니다.</div>
</div>
</div>
<label class="map-checkbox-row" style="margin-top:8px;">
<input type="checkbox" id="agentMapMixUseGlobal" checked>
<span>Use global setting</span>
</label>
<div class="knowledge-mix-control map-mix-control" style="margin-top:6px;">
<span class="km-end-label">Model</span>
<input type="range" id="agentMapMixSlider" min="0" max="100" step="5" value="50" class="km-slider" disabled>
<span class="km-end-label">Brain</span>
</div>
<div class="map-section-hint" id="agentMapMixHint" style="margin-top:4px;">Model 50% · Brain 50%</div>
</div>
<div class="map-section">
<div class="map-section-head">
<div>
<div class="map-section-title">📚 Knowledge Folders</div>
<div class="map-section-hint">Brain 안에서 검색(RAG)에 사용할 폴더입니다. 폴더가 Brain 외부면 자동으로 제외됩니다.</div>
</div>
<button class="secondary-btn" id="addKnowledgeFolderBtn">+ 폴더 추가</button>
</div>
<ul id="knowledgeFolderList" class="map-list"></ul>
</div>
<div class="map-section">
<div class="map-section-head">
<div>
<div class="map-section-title">🛠 External Skills</div>
<div class="map-section-hint">.md 스킬 파일이 들어 있는 폴더 또는 개별 .md 파일을 연결하세요. 채팅 시 항상 함께 로드됩니다.</div>
</div>
<div class="map-btn-group">
<button class="secondary-btn" id="addSkillFolderBtn">+ 폴더 추가</button>
<button class="secondary-btn" id="addSkillFileBtn">+ 파일 추가</button>
</div>
</div>
<ul id="skillFolderList" class="map-list"></ul>
</div>
<div class="map-footer">
<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>
</div>
<div id="agentMapStatus" class="map-status"></div>
</div>
<div class="thinking-bar" id="thinkingBar"></div>
<div id="stepper" class="stepper-container">
<div class="steps">
<div class="step" id="step-analyze"><div class="step-dot"></div><div class="step-label">Analyze</div></div>
<div class="step" id="step-plan"><div class="step-dot"></div><div class="step-label">Plan</div></div>
<div class="step" id="step-execute"><div class="step-dot"></div><div class="step-label">Execute</div></div>
<div class="step" id="step-verify"><div class="step-dot"></div><div class="step-label">Verify</div></div>
</div>
</div>
<!--
Pixel Office — Agent Work Pipeline 상태 시각화 패널.
백엔드의 SidebarChatProvider.pixelOfficeOn*() 콜이 push하는 `pixelOfficeUpdate`
메시지를 받아 그대로 그린다. dispatcher / agent 로직은 절대 건드리지 않는
read-only 시각화 레이어. data-enabled 속성으로 보이기/숨기기 토글.
-->
<div id="pixelOffice" class="pixel-office" data-enabled="false" data-collapsed="false">
<div class="po-head">
<div class="po-head-left">
<span class="po-title">🏢 Pixel Office</span>
<span class="po-status-label" id="poStatusLabel">idle</span>
</div>
<div class="po-head-actions">
<button class="po-expand" id="poExpandBtn" title="전체 사무실 뷰 열기"></button>
<button class="po-collapse" id="poCollapseBtn" title="접기/펼치기"></button>
</div>
</div>
<div class="po-body" id="poBody">
<div class="po-scene">
<!-- 캐릭터 영역: 단일 캐릭터 + 머리 위 말풍선 슬롯. -->
<div class="po-char-wrap">
<div class="po-bubbles" id="poBubbles"></div>
<div class="po-char" id="poChar">
<div class="po-char-emoji" id="poCharEmoji">🧑‍💼</div>
<div class="po-char-prop" id="poCharProp"></div>
</div>
<div class="po-desk"></div>
</div>
<!-- 진행률 막대. -->
<div class="po-progress" id="poProgress">
<div class="po-progress-bar" id="poProgressBar" style="width:0%"></div>
</div>
</div>
<div class="po-panel" id="poPanel">
<div class="po-row"><span class="po-key">Agent</span><span class="po-val" id="poAgentName"></span></div>
<div class="po-row"><span class="po-key">Status</span><span class="po-val po-val-status" id="poStatusVal">idle</span></div>
<div class="po-row"><span class="po-key">Task</span><span class="po-val" id="poTask"></span></div>
<div class="po-row"><span class="po-key">Step</span><span class="po-val" id="poStep"></span></div>
<div class="po-row" id="poNextStepRow" style="display:none;"><span class="po-key">Next</span><span class="po-val" id="poNextStep"></span></div>
<div class="po-row" id="poMessageRow" style="display:none;"><span class="po-key">Note</span><span class="po-val" id="poMessage"></span></div>
<div class="po-section" id="poNeedInputSection" style="display:none;">
<div class="po-section-head">Need User Input</div>
<ol class="po-need-input" id="poNeedInputList"></ol>
</div>
<div class="po-section" id="poApprovalSection" style="display:none;">
<div class="po-section-head">Awaiting Approval</div>
<div class="po-approval" id="poApprovalText"></div>
</div>
<div class="po-section" id="poContractSection" style="display:none;">
<div class="po-section-head">Requirement Contract</div>
<div class="po-contract" id="poContract"></div>
</div>
<div class="po-section" id="poLogsSection" style="display:none;">
<div class="po-section-head">Recent Logs</div>
<div class="po-logs" id="poLogs"></div>
</div>
</div>
</div>
</div>
<div class="chat" id="chat">
<!-- Dynamic welcome panel — JS의 _renderWelcome()이 두뇌/모델 상태에 맞춰
시작 체크리스트 또는 예시 질문을 채워 넣음. 첫 메시지가 가면 사라짐. -->
<div class="welcome" id="welcomePanel"></div>
</div>
<div class="input-wrap">
<!--
Project Architecture chip. Hidden by default; the JS handler flips
`data-active` when the extension host sends an `architectureStatus`
message with active=true. Click "Open" / "Refresh" / "Detach" to
route back to the chatHandlers cases.
-->
<!--
Three-state chip:
data-state="hidden" → completely collapsed
data-state="active" → full info + Open / Refresh / Detach
data-state="inactive" → project name + Attach (or Re-attach) only
JS switches the state attribute on every `architectureStatus`
event so the user always has a one-click path back into project
mode after a Detach.
-->
<div id="archChip" class="arch-chip" data-state="hidden">
<span class="arch-chip-icon">📋</span>
<div class="arch-chip-info">
<div class="arch-chip-title" id="archChipTitle"></div>
<div class="arch-chip-meta" id="archChipMeta"></div>
</div>
<div class="arch-chip-actions" id="archChipActions">
<!-- active state buttons -->
<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="이 프로젝트 컨텍스트를 대화에 자동 첨부">첨부</button>
</div>
</div>
<div id="agentConfigPanel" class="panel">
<div class="field-label">에이전트 페르소나 / 지시사항</div>
<textarea id="agentPrompt" rows="5" placeholder="이 에이전트가 어떤 역할로, 어떤 톤으로 답해야 하는지 적으세요..."></textarea>
<div class="field-label">금지 사항 (반드시 지킬 규칙)</div>
<textarea id="negativePrompt" rows="2" placeholder="이 에이전트가 절대 하지 말아야 할 것..."></textarea>
<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="Astra에게 무엇이든 물어보세요..."></textarea>
<div class="input-footer">
<div class="footer-left">
<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="입력 비우기" 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>
</div>
</div>
<input type="file" id="fileInput" multiple hidden accept="image/*,.txt,.md,.pdf,.csv,.json,.js,.ts,.py,.java,.rs,.go">
</div>
<script src="__SCRIPT_URI__"></script>
</body>
</html>