CxWebApp/static/js/panes/slack.js
CxAI Agent d057e09fa2
Some checks are pending
build-and-push / image (push) Waiting to run
feat: add new panes for demand, diffusion, inbox, items, lang, mac, slack, system, tools, and websocket
- Implemented demand pane for managing trend-driven design jobs.
- Created diffusion pane for generating images via Stable Diffusion.
- Added inbox pane for sweeping and routing artifacts through the CxAI inbox classifier.
- Developed items pane for CRUD operations against /api/items.
- Introduced lang pane for running language pipelines.
- Established mac pane for macOS app distribution information.
- Integrated slack pane for sending messages and displaying diagnostics.
- Built system pane for process introspection and version information.
- Launched tools pane for browsing and invoking MCP tools.
- Set up websocket pane for connecting to the /ws/echo service.
2026-05-16 19:23:30 -05:00

80 lines
3.4 KiB
JavaScript

// panes/slack.js — Slack sidecar.
import { registerPane } from '../app.js';
import { jget, jpost, escapeHtml } from '../lib/api.js';
import { fmtJSON, err } from '../lib/ui.js';
const TPL = `
<div class="pane-head">
<div><div class="title">Slack</div><div class="sub">Sidecar health and ad-hoc responses.</div></div>
<div class="grow"></div>
<button class="btn btn-secondary" id="slack-refresh">Refresh</button>
</div>
<div class="grid cols-2">
<div class="card">
<div class="card-title"><h2>Respond</h2></div>
<form id="slack-form" class="grid gap-3">
<label class="field"><span class="lbl">Message</span><textarea class="input" id="slack-text" rows="3" required></textarea></label>
<div class="grid cols-2 gap-3">
<label class="field"><span class="lbl">Channel</span><input class="input" id="slack-channel" placeholder="rest"/></label>
<label class="field"><span class="lbl">Thread ts</span><input class="input" id="slack-thread" placeholder="rest"/></label>
</div>
<button class="btn btn-primary" type="submit">Generate reply</button>
</form>
<h3 class="mt-3 mb-2">Reply</h3>
<pre id="slack-reply" class="muted">—</pre>
</div>
<div class="card">
<div class="card-title"><h2>Diagnostics</h2></div>
<h3 class="mb-2">healthz</h3><pre id="slack-health">…</pre>
<h3 class="mt-3 mb-2">info</h3><pre id="slack-info">…</pre>
<h3 class="mt-3 mb-2">memory</h3><pre id="slack-memory">…</pre>
<h3 class="mt-3 mb-2">tools</h3><div id="slack-tools">…</div>
</div>
</div>
`;
async function refresh(host) {
try {
const [h, info, mem, tools] = await Promise.all([
jget('/api/slack/healthz').catch(e => ({ error: e.message })),
jget('/api/slack/info').catch(e => ({ error: e.message })),
jget('/api/slack/memory').catch(e => ({ error: e.message })),
jget('/api/slack/tools').catch(e => ({ error: e.message })),
]);
host.querySelector('#slack-health').textContent = fmtJSON(h);
host.querySelector('#slack-info').textContent = fmtJSON(info);
host.querySelector('#slack-memory').textContent = fmtJSON(mem);
host.querySelector('#slack-tools').innerHTML = (tools.tools || []).map(t =>
`<div class="row"><div class="grow"><div class="ttl mono">${escapeHtml(t.name)}</div><div class="desc">${escapeHtml(t.description||'')}</div></div></div>`
).join('') || '<div class="muted" style="padding:12px">(none)</div>';
} catch (e) {
host.querySelector('#slack-health').textContent = e.message;
}
}
registerPane('slack', {
label: 'Slack',
init(host) {
host.innerHTML = TPL;
host.querySelector('#slack-refresh').addEventListener('click', () => refresh(host));
host.querySelector('#slack-form').addEventListener('submit', async (e) => {
e.preventDefault();
const body = {
text: host.querySelector('#slack-text').value,
channel: host.querySelector('#slack-channel').value || 'rest',
thread_ts: host.querySelector('#slack-thread').value || 'rest',
};
host.querySelector('#slack-reply').textContent = 'thinking…';
try {
const r = await jpost('/api/slack/respond', body);
host.querySelector('#slack-reply').textContent = r.reply || fmtJSON(r);
} catch (e) {
host.querySelector('#slack-reply').textContent = e.body?.detail || e.message;
err(e.message);
}
});
refresh(host);
},
refresh,
});