feat(webchat): add personal assistant mode controls in settings
This commit is contained in:
@@ -19,6 +19,7 @@ function escapeHtml(text) {
|
||||
|
||||
let _client = null;
|
||||
let _el = null;
|
||||
let _settingsCache = null;
|
||||
|
||||
function describePushStatus(status) {
|
||||
if (!status.supported) {
|
||||
@@ -114,6 +115,18 @@ async function loadSettings() {
|
||||
const confirmPatterns = hooks.confirm ?? [];
|
||||
const logPatterns = hooks.log ?? [];
|
||||
const silentPatterns = hooks.silent ?? [];
|
||||
const automation = config?.automation ?? {};
|
||||
const memory = config?.memory ?? {};
|
||||
const tts = config?.tts ?? {};
|
||||
_settingsCache = config ?? {};
|
||||
|
||||
const deliveryMode = automation.delivery_mode ?? 'shared_session';
|
||||
const dailyBriefingEnabled = Boolean(automation.daily_briefing?.enabled);
|
||||
const dailyMemoryEnabled = Boolean(memory.daily_log?.enabled);
|
||||
const proactiveExtractEnabled = Boolean(memory.proactive_extract?.enabled);
|
||||
const proactiveMinToolCalls = Number(memory.proactive_extract?.min_tool_calls ?? 1);
|
||||
const ttsEnabled = Boolean(tts.enabled);
|
||||
const ttsChannelText = Array.isArray(tts.enabled_channels) ? tts.enabled_channels.join(', ') : '';
|
||||
|
||||
// Build config view (redacted JSON)
|
||||
const configJson = JSON.stringify(config, null, 2);
|
||||
@@ -127,6 +140,44 @@ async function loadSettings() {
|
||||
_el.innerHTML = `
|
||||
<h1 class="page-title">Settings</h1>
|
||||
|
||||
<h2 class="section-title">Personal Assistant Mode</h2>
|
||||
<div class="settings-section">
|
||||
<div class="assistant-mode-grid">
|
||||
<label class="assistant-toggle">
|
||||
<input id="assist-delivery-announce" type="checkbox" ${deliveryMode === 'announce' ? 'checked' : ''} />
|
||||
<span>Automation announce delivery mode</span>
|
||||
</label>
|
||||
<label class="assistant-toggle">
|
||||
<input id="assist-daily-briefing" type="checkbox" ${dailyBriefingEnabled ? 'checked' : ''} />
|
||||
<span>Daily briefing enabled</span>
|
||||
</label>
|
||||
<label class="assistant-toggle">
|
||||
<input id="assist-memory-daily" type="checkbox" ${dailyMemoryEnabled ? 'checked' : ''} />
|
||||
<span>Daily memory logging</span>
|
||||
</label>
|
||||
<label class="assistant-toggle">
|
||||
<input id="assist-memory-proactive" type="checkbox" ${proactiveExtractEnabled ? 'checked' : ''} />
|
||||
<span>Proactive memory extraction</span>
|
||||
</label>
|
||||
<label class="assistant-field">
|
||||
<span>Proactive extract tool-call threshold</span>
|
||||
<input id="assist-memory-min-tools" type="number" min="0" max="50" value="${Number.isFinite(proactiveMinToolCalls) ? proactiveMinToolCalls : 1}" />
|
||||
</label>
|
||||
<label class="assistant-toggle">
|
||||
<input id="assist-tts-enabled" type="checkbox" ${ttsEnabled ? 'checked' : ''} />
|
||||
<span>TTS voice replies enabled</span>
|
||||
</label>
|
||||
<label class="assistant-field">
|
||||
<span>TTS channels (comma-separated, blank = all)</span>
|
||||
<input id="assist-tts-channels" type="text" value="${escapeHtml(ttsChannelText)}" placeholder="telegram,discord,whatsapp" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="assistant-actions">
|
||||
<button id="assistant-mode-save" class="btn btn-primary">Save Assistant Mode</button>
|
||||
<span id="assistant-mode-status" class="text-sm text-muted"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">WebChat Push Notifications</h2>
|
||||
<div class="settings-section">
|
||||
${isPushSupported() ? '' : '<div class="text-sm text-muted">This browser does not support PushManager APIs.</div>'}
|
||||
@@ -218,11 +269,73 @@ async function loadSettings() {
|
||||
|
||||
// Bind save hooks
|
||||
_el.querySelector('#hooks-save').addEventListener('click', saveHooks);
|
||||
_el.querySelector('#assistant-mode-save').addEventListener('click', saveAssistantMode);
|
||||
_el.querySelector('#push-enable').addEventListener('click', onEnablePush);
|
||||
_el.querySelector('#push-disable').addEventListener('click', onDisablePush);
|
||||
await renderPushStatus();
|
||||
}
|
||||
|
||||
async function saveAssistantMode() {
|
||||
const status = _el.querySelector('#assistant-mode-status');
|
||||
status.textContent = 'Saving...';
|
||||
status.className = 'text-sm text-muted';
|
||||
|
||||
const useAnnounce = Boolean(_el.querySelector('#assist-delivery-announce')?.checked);
|
||||
const dailyBriefing = Boolean(_el.querySelector('#assist-daily-briefing')?.checked);
|
||||
const memoryDaily = Boolean(_el.querySelector('#assist-memory-daily')?.checked);
|
||||
const memoryProactive = Boolean(_el.querySelector('#assist-memory-proactive')?.checked);
|
||||
const ttsEnabled = Boolean(_el.querySelector('#assist-tts-enabled')?.checked);
|
||||
const minToolsRaw = Number.parseInt(_el.querySelector('#assist-memory-min-tools')?.value ?? '1', 10);
|
||||
const minTools = Number.isFinite(minToolsRaw) ? Math.min(50, Math.max(0, minToolsRaw)) : 1;
|
||||
const ttsChannelsRaw = _el.querySelector('#assist-tts-channels')?.value ?? '';
|
||||
const ttsChannels = ttsChannelsRaw
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const patches = {
|
||||
'automation.delivery_mode': useAnnounce ? 'announce' : 'shared_session',
|
||||
'automation.daily_briefing.enabled': dailyBriefing,
|
||||
'memory.daily_log.enabled': memoryDaily,
|
||||
'memory.proactive_extract.enabled': memoryProactive,
|
||||
'memory.proactive_extract.min_tool_calls': minTools,
|
||||
'tts.enabled': ttsEnabled,
|
||||
'tts.enabled_channels': ttsChannels,
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await _client.call('config.patch', { patches });
|
||||
const applied = result.applied ?? [];
|
||||
const rejected = result.rejected ?? [];
|
||||
const persisted = result.persisted === true;
|
||||
const persistError = result.persistError;
|
||||
|
||||
if (rejected.length > 0) {
|
||||
status.textContent = `Partially saved. Rejected: ${rejected.join(', ')}`;
|
||||
status.className = 'text-sm text-error';
|
||||
} else if (persistError) {
|
||||
status.textContent = `Save failed: ${persistError}`;
|
||||
status.className = 'text-sm text-error';
|
||||
} else if (!persisted) {
|
||||
status.textContent = `Saved in runtime only (${applied.length} updated)`;
|
||||
status.className = 'text-sm text-muted';
|
||||
} else {
|
||||
status.textContent = `Saved (${applied.length} updated)`;
|
||||
status.className = 'text-sm text-success';
|
||||
if (_settingsCache && _settingsCache.automation) {
|
||||
_settingsCache.automation.delivery_mode = useAnnounce ? 'announce' : 'shared_session';
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
status.textContent = `Error: ${err.message}`;
|
||||
status.className = 'text-sm text-error';
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (status) {status.textContent = '';}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
async function saveHooks() {
|
||||
const status = _el.querySelector('#hooks-status');
|
||||
status.textContent = 'Saving...';
|
||||
@@ -280,5 +393,6 @@ export const SettingsPage = {
|
||||
teardown() {
|
||||
_client = null;
|
||||
_el = null;
|
||||
_settingsCache = null;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user