chore: version up to 2.80.36 and package with UI/UX refinement
This commit is contained in:
@@ -755,6 +755,7 @@
|
||||
.ready-bar .rb-seg.ok { color: var(--success); }
|
||||
.ready-bar .rb-seg.bad { color: var(--error); }
|
||||
.ready-bar .rb-seg.rb-dim, .ready-bar .rb-dim { color: var(--border-bright); }
|
||||
.ready-bar .rb-seg.rb-warn { color: var(--warning); font-weight: 600; }
|
||||
.ready-bar .rb-sep { color: var(--border); margin: 0 1px; }
|
||||
.ready-bar .rb-link { color: var(--accent); cursor: pointer; }
|
||||
.ready-bar .rb-link:hover { text-decoration: underline; }
|
||||
@@ -826,3 +827,113 @@
|
||||
}
|
||||
.lesson-candidate-box .lc-rec { border-color: var(--warning); color: var(--warning); }
|
||||
.lesson-candidate-box button:hover { border-color: var(--border-bright); }
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
Compact header: top-bar dropdowns + Context Bar + Records line
|
||||
(collapse the old "select bomb" into role-grouped popovers)
|
||||
════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* compact toggle chips kept visible in the top bar (Trace / Web) */
|
||||
.toggle-chip { font-size: 10.5px; padding: 0 8px; }
|
||||
|
||||
/* a trigger + popover menu (Tools ▾ / Edit ▾ / Records ▾) */
|
||||
.hdr-dropdown { position: relative; display: inline-flex; }
|
||||
.hdr-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 5px);
|
||||
right: 0;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 190px;
|
||||
max-width: calc(100vw - 16px);
|
||||
padding: 5px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border-bright);
|
||||
border-radius: 9px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.35);
|
||||
z-index: 300;
|
||||
}
|
||||
.hdr-menu.open { display: flex; }
|
||||
.hdr-menu-wide { width: min(330px, calc(100vw - 16px)); }
|
||||
.hdr-menu-label {
|
||||
font-size: 9.5px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .04em;
|
||||
color: var(--text-dim);
|
||||
padding: 5px 8px 2px;
|
||||
}
|
||||
.hdr-menu-label:first-child { padding-top: 2px; }
|
||||
.hdr-menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 6px 9px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hdr-menu-item:hover { background: var(--control-bg-hover); color: var(--text-bright); }
|
||||
.hdr-menu .toggle-item::after { content: ' · 꺼짐'; color: var(--text-dim); font-size: 10px; }
|
||||
.hdr-menu .toggle-item.active { color: var(--accent); }
|
||||
.hdr-menu .toggle-item.active::after { content: ' · 켜짐'; color: var(--accent); }
|
||||
.hdr-menu .control-row { margin: 0 4px 2px; }
|
||||
.hdr-menu .select-wrap { margin: 0 4px 2px; }
|
||||
|
||||
/* Context Bar — "what knowledge context this answer uses" */
|
||||
.context-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 5px 12px;
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 11px;
|
||||
min-width: 0;
|
||||
}
|
||||
.context-summary {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
white-space: nowrap;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.context-summary::-webkit-scrollbar { display: none; }
|
||||
.context-summary .cb-key { color: var(--text-dim); }
|
||||
.context-summary .cb-val { color: var(--text-bright); font-weight: 600; }
|
||||
.context-summary .cb-sep { color: var(--border); }
|
||||
.context-edit-dd { flex-shrink: 0; }
|
||||
|
||||
/* Records line — auto-records status + collapsed record picker */
|
||||
.records-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 10.5px;
|
||||
color: var(--text-dim);
|
||||
min-width: 0;
|
||||
}
|
||||
.records-line .rl-summary {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.records-line .rl-latest { color: var(--border-bright); overflow: hidden; text-overflow: ellipsis; }
|
||||
.records-line .hdr-dropdown { flex-shrink: 0; }
|
||||
|
||||
+81
-52
@@ -13,70 +13,99 @@
|
||||
<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" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">Wiki</button>
|
||||
<button class="icon-btn active" id="brainTraceBtn" data-tooltip="Second Brain Trace Mode">Trace</button>
|
||||
<button class="icon-btn" id="brainTraceDebugBtn" data-tooltip="Second Brain Debug JSON">Dbg</button>
|
||||
<button class="icon-btn" id="internetBtn" data-tooltip="Internet Access">Web</button>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="View History">Log</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>
|
||||
<div class="hdr-dropdown" data-dd>
|
||||
<button class="icon-btn" id="toolsMenuBtn" data-dd-trigger data-tooltip="Tools">Tools ▾</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>
|
||||
</div>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="View History">History</button>
|
||||
<button class="icon-btn" id="settingsBtn" data-tooltip="Settings">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
<div class="select-stack">
|
||||
<div class="select-line">
|
||||
<div class="status-pill"><span id="statusDot" class="status-dot"></span><span id="engineStatusText">Engine</span></div>
|
||||
<div class="select-wrap"><select id="modelSel" title="Select Model"></select></div>
|
||||
</div>
|
||||
<div class="paired-row">
|
||||
<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>
|
||||
<button class="icon-btn" id="brainBtn" data-tooltip="Sync Knowledge">Sync</button>
|
||||
</div>
|
||||
</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>
|
||||
</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>
|
||||
</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">Brain</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-sep">·</span>
|
||||
<span class="cb-seg"><span class="cb-key">Project</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>
|
||||
<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">Brain</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>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Agent</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>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Agent ↔ Knowledge</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>
|
||||
</div>
|
||||
|
||||
<div class="hdr-menu-label">Project (Chronicle)</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>
|
||||
<button class="icon-btn" id="openDesignerBtn" data-tooltip="Open Record Folder">Open</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-row record-row">
|
||||
<div class="status-pill" id="chronicleAutoStatus" title="Project records are saved automatically after meaningful project turns.">
|
||||
<span class="status-dot ready"></span><span>Auto Records</span>
|
||||
</div>
|
||||
<div class="select-wrap"><select id="chronicleRecordSel" title="Select Chronicle Record"></select></div>
|
||||
<div class="tool-group" aria-label="Chronicle record actions">
|
||||
<button class="icon-btn" id="refreshChronicleRecordsBtn" data-tooltip="Refresh Records">Ref</button>
|
||||
<button class="icon-btn" id="openChronicleRecordBtn" data-tooltip="Open Selected Record">Open</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="readyBar" class="ready-bar" title="현재 준비 상태 — 엔진 / 모델 / Brain / Agent 범위 / 메모리 / 컨텍스트 창">
|
||||
<span class="rb-dot" id="rbDot"></span>
|
||||
<span class="rb-content" id="rbContent">준비 상태 확인 중…</span>
|
||||
|
||||
<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 class="rl-latest" id="recordsLatest"></span>
|
||||
</div>
|
||||
<div class="hdr-dropdown" data-dd>
|
||||
<button class="icon-btn" id="recordsMenuBtn" data-dd-trigger data-tooltip="Chronicle records">Records ▾</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="historyOverlay" class="history-overlay">
|
||||
@@ -161,10 +190,10 @@
|
||||
<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">Negative Prompt (Strict Rules)</div>
|
||||
<textarea id="negativePrompt" rows="2" placeholder="What NOT to do..."></textarea>
|
||||
|
||||
|
||||
<button id="updateAgentBtn" class="secondary-btn">Update Agent Skill</button>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
|
||||
+68
-27
@@ -173,62 +173,66 @@
|
||||
const rbDot = document.getElementById('rbDot');
|
||||
const rbContent = document.getElementById('rbContent');
|
||||
const ctxBadge = document.getElementById('ctxBadge');
|
||||
const ctxBrainName = document.getElementById('ctxBrainName');
|
||||
const ctxAgentName = document.getElementById('ctxAgentName');
|
||||
const ctxProjectName = document.getElementById('ctxProjectName');
|
||||
const recordsLatest = document.getElementById('recordsLatest');
|
||||
|
||||
// ── Ready-status bar ─────────────────────────────────────────────────
|
||||
let readyState = {};
|
||||
function escAttr(t) { return String(t == null ? '' : t).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); }
|
||||
function fmtK(n) {
|
||||
if (typeof n !== 'number' || !isFinite(n)) return '?';
|
||||
if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\.0$/, '') + 'k';
|
||||
return String(n);
|
||||
}
|
||||
function shortModel(m) { m = String(m || ''); const i = m.lastIndexOf('/'); return i >= 0 ? m.slice(i + 1) : m; }
|
||||
function selText(sel) { try { return sel && sel.selectedIndex >= 0 ? (sel.options[sel.selectedIndex].text || '').trim() : ''; } catch { return ''; } }
|
||||
function truncMid(s, n) { s = String(s || ''); if (s.length <= n) return s; const h = Math.max(4, Math.floor((n - 1) / 2)); return s.slice(0, h) + '…' + s.slice(-h); }
|
||||
|
||||
// ── Context Bar (Brain / Agent / Project summary) + Records line ──────
|
||||
function syncContextBar() {
|
||||
if (ctxBrainName) ctxBrainName.textContent = selText(brainSel) || '—';
|
||||
if (ctxAgentName) { const t = selText(agentSel); ctxAgentName.textContent = (!t || /no agent/i.test(t)) ? '기본' : t; }
|
||||
if (ctxProjectName) ctxProjectName.textContent = selText(designerSel) || '—';
|
||||
}
|
||||
function syncRecordsLine() {
|
||||
if (!recordsLatest) return;
|
||||
const opt = chronicleRecordSel && chronicleRecordSel.value ? selText(chronicleRecordSel) : '';
|
||||
recordsLatest.textContent = opt ? '· ' + truncMid(opt, 38) : '';
|
||||
}
|
||||
|
||||
// ── Ready-status bar (Engine / Model / Brain count / Context / Memory) ──
|
||||
let readyState = {};
|
||||
function renderReadyBar() {
|
||||
if (!readyBar || !rbContent) return;
|
||||
const s = readyState;
|
||||
const segs = [];
|
||||
// Engine
|
||||
if (s.engine) {
|
||||
const on = s.engine.online;
|
||||
const tag = on === true ? '온라인' : on === false ? '오프라인' : '확인 중';
|
||||
segs.push(`<span class="rb-seg ${on === false ? 'bad' : on === true ? 'ok' : ''}">${s.engine.label || 'Engine'}: ${tag}</span>`);
|
||||
const tag = on === true ? 'Online' : on === false ? 'Offline' : '확인 중';
|
||||
segs.push(`<span class="rb-seg ${on === false ? 'bad' : on === true ? 'ok' : ''}">${escAttr(tag)}</span>`);
|
||||
}
|
||||
// Model
|
||||
if (s.model && s.model.name) {
|
||||
const loaded = s.model.loaded;
|
||||
const dot = loaded === true ? '● ' : loaded === false ? '○ ' : '';
|
||||
segs.push(`<span class="rb-seg" title="${loaded === true ? '메모리에 로드됨' : loaded === false ? '아직 로드되지 않음' : ''}">${dot}${escAttr(s.model.name)}</span>`);
|
||||
segs.push(`<span class="rb-seg" title="${escAttr(s.model.name)}${loaded === true ? ' — 메모리에 로드됨' : loaded === false ? ' — 아직 로드 안 됨' : ''}">${escAttr(shortModel(s.model.name))}</span>`);
|
||||
}
|
||||
// Brain
|
||||
if (s.brain) {
|
||||
segs.push(`<span class="rb-seg">Brain ${typeof s.brain.files === 'number' ? s.brain.files : '?'}<span class="rb-dim"> ${escAttr(s.brain.name || '')}</span></span>`);
|
||||
if (s.brain && typeof s.brain.files === 'number') {
|
||||
segs.push(`<span class="rb-seg" title="${escAttr('Brain: ' + (s.brain.name || ''))}">Brain ${s.brain.files}</span>`);
|
||||
}
|
||||
// Agent + scope
|
||||
if (s.agent && s.agent.name) {
|
||||
const scope = s.agent.scopeFolders > 0
|
||||
? ` <span class="rb-link" data-act="map">(범위 ${s.agent.scopeFolders})</span>`
|
||||
: ` <span class="rb-dim">(범위 미설정)</span>`;
|
||||
segs.push(`<span class="rb-seg">Agent: ${escAttr(s.agent.name)}${scope}</span>`);
|
||||
} else {
|
||||
segs.push(`<span class="rb-seg rb-dim">Agent 없음</span>`);
|
||||
}
|
||||
// Memory
|
||||
segs.push(`<span class="rb-seg ${s.memory ? '' : 'rb-dim'}">메모리 ${s.memory ? '켜짐' : '꺼짐'}</span>`);
|
||||
// Multi-agent (only when on)
|
||||
if (s.multiAgent) segs.push(`<span class="rb-seg">멀티에이전트</span>`);
|
||||
// Context window (capped for small models gets a ↓ marker)
|
||||
if (typeof s.contextLength === 'number') {
|
||||
if (s.cappedForSmallModel) {
|
||||
segs.push(`<span class="rb-seg" title="작은 모델(≤4B) 감지 — 예산을 ${fmtK(s.contextLength)} tokens 로 축소 (설정 g1nation.contextLength = ${fmtK(s.nominalContextLength)}). g1nation.smallModelContextCap 로 조절.">ctx ${fmtK(s.contextLength)}<span class="rb-dim"> ↓작은모델</span></span>`);
|
||||
segs.push(`<span class="rb-seg rb-warn" title="${escAttr('작은 모델(≤4B) 감지 — 예산을 ' + fmtK(s.contextLength) + ' tokens 로 축소 (설정 g1nation.contextLength = ' + fmtK(s.nominalContextLength) + '). g1nation.smallModelContextCap 으로 조절.')}">ctx ${fmtK(s.contextLength)} <span class="rb-dim">· 소형모델 제한</span></span>`);
|
||||
} else {
|
||||
segs.push(`<span class="rb-seg" title="모델 context window (g1nation.contextLength). 실제 로드된 값과 맞춰주세요.">ctx ${fmtK(s.contextLength)}</span>`);
|
||||
}
|
||||
}
|
||||
segs.push(`<span class="rb-seg ${s.memory ? '' : 'rb-dim'}">메모리 ${s.memory ? 'On' : 'Off'}</span>`);
|
||||
if (s.multiAgent) segs.push(`<span class="rb-seg">멀티에이전트</span>`);
|
||||
rbContent.innerHTML = segs.join('<span class="rb-sep">·</span>');
|
||||
if (rbDot) {
|
||||
const on = s.engine && s.engine.online;
|
||||
rbDot.className = 'rb-dot ' + (on === true ? 'ok' : on === false ? 'bad' : 'warn');
|
||||
}
|
||||
}
|
||||
function escAttr(t) { return String(t == null ? '' : t).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); }
|
||||
|
||||
// ── Context-budget badge (직전 요청 기준) ────────────────────────────
|
||||
function renderCtxBadge(b) {
|
||||
@@ -587,6 +591,7 @@
|
||||
const addOpt = document.createElement('option');
|
||||
addOpt.value = 'new'; addOpt.innerText = '+ Add New Brain...';
|
||||
brainSel.appendChild(addOpt);
|
||||
syncContextBar();
|
||||
break;
|
||||
case 'sessionList':
|
||||
historyList.innerHTML = '';
|
||||
@@ -653,6 +658,7 @@
|
||||
vscode.postMessage({ type: 'getAgentContent', path: msg.selected });
|
||||
}
|
||||
vscode.postMessage({ type: 'getKnowledgeScope', agentPath: msg.selected });
|
||||
syncContextBar();
|
||||
break;
|
||||
case 'agentMapData':
|
||||
if (msg.value) {
|
||||
@@ -728,6 +734,7 @@
|
||||
newDesignerOpt.value = 'new';
|
||||
newDesignerOpt.innerText = '+ Add Designer Project...';
|
||||
designerSel.appendChild(newDesignerOpt);
|
||||
syncContextBar();
|
||||
vscode.postMessage({ type: 'getChronicleRecords' });
|
||||
break;
|
||||
case 'chronicleRecords':
|
||||
@@ -737,6 +744,7 @@
|
||||
emptyRecordOpt.value = '';
|
||||
emptyRecordOpt.innerText = 'No records yet';
|
||||
chronicleRecordSel.appendChild(emptyRecordOpt);
|
||||
syncRecordsLine();
|
||||
break;
|
||||
}
|
||||
msg.value.forEach(record => {
|
||||
@@ -746,6 +754,7 @@
|
||||
o.title = record.path;
|
||||
chronicleRecordSel.appendChild(o);
|
||||
});
|
||||
syncRecordsLine();
|
||||
break;
|
||||
case 'agentContent':
|
||||
agentPrompt.value = msg.value;
|
||||
@@ -1072,6 +1081,38 @@
|
||||
vscode.postMessage({ type: 'openChronicleRecord', path: chronicleRecordSel.value });
|
||||
};
|
||||
|
||||
// ── Header dropdowns (Tools ▾ / Edit ▾ / Records ▾) ──────────────────
|
||||
function closeAllDropdowns(except) {
|
||||
document.querySelectorAll('.hdr-menu.open').forEach(m => { if (m !== except) m.classList.remove('open'); });
|
||||
}
|
||||
document.querySelectorAll('[data-dd]').forEach(dd => {
|
||||
const trigger = dd.querySelector('[data-dd-trigger]');
|
||||
const menu = dd.querySelector('[data-dd-menu]');
|
||||
if (!trigger || !menu) return;
|
||||
trigger.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
const willOpen = !menu.classList.contains('open');
|
||||
closeAllDropdowns(menu);
|
||||
menu.classList.toggle('open', willOpen);
|
||||
});
|
||||
// Clicks inside the menu shouldn't bubble to the document (which would close it). A click
|
||||
// on a <button> means "I picked something" → close after its own handler runs; a <select>
|
||||
// (or label/spacer) keeps the menu open so the user can change several things.
|
||||
menu.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
if (e.target && e.target.closest && e.target.closest('button')) {
|
||||
setTimeout(() => menu.classList.remove('open'), 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
document.addEventListener('click', () => closeAllDropdowns());
|
||||
|
||||
// Keep the Context Bar / Records line in sync with the (now-collapsed) selectors.
|
||||
[brainSel, agentSel, designerSel].forEach(s => s && s.addEventListener('change', syncContextBar));
|
||||
if (chronicleRecordSel) chronicleRecordSel.addEventListener('change', syncRecordsLine);
|
||||
syncContextBar();
|
||||
syncRecordsLine();
|
||||
|
||||
vscode.postMessage({ type: 'getModels' });
|
||||
vscode.postMessage({ type: 'getAgents' });
|
||||
vscode.postMessage({ type: 'getChronicleProjects' });
|
||||
|
||||
Reference in New Issue
Block a user