eb4bef0744
Astra Settings 패널에 Email 섹션 추가 — autoSync 토글, 간격/범위/최대수 설정, '지금 동기화' 버튼(슬래시와 동일 syncEmails 코어), 인덱스 상태(건수/최신일) 표시. VSCode 설정 JSON 안 건드리고 패널에서 관리. 타입체크·빌드 통과. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
528 lines
25 KiB
JavaScript
528 lines
25 KiB
JavaScript
(function () {
|
|
const vscode = acquireVsCodeApi();
|
|
const $ = (id) => document.getElementById(id);
|
|
|
|
// ---- Telegram ----
|
|
const tokenInput = $('tgToken');
|
|
const saveBtn = $('tgSaveToken');
|
|
const clearBtn = $('tgClearToken');
|
|
const testBtn = $('tgTest');
|
|
const enabledChk = $('tgEnabled');
|
|
const enrollBtn = $('tgEnroll');
|
|
const enrollCancelBtn = $('tgEnrollCancel');
|
|
const enrollStatus = $('tgEnrollStatus');
|
|
const tokenStatus = $('tgTokenStatus');
|
|
const botName = $('tgBotName');
|
|
const tgFeedback = $('tgFeedback');
|
|
const tgError = $('tgError');
|
|
const chatList = $('tgChatIds');
|
|
|
|
// ---- Connection ----
|
|
const cnUrl = $('cnUrl');
|
|
const cnModel = $('cnModel');
|
|
const cnTimeout = $('cnTimeout');
|
|
const cnRefreshModels = $('cnRefreshModels');
|
|
const cnModelHint = $('cnModelHint');
|
|
|
|
// ---- Datacollect ----
|
|
const dcBridgeTarget = $('dcBridgeTarget');
|
|
const dcBridgeUrl = $('dcBridgeUrl');
|
|
const dcBridgeNasUrl = $('dcBridgeNasUrl');
|
|
const dcBridgeNasToken = $('dcBridgeNasToken');
|
|
const dcSavePath = $('dcSavePath');
|
|
const dcCrawlDepth = $('dcCrawlDepth');
|
|
const dcMaxPages = $('dcMaxPages');
|
|
const dcSynthTemp = $('dcSynthTemp');
|
|
|
|
// ---- Email (Project Astra) ----
|
|
const emailStatus = $('emailStatus');
|
|
const emailAutoSync = $('emailAutoSync');
|
|
const emailInterval = $('emailInterval');
|
|
const emailDays = $('emailDays');
|
|
const emailMax = $('emailMax');
|
|
const emailSyncNow = $('emailSyncNow');
|
|
const emailSyncMsg = $('emailSyncMsg');
|
|
|
|
// ---- Memory ----
|
|
const memEnabled = $('memEnabled');
|
|
const memShort = $('memShort');
|
|
const memMid = $('memMid');
|
|
const memLong = $('memLong');
|
|
|
|
// ---- Brain ----
|
|
const brainName = $('brainName');
|
|
const brainPath = $('brainPath');
|
|
const brainAutoPush = $('brainAutoPush');
|
|
|
|
// ---- Advanced ----
|
|
const advDryRun = $('advDryRun');
|
|
const advMulti = $('advMulti');
|
|
const advAutoSteps = $('advAutoSteps');
|
|
const advCtxSize = $('advCtxSize');
|
|
const advChatTemp = $('advChatTemp');
|
|
const advChunkedSwitch = $('advChunkedSwitch');
|
|
const advChunkedMax = $('advChunkedMax');
|
|
const advPolishPersona = $('advPolishPersona');
|
|
|
|
// ---- Google (Calendar + Sheets) ----
|
|
const gClientId = $('gClientId');
|
|
const gClientSecret = $('gClientSecret');
|
|
const gCalendarId = $('gCalendarId');
|
|
const gDefaultDur = $('gDefaultDur');
|
|
const gIcalUrl = $('gIcalUrl');
|
|
const gIcalDays = $('gIcalDays');
|
|
const googleConnStatus = $('googleConnStatus');
|
|
const googleConnStatusInline = $('googleConnStatusInline');
|
|
const googleConnectBtn = $('googleConnect');
|
|
const googleDisconnectBtn = $('googleDisconnect');
|
|
const googleIcalRefreshBtn = $('googleIcalRefresh');
|
|
const googleIcalStatus = $('googleIcalStatus');
|
|
const googleFeedback = $('googleFeedback');
|
|
const googleError = $('googleError');
|
|
|
|
// ---- Devil Agent ----
|
|
const devilEnabled = $('devilEnabled');
|
|
|
|
// ---- Cloud LLM Providers ----
|
|
const prOpenrouterEnabled = $('prOpenrouterEnabled');
|
|
const prOpenrouterKey = $('prOpenrouterKey');
|
|
const prOpenrouterDefault = $('prOpenrouterDefault');
|
|
const prAnthropicEnabled = $('prAnthropicEnabled');
|
|
const prAnthropicKey = $('prAnthropicKey');
|
|
const prAnthropicDefault = $('prAnthropicDefault');
|
|
const prGeminiEnabled = $('prGeminiEnabled');
|
|
const prGeminiKey = $('prGeminiKey');
|
|
const prGeminiDefault = $('prGeminiDefault');
|
|
|
|
// ---- Banner ----
|
|
const bannerError = $('bannerError');
|
|
|
|
// ---- Telegram listeners ----
|
|
saveBtn.addEventListener('click', () => {
|
|
const t = (tokenInput.value || '').trim();
|
|
if (!t) return;
|
|
vscode.postMessage({ type: 'telegram.saveToken', token: t });
|
|
tokenInput.value = '';
|
|
});
|
|
clearBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.clearToken' }));
|
|
testBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.testConnection' }));
|
|
enabledChk.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'telegram.toggleEnabled', enabled: e.target.checked })
|
|
);
|
|
enrollBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.enroll' }));
|
|
enrollCancelBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.cancelEnroll' }));
|
|
chatList.addEventListener('click', (e) => {
|
|
if (!(e.target instanceof HTMLElement)) return;
|
|
if (e.target.classList.contains('remove')) {
|
|
const id = Number(e.target.dataset.id);
|
|
if (Number.isFinite(id)) vscode.postMessage({ type: 'telegram.removeChatId', chatId: id });
|
|
}
|
|
});
|
|
|
|
// ---- Connection listeners ----
|
|
document.querySelector('[data-save="connection.url"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'connection.update', ollamaUrl: cnUrl.value })
|
|
);
|
|
document.querySelector('[data-save="connection.model"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'connection.update', defaultModel: cnModel.value })
|
|
);
|
|
cnRefreshModels.addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'connection.update', refreshModels: true })
|
|
);
|
|
cnModel.addEventListener('change', () =>
|
|
vscode.postMessage({ type: 'connection.update', defaultModel: cnModel.value })
|
|
);
|
|
document.querySelector('[data-save="connection.timeout"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'connection.update', requestTimeout: Number(cnTimeout.value) })
|
|
);
|
|
|
|
// ---- Datacollect listeners ----
|
|
document.querySelector('[data-save="datacollect.bridgeTarget"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', bridgeTarget: dcBridgeTarget.value })
|
|
);
|
|
document.querySelector('[data-save="datacollect.bridgeUrl"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', bridgeUrl: dcBridgeUrl.value })
|
|
);
|
|
document.querySelector('[data-save="datacollect.bridgeNasUrl"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', bridgeNasUrl: dcBridgeNasUrl.value })
|
|
);
|
|
document.querySelector('[data-save="datacollect.bridgeNasToken"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', bridgeNasToken: dcBridgeNasToken.value })
|
|
);
|
|
document.querySelector('[data-save="datacollect.savePath"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', savePath: dcSavePath.value })
|
|
);
|
|
document.querySelector('[data-save="datacollect.crawlDepth"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', crawlDepth: Number(dcCrawlDepth.value) })
|
|
);
|
|
document.querySelector('[data-save="datacollect.maxPages"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', maxPages: Number(dcMaxPages.value) })
|
|
);
|
|
document.querySelector('[data-save="datacollect.synthesisTemperature"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'datacollect.update', synthesisTemperature: Number(dcSynthTemp.value) })
|
|
);
|
|
|
|
// ---- Email listeners ----
|
|
emailAutoSync.addEventListener('change', () =>
|
|
vscode.postMessage({ type: 'email.update', autoSync: emailAutoSync.checked })
|
|
);
|
|
document.querySelector('[data-save="email.interval"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'email.update', autoSyncIntervalMinutes: Number(emailInterval.value) })
|
|
);
|
|
document.querySelector('[data-save="email.days"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'email.update', syncDays: Number(emailDays.value) })
|
|
);
|
|
document.querySelector('[data-save="email.max"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'email.update', syncMaxMessages: Number(emailMax.value) })
|
|
);
|
|
emailSyncNow.addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'email.syncNow' })
|
|
);
|
|
|
|
// ---- Memory listeners ----
|
|
memEnabled.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'memory.update', memoryEnabled: e.target.checked })
|
|
);
|
|
document.querySelector('[data-save="memory.short"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'memory.update', memoryShortTermMessages: Number(memShort.value) })
|
|
);
|
|
document.querySelector('[data-save="memory.mid"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'memory.update', memoryMediumTermSessions: Number(memMid.value) })
|
|
);
|
|
document.querySelector('[data-save="memory.long"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'memory.update', memoryLongTermFiles: Number(memLong.value) })
|
|
);
|
|
|
|
// ---- Brain listeners ----
|
|
brainAutoPush.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'brain.update', autoPushBrain: e.target.checked })
|
|
);
|
|
$('brainOpenSettings').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'openVscodeSettings' })
|
|
);
|
|
|
|
// ---- Advanced listeners ----
|
|
advDryRun.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'advanced.update', dryRun: e.target.checked })
|
|
);
|
|
advMulti.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'advanced.update', multiAgentEnabled: e.target.checked })
|
|
);
|
|
document.querySelector('[data-save="advanced.autoSteps"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'advanced.update', maxAutoSteps: Number(advAutoSteps.value) })
|
|
);
|
|
// ---- Google listeners ----
|
|
document.querySelector('[data-save="google.clientId"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'google.update', clientId: gClientId.value })
|
|
);
|
|
document.querySelector('[data-save="google.clientSecret"]').addEventListener('click', () => {
|
|
// 저장 후 입력 필드는 비움 — 다음부터는 placeholder 가 "저장됨" 으로 표시됨.
|
|
vscode.postMessage({ type: 'google.update', clientSecret: gClientSecret.value });
|
|
gClientSecret.value = '';
|
|
});
|
|
document.querySelector('[data-save="google.calendarId"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'google.update', calendarId: gCalendarId.value })
|
|
);
|
|
document.querySelector('[data-save="google.defaultEventDurationMinutes"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'google.update', defaultEventDurationMinutes: Number(gDefaultDur.value) })
|
|
);
|
|
document.querySelector('[data-save="google.icalUrl"]').addEventListener('click', () => {
|
|
vscode.postMessage({ type: 'google.update', icalUrl: gIcalUrl.value });
|
|
gIcalUrl.value = '';
|
|
});
|
|
document.querySelector('[data-save="google.icalDaysAhead"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'google.update', icalDaysAhead: Number(gIcalDays.value) })
|
|
);
|
|
googleConnectBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.connect' }));
|
|
googleDisconnectBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.disconnect' }));
|
|
googleIcalRefreshBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.icalRefresh' }));
|
|
|
|
// ---- Devil Agent listener ----
|
|
devilEnabled.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'devilAgent.toggle', enabled: e.target.checked })
|
|
);
|
|
|
|
// ---- Cloud LLM Providers listeners ----
|
|
prOpenrouterEnabled.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', enabled: e.target.checked })
|
|
);
|
|
document.querySelector('[data-save="providers.openrouter.apiKey"]').addEventListener('click', () => {
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', apiKey: prOpenrouterKey.value });
|
|
prOpenrouterKey.value = '';
|
|
});
|
|
document.querySelector('[data-save="providers.openrouter.defaultModel"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', defaultModel: prOpenrouterDefault.value })
|
|
);
|
|
prAnthropicEnabled.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', enabled: e.target.checked })
|
|
);
|
|
document.querySelector('[data-save="providers.anthropic.apiKey"]').addEventListener('click', () => {
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', apiKey: prAnthropicKey.value });
|
|
prAnthropicKey.value = '';
|
|
});
|
|
document.querySelector('[data-save="providers.anthropic.defaultModel"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', defaultModel: prAnthropicDefault.value })
|
|
);
|
|
prGeminiEnabled.addEventListener('change', (e) =>
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'gemini', enabled: e.target.checked })
|
|
);
|
|
document.querySelector('[data-save="providers.gemini.apiKey"]').addEventListener('click', () => {
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'gemini', apiKey: prGeminiKey.value });
|
|
prGeminiKey.value = '';
|
|
});
|
|
document.querySelector('[data-save="providers.gemini.defaultModel"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'providers.update', providerId: 'gemini', defaultModel: prGeminiDefault.value })
|
|
);
|
|
|
|
document.querySelector('[data-save="advanced.ctxSize"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'advanced.update', maxContextSize: Number(advCtxSize.value) })
|
|
);
|
|
|
|
document.querySelector('[data-save="advanced.chatTemperature"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'advanced.update', chatTemperature: Number(advChatTemp.value) })
|
|
);
|
|
|
|
document.querySelector('[data-save="advanced.chunkedSwitchTokens"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'advanced.update', chunkedSwitchTokens: Number(advChunkedSwitch.value) })
|
|
);
|
|
|
|
document.querySelector('[data-save="advanced.chunkedMaxSections"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'advanced.update', chunkedMaxSections: Number(advChunkedMax.value) })
|
|
);
|
|
|
|
document.querySelector('[data-save="advanced.polishPersonaOverride"]').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'advanced.update', polishPersonaOverride: String(advPolishPersona.value || '') })
|
|
);
|
|
|
|
// ---- Header ----
|
|
$('openVscodeSettings').addEventListener('click', () =>
|
|
vscode.postMessage({ type: 'openVscodeSettings' })
|
|
);
|
|
|
|
// ---- State sync ----
|
|
window.addEventListener('message', (e) => {
|
|
const msg = e.data;
|
|
if (!msg || msg.type !== 'state') return;
|
|
renderState(msg.value);
|
|
});
|
|
|
|
/** Set input.value only when the field is not currently focused, so user edits aren't clobbered mid-typing. */
|
|
function setIfNotFocused(input, value) {
|
|
if (document.activeElement === input) return;
|
|
const next = value === undefined || value === null ? '' : String(value);
|
|
if (input.value !== next) input.value = next;
|
|
}
|
|
|
|
function renderState(state) {
|
|
// ---- Banner ----
|
|
if (state.bannerError) {
|
|
bannerError.hidden = false;
|
|
bannerError.textContent = state.bannerError;
|
|
} else {
|
|
bannerError.hidden = true;
|
|
bannerError.textContent = '';
|
|
}
|
|
|
|
// ---- Telegram ----
|
|
const tg = state.telegram;
|
|
tokenStatus.textContent = tg.hasToken ? '저장된 토큰이 있습니다.' : '아직 토큰이 등록되지 않았습니다.';
|
|
clearBtn.disabled = !tg.hasToken;
|
|
|
|
if (tg.botName) {
|
|
botName.textContent = `연결됨: ${tg.botName}` + (tg.connected ? ' · 폴링 중' : ' · 비활성화 상태');
|
|
botName.classList.toggle('ok', tg.connected);
|
|
} else {
|
|
botName.textContent = '';
|
|
botName.classList.remove('ok');
|
|
}
|
|
|
|
enabledChk.checked = !!tg.enabled;
|
|
enabledChk.disabled = !tg.hasToken;
|
|
|
|
if (tg.enrolling) {
|
|
enrollBtn.hidden = true;
|
|
enrollCancelBtn.hidden = false;
|
|
enrollStatus.textContent = '봇에게 메시지를 한 번 보내주세요. 다음 메시지의 채널이 자동 등록됩니다.';
|
|
} else {
|
|
enrollBtn.hidden = false;
|
|
enrollCancelBtn.hidden = true;
|
|
enrollStatus.textContent = '';
|
|
}
|
|
enrollBtn.disabled = !tg.hasToken;
|
|
|
|
chatList.innerHTML = '';
|
|
if (!tg.allowedChatIds || tg.allowedChatIds.length === 0) {
|
|
const li = document.createElement('li');
|
|
li.className = 'empty';
|
|
li.textContent = '등록된 채널 없음';
|
|
chatList.appendChild(li);
|
|
} else {
|
|
for (const id of tg.allowedChatIds) {
|
|
const li = document.createElement('li');
|
|
li.textContent = String(id);
|
|
const x = document.createElement('span');
|
|
x.className = 'remove';
|
|
x.dataset.id = String(id);
|
|
x.textContent = '✕';
|
|
x.title = '허용 목록에서 제거';
|
|
li.appendChild(x);
|
|
chatList.appendChild(li);
|
|
}
|
|
}
|
|
|
|
if (tg.lastSuccess) {
|
|
tgFeedback.hidden = false;
|
|
tgFeedback.textContent = tg.lastSuccess;
|
|
} else {
|
|
tgFeedback.hidden = true;
|
|
tgFeedback.textContent = '';
|
|
}
|
|
if (tg.lastError) {
|
|
tgError.hidden = false;
|
|
tgError.textContent = tg.lastError;
|
|
} else {
|
|
tgError.hidden = true;
|
|
tgError.textContent = '';
|
|
}
|
|
|
|
// ---- Connection ----
|
|
const cn = state.connection;
|
|
setIfNotFocused(cnUrl, cn.ollamaUrl);
|
|
setIfNotFocused(cnTimeout, cn.requestTimeout);
|
|
|
|
// Model dropdown — preserve selection, allow current value even if not in list
|
|
const wantedModel = cn.defaultModel || '';
|
|
const list = Array.isArray(cn.availableModels) ? cn.availableModels.slice() : [];
|
|
if (wantedModel && !list.includes(wantedModel)) list.unshift(wantedModel);
|
|
// Only repaint when list actually changed (otherwise we lose the user's
|
|
// open dropdown state).
|
|
const currentOptions = Array.from(cnModel.options).map((o) => o.value);
|
|
const listChanged = currentOptions.length !== list.length
|
|
|| currentOptions.some((v, i) => v !== list[i]);
|
|
if (listChanged) {
|
|
cnModel.innerHTML = '';
|
|
for (const m of list) {
|
|
const opt = document.createElement('option');
|
|
opt.value = m;
|
|
opt.textContent = m;
|
|
cnModel.appendChild(opt);
|
|
}
|
|
if (list.length === 0) {
|
|
const opt = document.createElement('option');
|
|
opt.value = '';
|
|
opt.textContent = '(모델 없음 — 엔진 연결 확인)';
|
|
opt.disabled = true;
|
|
cnModel.appendChild(opt);
|
|
}
|
|
}
|
|
cnModel.value = wantedModel;
|
|
cnModelHint.textContent = cn.modelsLoading
|
|
? '모델 목록 가져오는 중…'
|
|
: `사이드바에서 선택한 모델이 여기에도 동기화됩니다. (${list.length}개 발견)`;
|
|
|
|
// ---- Datacollect ----
|
|
const dc = state.datacollect;
|
|
if (dc) {
|
|
if (dcBridgeTarget && document.activeElement !== dcBridgeTarget && (dc.bridgeTarget === 'local' || dc.bridgeTarget === 'nas')) {
|
|
dcBridgeTarget.value = dc.bridgeTarget;
|
|
}
|
|
setIfNotFocused(dcBridgeUrl, dc.bridgeUrl);
|
|
setIfNotFocused(dcBridgeNasUrl, dc.bridgeNasUrl);
|
|
setIfNotFocused(dcBridgeNasToken, dc.bridgeNasToken);
|
|
setIfNotFocused(dcSavePath, dc.savePath);
|
|
setIfNotFocused(dcCrawlDepth, dc.crawlDepth);
|
|
setIfNotFocused(dcMaxPages, dc.maxPages);
|
|
setIfNotFocused(dcSynthTemp, dc.synthesisTemperature);
|
|
}
|
|
|
|
// ---- Email (Project Astra) ----
|
|
const em = state.email;
|
|
if (em) {
|
|
if (document.activeElement !== emailAutoSync) emailAutoSync.checked = !!em.autoSync;
|
|
setIfNotFocused(emailInterval, em.autoSyncIntervalMinutes);
|
|
setIfNotFocused(emailDays, em.syncDays);
|
|
setIfNotFocused(emailMax, em.syncMaxMessages);
|
|
emailStatus.textContent = em.indexedCount > 0
|
|
? `${em.indexedCount}건 저장됨${em.newestDate ? ` · 최신 ${em.newestDate}` : ''}`
|
|
: '수집된 메일 없음 — "지금 동기화" 또는 /email-sync 실행';
|
|
emailSyncMsg.textContent = em.lastSyncMessage || '';
|
|
emailSyncNow.disabled = !!em.syncing;
|
|
emailSyncNow.textContent = em.syncing ? '동기화 중…' : '지금 동기화';
|
|
}
|
|
|
|
// ---- Memory ----
|
|
const mem = state.memory;
|
|
memEnabled.checked = !!mem.memoryEnabled;
|
|
setIfNotFocused(memShort, mem.memoryShortTermMessages);
|
|
setIfNotFocused(memMid, mem.memoryMediumTermSessions);
|
|
setIfNotFocused(memLong, mem.memoryLongTermFiles);
|
|
|
|
// ---- Brain ----
|
|
const br = state.brain;
|
|
brainName.textContent = br.activeBrainName + (br.profileCount > 0 ? ` (전체 ${br.profileCount}개)` : '');
|
|
brainPath.textContent = br.activeBrainPath || '경로 없음';
|
|
brainAutoPush.checked = !!br.autoPushBrain;
|
|
|
|
// ---- Advanced ----
|
|
const adv = state.advanced;
|
|
advDryRun.checked = !!adv.dryRun;
|
|
advMulti.checked = !!adv.multiAgentEnabled;
|
|
setIfNotFocused(advAutoSteps, adv.maxAutoSteps);
|
|
setIfNotFocused(advCtxSize, adv.maxContextSize);
|
|
setIfNotFocused(advChatTemp, adv.chatTemperature);
|
|
setIfNotFocused(advChunkedSwitch, adv.chunkedSwitchTokens);
|
|
setIfNotFocused(advChunkedMax, adv.chunkedMaxSections);
|
|
setIfNotFocused(advPolishPersona, adv.polishPersonaOverride);
|
|
|
|
// ---- Google (Calendar + Sheets) ----
|
|
const g = state.google;
|
|
if (g) {
|
|
setIfNotFocused(gClientId, g.clientId);
|
|
// Secret 은 값 자체를 화면에 안 그림 — 설정 여부만 placeholder 로 표현.
|
|
gClientSecret.placeholder = g.hasClientSecret ? '••• 저장됨 (덮어쓰려면 새 값 입력)' : 'GOCSPX-...';
|
|
setIfNotFocused(gCalendarId, g.calendarId);
|
|
setIfNotFocused(gDefaultDur, g.defaultEventDurationMinutes);
|
|
gIcalUrl.placeholder = g.hasIcalUrl ? '••• 저장됨 (덮어쓰려면 새 URL 입력)' : 'https://calendar.google.com/calendar/ical/.../basic.ics';
|
|
setIfNotFocused(gIcalDays, g.icalDaysAhead);
|
|
// 연결 상태 readout.
|
|
if (g.connected) {
|
|
const at = g.connectedAt ? g.connectedAt.slice(0, 16).replace('T', ' ') : '';
|
|
googleConnStatus.textContent = `✅ 연결됨${g.connectedAs ? ' · ' + g.connectedAs : ''}${at ? ' (' + at + ')' : ''}`;
|
|
googleConnStatus.style.color = '#10b981';
|
|
googleDisconnectBtn.disabled = false;
|
|
} else {
|
|
googleConnStatus.textContent = '⛔ OAuth 연결 안됨 — Client ID/Secret 저장 후 [OAuth 연결] 클릭';
|
|
googleConnStatus.style.color = '';
|
|
googleDisconnectBtn.disabled = true;
|
|
}
|
|
googleIcalStatus.textContent = g.lastIcalFetchAt
|
|
? `마지막 새로고침: ${g.lastIcalFetchAt.slice(0, 16).replace('T', ' ')}`
|
|
: '';
|
|
}
|
|
|
|
// ---- Devil Agent ----
|
|
if (state.devilAgent) {
|
|
devilEnabled.checked = !!state.devilAgent.enabled;
|
|
}
|
|
|
|
// ---- Cloud LLM Providers ----
|
|
const pr = state.providers;
|
|
if (pr) {
|
|
// OpenRouter
|
|
prOpenrouterEnabled.checked = !!pr.openrouter.enabled;
|
|
prOpenrouterKey.placeholder = pr.openrouter.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'sk-or-...';
|
|
setIfNotFocused(prOpenrouterDefault, pr.openrouter.defaultModel);
|
|
// Anthropic
|
|
prAnthropicEnabled.checked = !!pr.anthropic.enabled;
|
|
prAnthropicKey.placeholder = pr.anthropic.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'sk-ant-...';
|
|
setIfNotFocused(prAnthropicDefault, pr.anthropic.defaultModel);
|
|
// Gemini
|
|
prGeminiEnabled.checked = !!pr.gemini.enabled;
|
|
prGeminiKey.placeholder = pr.gemini.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'AIzaSy...';
|
|
setIfNotFocused(prGeminiDefault, pr.gemini.defaultModel);
|
|
}
|
|
}
|
|
|
|
vscode.postMessage({ type: 'ready' });
|
|
})();
|