Skip to content

Commit 252e602

Browse files
committed
feat: Refactor README and implement custom elements for modals, compose shell, and agent status in the web console UI
1 parent 3742d1d commit 252e602

7 files changed

Lines changed: 166 additions & 70 deletions

File tree

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
# Image
1+
# AgentChatBus
22
![bus_big](doc/bus_big.png)
33

4-
# AgentChatBus 🚌
5-
64
**AgentChatBus** is a persistent AI communication bus that lets multiple independent AI Agents chat, collaborate, and delegate tasks — across terminals, across IDEs, and across frameworks.
75

86
It exposes a **fully standards-compliant MCP (Model Context Protocol) server** over HTTP + SSE, and is designed to be forward-compatible with the **A2A (Agent-to-Agent)** protocol, making it a true multi-agent collaboration hub.

src/static/index.html

Lines changed: 13 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,11 @@
412412
#messages::-webkit-scrollbar { width: 16px; }
413413
#messages::-webkit-scrollbar-track { background: transparent; }
414414
#messages::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 4px; }
415+
#messages > acb-empty-state {
416+
display: flex;
417+
flex: 1;
418+
min-height: 100%;
419+
}
415420

416421
/* ── Message rows (author-based coloring) ── */
417422
.msg-row {
@@ -850,92 +855,39 @@
850855

851856
<!-- Main -->
852857
<div id="main">
853-
<div id="thread-header" style="display:none">
854-
<h2 id="thread-title"></h2>
855-
<div id="online-presence" title="">
856-
<span id="online-count">1</span>
857-
</div>
858-
</div>
858+
<acb-thread-header></acb-thread-header>
859859

860860
<div id="messages">
861861
<acb-empty-state></acb-empty-state>
862862
</div>
863863

864-
<div id="compose">
865-
<input id="compose-author" type="text" value="human" placeholder="author" />
866-
<textarea id="compose-input" rows="1" placeholder="Send a system message… (Enter to send, Shift+Enter for newline)"
867-
oninput="autoResize(this)" onkeydown="handleKey(event)"></textarea>
868-
<button id="btn-send" onclick="sendMessage()" title="Send"></button>
869-
</div>
864+
<acb-compose-shell></acb-compose-shell>
870865
<!-- Agent Status Bar -->
871-
<div id="agent-status-bar">
872-
<div id="agent-status-list"></div>
873-
<div id="agent-status-info">ℹ️</div>
874-
</div>
866+
<acb-agent-status-shell></acb-agent-status-shell>
875867
</div>
876868
</div>
877869

878870
<acb-thread-context-menu></acb-thread-context-menu>
879871

880-
<!-- ── New thread modal ─────────────────────── -->
881-
<div id="modal-overlay" onclick="closeModal(event)">
882-
<div id="modal">
883-
<h3>✦ Create New Thread</h3>
884-
<input id="modal-topic" type="text" placeholder="Thread topic…" onkeydown="if(event.key==='Enter') submitModal()" />
885-
<div class="modal-actions">
886-
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
887-
<button class="btn-primary" onclick="submitModal()">Create</button>
888-
</div>
889-
</div>
890-
</div>
891-
892-
<!-- ── Settings modal ───────────────────────── -->
893-
<div id="settings-modal-overlay" onclick="closeSettingsModal(event)"
894-
style="display:none; position: fixed; inset: 0; background: rgba(0,0,0,.7); align-items: center; justify-content: center; z-index: 100; animation: fade-in .15s ease;">
895-
<div id="settings-modal"
896-
style="background: var(--bg-card); border: 1px solid var(--border-light); border-radius: 14px; padding: 28px; width: 440px; max-width: 90vw; box-shadow: var(--shadow); animation: modal-in .2s ease;"
897-
onclick="event.stopPropagation()">
898-
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: 18px; color: var(--text-1)">⚙️ MCP Server Settings</h3>
899-
900-
<label style="display: block; font-size: 13px; color: var(--text-2); margin-bottom: 6px;">Host</label>
901-
<input id="setting-host" type="text"
902-
style="width: 100%; background: var(--bg-input); border: 1px solid var(--border-light); color: var(--text-1); border-radius: 10px; padding: 10px 14px; font-size: 14px; font-family: inherit; margin-bottom: 16px;" />
903-
904-
<label style="display: block; font-size: 13px; color: var(--text-2); margin-bottom: 6px;">Port</label>
905-
<input id="setting-port" type="number"
906-
style="width: 100%; background: var(--bg-input); border: 1px solid var(--border-light); color: var(--text-1); border-radius: 10px; padding: 10px 14px; font-size: 14px; font-family: inherit; margin-bottom: 16px;" />
907-
908-
<label style="display: block; font-size: 13px; color: var(--text-2); margin-bottom: 6px;">Agent Heartbeat Timeout
909-
(s)</label>
910-
<input id="setting-heartbeat" type="number"
911-
style="width: 100%; background: var(--bg-input); border: 1px solid var(--border-light); color: var(--text-1); border-radius: 10px; padding: 10px 14px; font-size: 14px; font-family: inherit; margin-bottom: 16px;" />
912-
913-
<label style="display: block; font-size: 13px; color: var(--text-2); margin-bottom: 6px;">Wait Timeout (s)</label>
914-
<input id="setting-wait" type="number"
915-
style="width: 100%; background: var(--bg-input); border: 1px solid var(--border-light); color: var(--text-1); border-radius: 10px; padding: 10px 14px; font-size: 14px; font-family: inherit; margin-bottom: 16px;" />
916-
917-
<div id="settings-message" style="font-size: 12px; color: var(--green); margin-bottom: 16px; display: none;"></div>
918-
919-
<div class="modal-actions">
920-
<button class="btn-secondary" onclick="closeSettingsModal()">Cancel</button>
921-
<button class="btn-primary" onclick="submitSettings()">Save (Requires Restart)</button>
922-
</div>
923-
</div>
924-
</div>
872+
<acb-modal-shell></acb-modal-shell>
925873

926874
<script src="/static/js/shared-api.js"></script>
927875
<script src="/static/js/shared-theme.js"></script>
928876
<script src="/static/js/shared-utils.js"></script>
929877
<script src="/static/js/shared-message-renderer.js"></script>
930878
<script src="/static/js/shared-sse.js"></script>
931879
<script src="/static/js/shared-modals.js"></script>
880+
<script src="/static/js/components/acb-modal-shell.js"></script>
932881
<script src="/static/js/components/acb-empty-state.js"></script>
933882
<script src="/static/js/components/acb-icon-button.js"></script>
934883
<script src="/static/js/components/acb-filter-actions.js"></script>
935884
<script src="/static/js/components/acb-filter-row.js"></script>
936885
<script src="/static/js/components/acb-thread-filter-shell.js"></script>
937886
<script src="/static/js/components/acb-thread-context-menu.js"></script>
887+
<script src="/static/js/components/acb-thread-header.js"></script>
938888
<script src="/static/js/components/acb-thread-item.js"></script>
889+
<script src="/static/js/components/acb-compose-shell.js"></script>
890+
<script src="/static/js/components/acb-agent-status-shell.js"></script>
939891
<script src="/static/js/shared-threads.js"></script>
940892
<script src="/static/js/shared-agent-status.js"></script>
941893
<script src="/static/js/components/acb-agent-status-item.js"></script>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(function registerAcbAgentStatusShell() {
2+
class AcbAgentStatusShell extends HTMLElement {
3+
connectedCallback() {
4+
if (this.childElementCount > 0) return;
5+
6+
this.innerHTML = `
7+
<div id="agent-status-bar">
8+
<div id="agent-status-list"></div>
9+
<div id="agent-status-info">ℹ️</div>
10+
</div>`;
11+
}
12+
}
13+
14+
if (!customElements.get('acb-agent-status-shell')) {
15+
customElements.define('acb-agent-status-shell', AcbAgentStatusShell);
16+
}
17+
})();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(function registerAcbComposeShell() {
2+
class AcbComposeShell extends HTMLElement {
3+
connectedCallback() {
4+
if (this.childElementCount > 0) return;
5+
6+
this.innerHTML = `
7+
<div id="compose">
8+
<input id="compose-author" type="text" value="human" placeholder="author" />
9+
<textarea id="compose-input" rows="1" placeholder="Send a system message... (Enter to send, Shift+Enter for newline)"
10+
oninput="autoResize(this)" onkeydown="handleKey(event)"></textarea>
11+
<button id="btn-send" onclick="sendMessage()" title="Send">➤</button>
12+
</div>`;
13+
}
14+
}
15+
16+
if (!customElements.get('acb-compose-shell')) {
17+
customElements.define('acb-compose-shell', AcbComposeShell);
18+
}
19+
})();
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
(function registerAcbModalShell() {
2+
const SETTINGS_FIELDS = [
3+
{ label: "Host", id: "setting-host", type: "text" },
4+
{ label: "Port", id: "setting-port", type: "number" },
5+
{ label: "Agent Heartbeat Timeout (s)", id: "setting-heartbeat", type: "number" },
6+
{ label: "Wait Timeout (s)", id: "setting-wait", type: "number" },
7+
];
8+
9+
function renderSettingsFields() {
10+
return SETTINGS_FIELDS.map((field) => {
11+
return `
12+
<label style="display:block;font-size:13px;color:var(--text-2);margin-bottom:6px;">${field.label}</label>
13+
<input id="${field.id}" type="${field.type}"
14+
style="width:100%;background:var(--bg-input);border:1px solid var(--border-light);color:var(--text-1);border-radius:10px;padding:10px 14px;font-size:14px;font-family:inherit;margin-bottom:16px;" />`;
15+
}).join("\n");
16+
}
17+
18+
class AcbModalShell extends HTMLElement {
19+
connectedCallback() {
20+
if (this.childElementCount > 0) return;
21+
22+
this.innerHTML = `
23+
<div id="modal-overlay" onclick="closeModal(event)">
24+
<div id="modal" onclick="event.stopPropagation()">
25+
<h3>✦ Create New Thread</h3>
26+
<input id="modal-topic" type="text" placeholder="Thread topic..." onkeydown="if(event.key==='Enter') submitModal()" />
27+
<div class="modal-actions">
28+
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
29+
<button class="btn-primary" onclick="submitModal()">Create</button>
30+
</div>
31+
</div>
32+
</div>
33+
34+
<div id="settings-modal-overlay" onclick="closeSettingsModal(event)"
35+
style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.7);align-items:center;justify-content:center;z-index:100;animation:fade-in .15s ease;">
36+
<div id="settings-modal"
37+
style="background:var(--bg-card);border:1px solid var(--border-light);border-radius:14px;padding:28px;width:440px;max-width:90vw;box-shadow:var(--shadow);animation:modal-in .2s ease;"
38+
onclick="event.stopPropagation()">
39+
<h3 style="font-size:16px;font-weight:600;margin-bottom:18px;color:var(--text-1)">⚙️ MCP Server Settings</h3>
40+
${renderSettingsFields()}
41+
<div id="settings-message" style="font-size:12px;color:var(--green);margin-bottom:16px;display:none;"></div>
42+
<div class="modal-actions">
43+
<button class="btn-secondary" onclick="closeSettingsModal()">Cancel</button>
44+
<button class="btn-primary" onclick="submitSettings()">Save (Requires Restart)</button>
45+
</div>
46+
</div>
47+
</div>`;
48+
}
49+
}
50+
51+
if (!customElements.get("acb-modal-shell")) {
52+
customElements.define("acb-modal-shell", AcbModalShell);
53+
}
54+
})();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(function registerAcbThreadHeader() {
2+
class AcbThreadHeader extends HTMLElement {
3+
connectedCallback() {
4+
if (this.childElementCount > 0) return;
5+
6+
this.innerHTML = `
7+
<div id="thread-header" style="display:none">
8+
<h2 id="thread-title"></h2>
9+
<div id="online-presence" title="">
10+
<span id="online-count">1</span>
11+
</div>
12+
</div>`;
13+
}
14+
}
15+
16+
if (!customElements.get('acb-thread-header')) {
17+
customElements.define('acb-thread-header', AcbThreadHeader);
18+
}
19+
})();

src/static/js/shared-modals.js

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
(function () {
2+
const MODAL_CONFIGS = {
3+
thread: {
4+
overlayId: "modal-overlay",
5+
visibility: "class",
6+
},
7+
settings: {
8+
overlayId: "settings-modal-overlay",
9+
visibility: "style",
10+
styleVisibleValue: "flex",
11+
},
12+
};
13+
14+
function getOverlay(configKey) {
15+
const cfg = MODAL_CONFIGS[configKey];
16+
if (!cfg) return null;
17+
return document.getElementById(cfg.overlayId);
18+
}
19+
20+
function setModalVisible(configKey, visible) {
21+
const cfg = MODAL_CONFIGS[configKey];
22+
const overlay = getOverlay(configKey);
23+
if (!cfg || !overlay) return;
24+
25+
if (cfg.visibility === "class") {
26+
overlay.classList.toggle("visible", visible);
27+
return;
28+
}
29+
30+
const styleValue = cfg.styleVisibleValue || "block";
31+
overlay.style.display = visible ? styleValue : "none";
32+
}
33+
34+
function isOverlayClick(event, configKey) {
35+
const overlay = getOverlay(configKey);
36+
return !!overlay && !!event && event.target === overlay;
37+
}
38+
239
function openThreadModal() {
3-
document.getElementById("modal-overlay").classList.add("visible");
40+
setModalVisible("thread", true);
441
setTimeout(() => document.getElementById("modal-topic").focus(), 100);
542
}
643

744
function closeThreadModal(e) {
8-
if (!e || e.target === document.getElementById("modal-overlay")) {
9-
document.getElementById("modal-overlay").classList.remove("visible");
45+
if (!e || isOverlayClick(e, "thread")) {
46+
setModalVisible("thread", false);
1047
}
1148
}
1249

@@ -30,7 +67,7 @@
3067

3168
async function openSettingsModal(api) {
3269
document.getElementById("settings-message").style.display = "none";
33-
document.getElementById("settings-modal-overlay").style.display = "flex";
70+
setModalVisible("settings", true);
3471
try {
3572
const res = await api("/api/settings");
3673
if (res) {
@@ -45,8 +82,8 @@
4582
}
4683

4784
function closeSettingsModal(e) {
48-
if (e && e.target !== document.getElementById("settings-modal-overlay")) return;
49-
document.getElementById("settings-modal-overlay").style.display = "none";
85+
if (e && !isOverlayClick(e, "settings")) return;
86+
setModalVisible("settings", false);
5087
}
5188

5289
async function submitSettings(api) {

0 commit comments

Comments
 (0)