v2.2.15: Astra Office Refactor & Multi-Service Integration
This commit is contained in:
@@ -1931,6 +1931,32 @@
|
||||
/* compact toggle chips kept visible in the top bar (Trace / Web) */
|
||||
.toggle-chip { font-size: 10.5px; padding: 0 8px; }
|
||||
|
||||
/* 스코프 프리셋 segmented control — 기업 모드 chip 옆에 붙임.
|
||||
세 버튼이 하나의 그룹으로 보이도록 inner border-radius 제거 + 사이 1px 분리.
|
||||
hidden 속성은 HTML5 기본 inherit, JS 가 toggle. */
|
||||
.scope-seg { display: inline-flex; align-items: center; gap: 1px; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 5px; background: rgba(255, 255, 255, 0.03); padding: 0; height: 24px; }
|
||||
.scope-seg[hidden] { display: none; }
|
||||
.scope-seg-btn {
|
||||
background: transparent;
|
||||
color: var(--muted, #94a3b8);
|
||||
border: 0;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 10.5px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.02em;
|
||||
border-radius: 0;
|
||||
}
|
||||
.scope-seg-btn:hover { background: rgba(255, 255, 255, 0.05); color: var(--text, #e5e7eb); }
|
||||
.scope-seg-btn.active {
|
||||
background: rgba(99, 102, 241, 0.18);
|
||||
color: #c7d2fe;
|
||||
font-weight: 700;
|
||||
}
|
||||
.scope-seg-btn:first-child { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
|
||||
.scope-seg-btn:last-child { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
|
||||
|
||||
/* a trigger + popover menu (Tools ▾ / Edit ▾ / Records ▾) */
|
||||
.hdr-dropdown { position: relative; display: inline-flex; }
|
||||
.hdr-menu {
|
||||
|
||||
@@ -23,6 +23,21 @@
|
||||
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>
|
||||
|
||||
@@ -945,6 +945,7 @@
|
||||
case 'companyStatus': {
|
||||
const v = msg.value || {};
|
||||
renderCompanyChip(!!v.enabled, v.summary || '');
|
||||
renderScopeSeg(v.activePipelineId || null);
|
||||
break;
|
||||
}
|
||||
case 'companyIntentDecision': {
|
||||
@@ -965,11 +966,51 @@
|
||||
break;
|
||||
}
|
||||
case 'pixelOfficeUpdate': {
|
||||
// 새 path (officeSnapshot) 가 한 번이라도 도착했다면 옛 message 는 무시.
|
||||
if (window.__officeSnapshotSeen) break;
|
||||
if (typeof window.__pixelOfficeApply === 'function') {
|
||||
window.__pixelOfficeApply(msg.value || {});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'officeSnapshot': {
|
||||
// refactor #E — mini view 도 OfficeSnapshot 수신.
|
||||
// OfficeSnapshot 을 옛 {state, bubbles, config} payload 모양으로 변환 후
|
||||
// 기존 __pixelOfficeApply 재사용. dual-mode 안전 전환.
|
||||
window.__officeSnapshotSeen = true;
|
||||
const snap = msg.value;
|
||||
if (!snap || typeof window.__pixelOfficeApply !== 'function') break;
|
||||
const roster = Array.isArray(snap.roster) ? snap.roster : [];
|
||||
const active = (snap.activeAgentId && roster.find((a) => a.agentId === snap.activeAgentId)) || roster[0];
|
||||
const phaseToStatus = (p) => {
|
||||
if (p === 'awaiting-approval') return 'waiting_approval';
|
||||
if (p === 'reporting') return 'done';
|
||||
if (p === 'intake') return 'analyzing';
|
||||
return p || 'idle';
|
||||
};
|
||||
const synthetic = {
|
||||
agentId: snap.activeAgentId || (active && active.agentId) || 'main',
|
||||
agentName: (active && active.agentName) || 'Agent',
|
||||
status: (active && active.status) || phaseToStatus(snap.phase),
|
||||
currentTask: snap.task && snap.task.goal,
|
||||
currentStep: active && active.currentStep,
|
||||
message: snap.activeAgentId || '',
|
||||
recentLogs: (active && active.lastLog) ? [active.lastLog] : [],
|
||||
progress: snap.pipeline ? (snap.pipeline.index / Math.max(1, snap.pipeline.stages.length)) : 0,
|
||||
pipelineStages: snap.pipeline && snap.pipeline.stages,
|
||||
needUserInput: (snap.awaiting && snap.awaiting.kind === 'clarification') ? snap.awaiting.questions : undefined,
|
||||
awaitingApproval: (snap.awaiting && snap.awaiting.kind === 'approval') ? snap.awaiting.questions[0] : undefined,
|
||||
requirementContract: snap.task,
|
||||
updatedAt: snap.updatedAt,
|
||||
};
|
||||
window.__pixelOfficeApply({
|
||||
state: synthetic,
|
||||
bubbles: Array.isArray(snap.newBubbles) ? snap.newBubbles : [],
|
||||
// config 는 그대로 유지 — snapshot 에는 enabled 만 함의적, 옛 cfg 가 살아있음.
|
||||
config: undefined,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'companyAlignmentCard': {
|
||||
// Intent Alignment 카드. kind에 따라 4가지 모드:
|
||||
// - 'auto-proceed' : confidence high → 자동 진행 안내(읽기 전용)
|
||||
@@ -1843,8 +1884,44 @@
|
||||
? `1인 기업 ON · ${summary || ''}`.trim()
|
||||
: '1인 기업 모드 OFF — 클릭해서 켜기',
|
||||
);
|
||||
// 스코프 프리셋 segmented control 도 기업 모드 ON 일 때만 노출.
|
||||
const scopeSeg = document.getElementById('companyScopeSeg');
|
||||
if (scopeSeg) scopeSeg.hidden = !active;
|
||||
};
|
||||
|
||||
// 활성 pipeline 의 id 가 어느 SCOPE 프리셋의 suggestedPipelineId 와 매칭되는지로 active 표시.
|
||||
// companyStatus 메시지가 activePipelineId 를 보낼 때마다 호출.
|
||||
const SCOPE_PRESET_TO_PIPELINE_ID = {
|
||||
'plan-only': 'plan-only',
|
||||
'dev-only': 'dev-only',
|
||||
'full-product-dev': 'product-dev',
|
||||
};
|
||||
const renderScopeSeg = (activePipelineId) => {
|
||||
const scopeSeg = document.getElementById('companyScopeSeg');
|
||||
if (!scopeSeg) return;
|
||||
for (const btn of scopeSeg.querySelectorAll('.scope-seg-btn')) {
|
||||
const tplId = btn.getAttribute('data-scope');
|
||||
const expected = SCOPE_PRESET_TO_PIPELINE_ID[tplId];
|
||||
btn.classList.toggle('active', !!activePipelineId && activePipelineId === expected);
|
||||
}
|
||||
};
|
||||
// Wire up clicks once.
|
||||
const _scopeSeg = document.getElementById('companyScopeSeg');
|
||||
if (_scopeSeg && !_scopeSeg.dataset.wired) {
|
||||
_scopeSeg.dataset.wired = '1';
|
||||
_scopeSeg.addEventListener('click', (e) => {
|
||||
const btn = e.target && e.target.closest && e.target.closest('.scope-seg-btn');
|
||||
if (!btn) return;
|
||||
const tplId = btn.getAttribute('data-scope');
|
||||
if (!tplId) return;
|
||||
// Optimistic visual flip — backend ack 가 companyStatus 갱신으로 결과 확정.
|
||||
for (const b of _scopeSeg.querySelectorAll('.scope-seg-btn')) {
|
||||
b.classList.toggle('active', b === btn);
|
||||
}
|
||||
vscode.postMessage({ type: 'setCompanyScopePreset', templateId: tplId });
|
||||
});
|
||||
}
|
||||
|
||||
if (_companyChip) {
|
||||
_companyChip.onclick = () => {
|
||||
const isActive = _companyChip.classList.contains('active');
|
||||
|
||||
Reference in New Issue
Block a user