/* Desk UI Kit — Channels subsystem: Slack-like rooms where agents talk.
Exposes window.ChannelsView; loaded before Workspace.jsx. */
(() => {
const { useState, useEffect, useRef, useCallback } = React;
const CH = window.DESK_CHANNELS;
function CIco({ name, size = 16 }) {
return ;
}
const byId = (id) => CH.members.find((m) => m.id === id) || { id, name: id, ava: '??', tone: 'c1', tag: 'agent' };
/* @mentions and `code` spans rendered inline */
function renderText(text) {
const parts = text.split(/(@[\w-]+|`[^`]+`)/g);
return parts.map((p, i) => {
if (/^@/.test(p)) return {p};
if (/^`/.test(p)) return {p.slice(1, -1)};
return p;
});
}
function nowStamp() {
const d = new Date();
return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
}
function ChannelsView() {
const [chans, setChans] = useState(() => JSON.parse(JSON.stringify(CH.channels)));
const [active, setActive] = useState('deploy');
const [draft, setDraft] = useState('');
const [typing, setTyping] = useState(null); // { who, chan }
const scrollRef = useRef(null);
const timers = useRef([]);
const activeRef = useRef('deploy');
const replyIdx = useRef({});
activeRef.current = active;
const chan = chans.find((c) => c.id === active) || chans[0];
useEffect(() => { try { window.lucide && window.lucide.createIcons(); } catch (e) {} });
useEffect(() => {
const el = scrollRef.current;
if (el) el.scrollTop = el.scrollHeight;
}, [chans, typing, active]);
const post = useCallback((chanId, who, text) => {
setChans((cs) => cs.map((c) => c.id === chanId
? { ...c, msgs: [...c.msgs, { who, t: nowStamp(), text }], unread: chanId === activeRef.current ? 0 : c.unread + 1 }
: c));
}, []);
/* ambient scripted traffic */
useEffect(() => {
(CH.drip || []).forEach((d) => {
timers.current.push(setTimeout(() => setTyping({ who: d.who, chan: d.chan }), Math.max(0, d.delay - 1000)));
timers.current.push(setTimeout(() => { setTyping(null); post(d.chan, d.who, d.text); }, d.delay));
});
return () => timers.current.forEach(clearTimeout);
}, [post]);
const send = () => {
const text = draft.trim();
if (!text) return;
setDraft('');
post(active, 'human', text);
const m = text.match(/@([\w-]+)/);
let responder = m ? CH.members.find((x) => x.id === m[1] && x.id !== 'human') : null;
if (!responder) responder = byId(CH.defaultResponder);
const pool = CH.replies[responder.id] || CH.replies[CH.defaultResponder];
replyIdx.current[responder.id] = ((replyIdx.current[responder.id] || 0) + 1) % pool.length;
const reply = pool[replyIdx.current[responder.id]];
const chanAt = active;
timers.current.push(setTimeout(() => setTyping({ who: responder.id, chan: chanAt }), 650));
timers.current.push(setTimeout(() => { setTyping(null); post(chanAt, responder.id, reply); }, 1900 + Math.random() * 900));
};
const openChan = (id) => {
setActive(id);
setChans((cs) => cs.map((x) => x.id === id ? { ...x, unread: 0 } : x));
};
return (