Raw
1 // @ts-check
2 (function () {
3 const vscode = acquireVsCodeApi();
4
5 const messagesEl = document.getElementById("messages");
6 const statusEl = document.getElementById("status");
7 const formEl = document.getElementById("composer");
8 const inputEl = /** @type {HTMLTextAreaElement} */ (document.getElementById("input"));
9 const sendEl = /** @type {HTMLButtonElement} */ (document.getElementById("send"));
10
11 // The assistant streams in chunks; keep a handle to the current bubble so we
12 // can append to it rather than creating a new one per chunk.
13 let currentAssistant = null;
14 let busy = false;
15
16 function scrollToBottom() {
17 messagesEl.scrollTop = messagesEl.scrollHeight;
18 }
19
20 function addMessage(role, text) {
21 const el = document.createElement("div");
22 el.className = "message message-" + role;
23 const body = document.createElement("div");
24 body.className = "bubble";
25 body.textContent = text;
26 el.appendChild(body);
27 messagesEl.appendChild(el);
28 scrollToBottom();
29 return body;
30 }
31
32 function appendAssistant(text) {
33 if (!currentAssistant) {
34 currentAssistant = addMessage("assistant", "");
35 }
36 currentAssistant.textContent += text;
37 scrollToBottom();
38 }
39
40 function endAssistant() {
41 currentAssistant = null;
42 }
43
44 function setStatus(text) {
45 statusEl.textContent = text;
46 }
47
48 function setBusy(value) {
49 busy = value;
50 sendEl.disabled = value;
51 sendEl.textContent = value ? "" : "Send";
52 }
53
54 function send() {
55 const text = inputEl.value.trim();
56 if (!text || busy) {
57 return;
58 }
59 endAssistant();
60 vscode.postMessage({ type: "prompt", text });
61 inputEl.value = "";
62 }
63
64 formEl.addEventListener("submit", (e) => {
65 e.preventDefault();
66 send();
67 });
68
69 inputEl.addEventListener("keydown", (e) => {
70 // Enter sends; Shift+Enter inserts a newline.
71 if (e.key === "Enter" && !e.shiftKey) {
72 e.preventDefault();
73 send();
74 }
75 });
76
77 window.addEventListener("message", (event) => {
78 const message = event.data;
79 switch (message.type) {
80 case "user":
81 endAssistant();
82 addMessage("user", message.text);
83 break;
84 case "assistant":
85 appendAssistant(message.text || "");
86 break;
87 case "thought":
88 addMessage("thought", message.text || "");
89 break;
90 case "tool": {
91 const label = message.title + (message.status ? "" + message.status : "");
92 addMessage("tool", label);
93 break;
94 }
95 case "status":
96 setStatus(message.text || "");
97 break;
98 case "error":
99 endAssistant();
100 addMessage("error", message.text || "Error");
101 break;
102 case "log":
103 // Agent stderr — surface quietly in the status line.
104 if (message.text) {
105 setStatus(String(message.text).trim().split("\n").pop() || "");
106 }
107 break;
108 case "busy":
109 setBusy(!!message.busy);
110 break;
111 case "turnEnd":
112 endAssistant();
113 setBusy(false);
114 break;
115 case "clear":
116 messagesEl.innerHTML = "";
117 endAssistant();
118 break;
119 default:
120 break;
121 }
122 });
123
124 vscode.postMessage({ type: "ready" });
125 })();