Skip to content

Commit 201f1d8

Browse files
committed
feat(ui): fix thread creation from UI modal (UI-14)
- Auto-register a browser-session agent (sessionStorage) to obtain X-Agent-Token required by POST /api/threads - Pass creator_agent_id + X-Agent-Token header when creating thread - Fix shared-api.js headers merge (spread was overwriting Content-Type) - Disable Create button when topic is empty; re-enable on input - Add .btn-primary:disabled CSS style
1 parent 03c1251 commit 201f1d8

6 files changed

Lines changed: 80 additions & 4 deletions

File tree

src/static/css/main.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,14 @@ body[data-theme="light"] .skills-badge {
21142114
background: #2563eb;
21152115
}
21162116

2117+
.btn-primary:disabled {
2118+
opacity: 0.4;
2119+
cursor: not-allowed;
2120+
}
2121+
.btn-primary:disabled:hover {
2122+
background: var(--accent);
2123+
}
2124+
21172125
/* Thread context menu */
21182126
#thread-context-menu {
21192127
position: fixed;

src/static/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
<acb-modal-shell></acb-modal-shell>
155155

156156
<script src="/static/js/shared-api.js?v=2"></script>
157+
<script src="/static/js/shared-ui-agent.js?v=1"></script>
157158
<script src="/static/js/shared-theme.js?v=2"></script>
158159
<script src="/static/js/shared-utils.js?v=3"></script>
159160
<script src="/static/js/shared-message-renderer.js?v=2"></script>

src/static/js/components/acb-modal-shell.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
</div>
4848
<div class="modal-actions">
4949
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
50-
<button class="btn-primary" onclick="submitModal()">Create</button>
50+
<button id="btn-create-thread" class="btn-primary" onclick="submitModal()" disabled>Create</button>
5151
</div>
5252
</div>
5353
</div>
@@ -101,6 +101,22 @@
101101

102102
// Attach minimap toggle listener after DOM is built
103103
this._attachMinimapToggle();
104+
// UI-14: enable Create button only when topic is non-empty
105+
this._attachTopicGuard();
106+
}
107+
108+
_attachTopicGuard() {
109+
const input = this.querySelector("#modal-topic");
110+
const btn = this.querySelector("#btn-create-thread");
111+
if (!input || !btn) return;
112+
const sync = () => { btn.disabled = input.value.trim().length === 0; };
113+
input.addEventListener("input", sync);
114+
// Re-sync when modal is opened (topic may have been cleared)
115+
const overlay = this.querySelector("#modal-overlay");
116+
if (overlay) {
117+
const observer = new MutationObserver(() => sync());
118+
observer.observe(overlay, { attributes: true, attributeFilter: ["style"] });
119+
}
104120
}
105121

106122
_attachMinimapToggle() {

src/static/js/shared-api.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
(function () {
22
async function api(path, opts) {
33
const options = opts || {};
4+
const { headers: extraHeaders, ...restOptions } = options;
45
try {
56
const response = await fetch(path, {
6-
headers: { "Content-Type": "application/json" },
7-
...options,
7+
headers: { "Content-Type": "application/json", ...extraHeaders },
8+
...restOptions,
89
});
910
if (!response.ok) {
1011
console.warn(`[API] ${options.method || 'GET'} ${path} → HTTP ${response.status}`);

src/static/js/shared-modals.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@
9999
const templateSel = document.getElementById("modal-template");
100100
const template = templateSel ? templateSel.value || null : null;
101101

102+
// UI-14: get or register a browser-session agent to provide auth for thread creation
103+
const uiAgent = window.AcbUiAgent ? await window.AcbUiAgent.ensureUiAgent() : null;
104+
if (!uiAgent) {
105+
console.error("[Thread Create] Could not obtain UI agent token — cannot create thread");
106+
return;
107+
}
108+
102109
topicInput.value = "";
103110
if (templateSel) templateSel.value = "";
104111
const descEl = document.getElementById("modal-template-desc");
@@ -107,7 +114,11 @@
107114

108115
const t = await api("/api/threads", {
109116
method: "POST",
110-
body: JSON.stringify({ topic, ...(template ? { template } : {}) }),
117+
headers: {
118+
"Content-Type": "application/json",
119+
"X-Agent-Token": uiAgent.token,
120+
},
121+
body: JSON.stringify({ topic, creator_agent_id: uiAgent.agent_id, ...(template ? { template } : {}) }),
111122
});
112123
if (t) {
113124
const syncContext =

src/static/js/shared-ui-agent.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// UI-14: Auto-register a browser-session agent so the UI can create threads.
2+
// Token is stored in sessionStorage (cleared on tab close).
3+
(function () {
4+
const SESSION_KEY = "acb-ui-agent";
5+
6+
async function ensureUiAgent() {
7+
const cached = sessionStorage.getItem(SESSION_KEY);
8+
if (cached) {
9+
try {
10+
const parsed = JSON.parse(cached);
11+
if (parsed.agent_id && parsed.token) return parsed;
12+
} catch (_) {
13+
// corrupted — fall through to re-register
14+
}
15+
}
16+
17+
try {
18+
const res = await fetch("/api/agents/register", {
19+
method: "POST",
20+
headers: { "Content-Type": "application/json" },
21+
body: JSON.stringify({
22+
name: "ui-human",
23+
display_name: "Browser User",
24+
ide: "browser",
25+
model: "human",
26+
}),
27+
});
28+
if (!res.ok) return null;
29+
const data = await res.json();
30+
if (!data.agent_id || !data.token) return null;
31+
sessionStorage.setItem(SESSION_KEY, JSON.stringify({ agent_id: data.agent_id, token: data.token }));
32+
return { agent_id: data.agent_id, token: data.token };
33+
} catch (_) {
34+
return null;
35+
}
36+
}
37+
38+
window.AcbUiAgent = { ensureUiAgent };
39+
})();

0 commit comments

Comments
 (0)