@symerian/symi 3.5.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +4 -4
- package/dist/bundled/session-memory/handler.js +4 -4
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/{chrome-C_I81hbq.js → chrome-B7-rO4i9.js} +4 -4
- package/dist/{chrome-BKUACyeO.js → chrome-DPjznJQ-.js} +4 -4
- package/dist/control-ui/css/revert-red-theme.md +141 -0
- package/dist/control-ui/css/style.css +5843 -0
- package/dist/control-ui/css/style.css.backup-2026-03-03-162525 +3546 -0
- package/dist/control-ui/css/style.css.backup-before-red-2026-03-03-162525 +3546 -0
- package/dist/control-ui/css/style.css.backup-before-red-theme-2026-03-03-162530 +3546 -0
- package/dist/control-ui/css/style.css.pre-2row +2165 -0
- package/dist/control-ui/css/style.css.pre-brand +1776 -0
- package/dist/control-ui/css/style.css.pre-history +1974 -0
- package/dist/control-ui/css/style.css.pre-nav +2264 -0
- package/dist/control-ui/css/style.css.pre-newsession +1898 -0
- package/dist/control-ui/css/style.css.pre-queue +2195 -0
- package/dist/control-ui/css/style.css.pre-red-prompt +2524 -0
- package/dist/control-ui/css/style.css.pre-stop +2239 -0
- package/dist/control-ui/css/style.css.pre-textarea +2184 -0
- package/dist/control-ui/css/style.css.pre-watchdog +1848 -0
- package/dist/control-ui/css/style.css.red-theme +2999 -0
- package/dist/control-ui/index.html +1049 -0
- package/dist/control-ui/js/app.js +1304 -0
- package/dist/control-ui/js/app.js.pre-2row +463 -0
- package/dist/control-ui/js/app.js.pre-heartbeat-filter +595 -0
- package/dist/control-ui/js/app.js.pre-newsession +408 -0
- package/dist/control-ui/js/app.js.pre-queue +476 -0
- package/dist/control-ui/js/app.js.pre-stop +564 -0
- package/dist/control-ui/js/app.js.pre-textarea +467 -0
- package/dist/control-ui/js/app.js.pre-watchdog +293 -0
- package/dist/control-ui/js/connections.js +438 -0
- package/dist/control-ui/js/gateway.js +233 -0
- package/dist/control-ui/js/gateway.js.pre-stop +110 -0
- package/dist/control-ui/js/history.js +732 -0
- package/dist/control-ui/js/logs.js +238 -0
- package/dist/control-ui/js/menu.js +232 -0
- package/dist/control-ui/js/menu.js.pre-nav +66 -0
- package/dist/control-ui/js/metrics.js +53 -0
- package/dist/control-ui/js/models.js +138 -0
- package/dist/control-ui/js/render.js +882 -0
- package/dist/control-ui/js/render.test.js +112 -0
- package/dist/control-ui/js/scheduling.js +461 -0
- package/dist/control-ui/js/settings.js +910 -0
- package/dist/control-ui/js/slash-autocomplete.js +168 -0
- package/dist/control-ui/js/subagents.js +560 -0
- package/dist/control-ui/js/utils.js +29 -0
- package/dist/control-ui/vendor/highlight.min.js +2518 -0
- package/dist/control-ui/vendor/marked.min.js +69 -0
- package/dist/{deliver-DyO3QD8O.js → deliver-DTRkeYm3.js} +4 -4
- package/dist/{deliver-Cjyb6h4g.js → deliver-oWGJwzFf.js} +4 -4
- package/dist/extensionAPI.js +4 -4
- package/dist/llm-slug-generator.js +4 -4
- package/dist/{manager-rvtFoeFT.js → manager-CFenq_aO.js} +1 -1
- package/dist/{manager-PTSjHNVq.js → manager-CsxTf96V.js} +1 -1
- package/dist/{pi-embedded-BPuUM-gD.js → pi-embedded-Cdub5Vs9.js} +10 -10
- package/dist/{pw-ai-BFS9ezWe.js → pw-ai-BOOB8qoi.js} +1 -1
- package/dist/{pw-ai-Cx-Ko_FL.js → pw-ai-D2pEVS5n.js} +1 -1
- package/dist/{synthesis-7UL3pCpj.js → synthesis-Be9nYyDd.js} +4 -4
- package/dist/{synthesis-fD8J2vag.js → synthesis-CBIT6Vnk.js} +4 -4
- package/dist/{unified-runner-BIiKFnNF.js → unified-runner-BVvvnjXW.js} +10 -10
- package/package.json +1 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// ── Symi UI — Live Gateway App ────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
const responseArea = document.getElementById('response-area');
|
|
4
|
+
const promptInput = document.getElementById('prompt-input');
|
|
5
|
+
const sendBtn = document.getElementById('send-btn');
|
|
6
|
+
|
|
7
|
+
let isStreaming = false;
|
|
8
|
+
let currentRunId = null;
|
|
9
|
+
let streamBubble = null;
|
|
10
|
+
let streamContent = null;
|
|
11
|
+
let streamStart = 0;
|
|
12
|
+
|
|
13
|
+
// ── Utility ───────────────────────────────────────────────────────
|
|
14
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
15
|
+
function escapeHtml(t) {
|
|
16
|
+
return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ── Status indicator (top-right of response area) ─────────────────
|
|
20
|
+
const statusEl = document.createElement('div');
|
|
21
|
+
statusEl.className = 'conn-status';
|
|
22
|
+
statusEl.innerHTML = '<span class="conn-dot dot-yellow pulse"></span><span class="conn-label">connecting…</span>';
|
|
23
|
+
responseArea.before(statusEl);
|
|
24
|
+
|
|
25
|
+
function setStatus(state) { // 'connecting' | 'online' | 'offline'
|
|
26
|
+
const dot = statusEl.querySelector('.conn-dot');
|
|
27
|
+
const label = statusEl.querySelector('.conn-label');
|
|
28
|
+
dot.className = `conn-dot ${state === 'online' ? 'dot-green pulse' : state === 'offline' ? 'dot-red' : 'dot-yellow pulse'}`;
|
|
29
|
+
label.textContent = state === 'online' ? 'live' : state === 'offline' ? 'disconnected' : 'connecting…';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Render helpers ─────────────────────────────────────────────────
|
|
33
|
+
function addUserMessage(text) {
|
|
34
|
+
const msg = document.createElement('div');
|
|
35
|
+
msg.className = 'message user-msg';
|
|
36
|
+
msg.innerHTML = `<div class="bubble">${escapeHtml(text)}</div><div class="message-clearfix"></div>`;
|
|
37
|
+
responseArea.appendChild(msg);
|
|
38
|
+
msg.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Thinking bubble ────────────────────────────────────────────────
|
|
42
|
+
function createThinkingBubble() {
|
|
43
|
+
const msg = document.createElement('div');
|
|
44
|
+
msg.className = 'message symi-msg thinking-msg';
|
|
45
|
+
msg.innerHTML = `
|
|
46
|
+
<div class="bubble thinking-bubble">
|
|
47
|
+
<div class="think-header">
|
|
48
|
+
<span class="think-badge">◈ PROCESSING</span>
|
|
49
|
+
<span class="think-dots"><span>.</span><span>.</span><span>.</span></span>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="think-bar-wrap"><div class="think-bar"></div></div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="message-clearfix"></div>
|
|
54
|
+
`;
|
|
55
|
+
responseArea.appendChild(msg);
|
|
56
|
+
msg.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
57
|
+
return msg;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Stream bubble lifecycle ────────────────────────────────────────
|
|
61
|
+
function openStreamBubble() {
|
|
62
|
+
streamStart = Date.now();
|
|
63
|
+
const msg = document.createElement('div');
|
|
64
|
+
msg.className = 'message symi-msg stream-msg';
|
|
65
|
+
msg.innerHTML = `<div class="bubble stream-bubble streaming"><div class="bubble-content"></div></div><div class="message-clearfix"></div>`;
|
|
66
|
+
responseArea.appendChild(msg);
|
|
67
|
+
streamBubble = msg.querySelector('.stream-bubble');
|
|
68
|
+
streamContent = msg.querySelector('.bubble-content');
|
|
69
|
+
msg.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
70
|
+
return msg;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function updateStream(text) {
|
|
74
|
+
if (!streamContent) return;
|
|
75
|
+
streamContent.textContent = text;
|
|
76
|
+
responseArea.scrollTop = responseArea.scrollHeight;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function closeStreamBubble(model = 'claude-sonnet-4-6') {
|
|
80
|
+
if (!streamBubble) return;
|
|
81
|
+
streamBubble.classList.remove('streaming');
|
|
82
|
+
streamBubble.classList.add('done');
|
|
83
|
+
|
|
84
|
+
// Apply full markdown rendering to the final text
|
|
85
|
+
const rawText = streamContent?.textContent ?? '';
|
|
86
|
+
if (streamContent && rawText) {
|
|
87
|
+
streamContent.innerHTML = '';
|
|
88
|
+
const textBlock = renderBlock({ type: 'text', text: rawText });
|
|
89
|
+
streamContent.appendChild(textBlock);
|
|
90
|
+
window.applyHighlighting(streamContent);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const elapsed = Date.now() - streamStart;
|
|
94
|
+
const chars = Math.round(rawText.length / 4);
|
|
95
|
+
|
|
96
|
+
// Add copy button
|
|
97
|
+
const copyBtn = document.createElement('button');
|
|
98
|
+
copyBtn.className = 'msg-copy-btn msg-copy-visible';
|
|
99
|
+
copyBtn.title = 'Copy';
|
|
100
|
+
copyBtn.onclick = function() { window.copyMessage(this); };
|
|
101
|
+
copyBtn.innerHTML = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
|
|
102
|
+
<rect x="9" y="9" width="13" height="13" rx="2" stroke="currentColor" stroke-width="2"/>
|
|
103
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" stroke="currentColor" stroke-width="2"/>
|
|
104
|
+
</svg>`;
|
|
105
|
+
streamBubble.appendChild(copyBtn);
|
|
106
|
+
|
|
107
|
+
const footer = document.createElement('div');
|
|
108
|
+
footer.className = 'telemetry';
|
|
109
|
+
footer.innerHTML = `
|
|
110
|
+
<span class="tele-item">✦ ${model}</span>
|
|
111
|
+
<span class="tele-sep">·</span>
|
|
112
|
+
<span class="tele-item">${chars} tokens</span>
|
|
113
|
+
<span class="tele-sep">·</span>
|
|
114
|
+
<span class="tele-item">${elapsed}ms</span>
|
|
115
|
+
`;
|
|
116
|
+
streamBubble.appendChild(footer);
|
|
117
|
+
requestAnimationFrame(() => {
|
|
118
|
+
footer.classList.add('tele-visible');
|
|
119
|
+
// Scroll to bottom immediately after markdown + footer are in the DOM
|
|
120
|
+
responseArea.scrollTop = responseArea.scrollHeight;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Second scroll after the fit-content width transition finishes (0.3s)
|
|
124
|
+
setTimeout(() => { responseArea.scrollTop = responseArea.scrollHeight; }, 350);
|
|
125
|
+
|
|
126
|
+
streamBubble = null;
|
|
127
|
+
streamContent = null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Render history (on first connect) ─────────────────────────────
|
|
131
|
+
function renderHistory(messages) {
|
|
132
|
+
responseArea.innerHTML = '';
|
|
133
|
+
for (const m of messages) {
|
|
134
|
+
if (!m || !m.role) continue;
|
|
135
|
+
const hasContent = Array.isArray(m.content)
|
|
136
|
+
? m.content.some(b => (b.text ?? b.thinking ?? b.name ?? '').trim())
|
|
137
|
+
: extractText(m.content).trim();
|
|
138
|
+
if (!hasContent) continue;
|
|
139
|
+
|
|
140
|
+
const el = window.renderMessage(m);
|
|
141
|
+
responseArea.appendChild(el);
|
|
142
|
+
window.applyHighlighting(el);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Scroll to very bottom after history renders
|
|
146
|
+
requestAnimationFrame(() => { responseArea.scrollTop = responseArea.scrollHeight; });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Gateway event handler ──────────────────────────────────────────
|
|
150
|
+
let thinkingEl = null;
|
|
151
|
+
|
|
152
|
+
function handleGatewayEvent(event) {
|
|
153
|
+
if (event.event !== 'chat') return;
|
|
154
|
+
const p = event.payload;
|
|
155
|
+
if (!p || p.sessionKey !== window.SESSION_KEY) return;
|
|
156
|
+
|
|
157
|
+
if (p.state === 'delta') {
|
|
158
|
+
// First delta — remove thinking bubble, open stream bubble
|
|
159
|
+
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
160
|
+
if (!streamBubble) {
|
|
161
|
+
openStreamBubble();
|
|
162
|
+
setAgentStatus('streaming');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const text = extractText(p.message);
|
|
166
|
+
if (text) updateStream(text);
|
|
167
|
+
|
|
168
|
+
} else if (p.state === 'final') {
|
|
169
|
+
closeStreamBubble();
|
|
170
|
+
setAgentStatus('done');
|
|
171
|
+
isStreaming = false;
|
|
172
|
+
currentRunId = null;
|
|
173
|
+
enableInput();
|
|
174
|
+
|
|
175
|
+
} else if (p.state === 'aborted' || p.state === 'error') {
|
|
176
|
+
if (streamBubble) closeStreamBubble();
|
|
177
|
+
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
178
|
+
setAgentStatus('idle');
|
|
179
|
+
isStreaming = false;
|
|
180
|
+
currentRunId = null;
|
|
181
|
+
enableInput();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Agent Status Orb ───────────────────────────────────────────────
|
|
186
|
+
const asoPanel = document.getElementById('agent-status-panel');
|
|
187
|
+
const asoLabel = document.getElementById('aso-label');
|
|
188
|
+
const asoSub = document.getElementById('aso-sub');
|
|
189
|
+
let asoDoneTimer = null;
|
|
190
|
+
|
|
191
|
+
const ASO_STATES = {
|
|
192
|
+
idle: { state: 'idle', label: 'STANDBY', sub: 'Awaiting your prompt' },
|
|
193
|
+
waiting: { state: 'waiting', label: 'WAITING', sub: 'Sending to Symi\u2026' },
|
|
194
|
+
thinking: { state: 'thinking', label: 'PROCESSING', sub: 'Reasoning through request' },
|
|
195
|
+
streaming: { state: 'streaming', label: 'RESPONDING', sub: 'Generating response' },
|
|
196
|
+
done: { state: 'done', label: 'COMPLETE', sub: 'Response ready' },
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
function setAgentStatus(key) {
|
|
200
|
+
if (!asoPanel) return;
|
|
201
|
+
if (asoDoneTimer) { clearTimeout(asoDoneTimer); asoDoneTimer = null; }
|
|
202
|
+
const s = ASO_STATES[key] || ASO_STATES.idle;
|
|
203
|
+
asoPanel.dataset.state = s.state;
|
|
204
|
+
if (asoLabel) asoLabel.textContent = s.label;
|
|
205
|
+
if (asoSub) asoSub.textContent = s.sub;
|
|
206
|
+
if (key === 'done') {
|
|
207
|
+
asoDoneTimer = setTimeout(() => setAgentStatus('idle'), 3000);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ── Input state ────────────────────────────────────────────────────
|
|
212
|
+
function disableInput() {
|
|
213
|
+
promptInput.disabled = true;
|
|
214
|
+
sendBtn.disabled = true;
|
|
215
|
+
sendBtn.classList.add('btn-loading');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function enableInput() {
|
|
219
|
+
promptInput.disabled = false;
|
|
220
|
+
sendBtn.disabled = false;
|
|
221
|
+
sendBtn.classList.remove('btn-loading');
|
|
222
|
+
promptInput.focus();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── Send handler ───────────────────────────────────────────────────
|
|
226
|
+
async function handleSend() {
|
|
227
|
+
if (isStreaming || !gateway.connected) return;
|
|
228
|
+
const text = promptInput.value.trim();
|
|
229
|
+
if (!text) return;
|
|
230
|
+
|
|
231
|
+
isStreaming = true;
|
|
232
|
+
promptInput.value = '';
|
|
233
|
+
disableInput();
|
|
234
|
+
|
|
235
|
+
addUserMessage(text);
|
|
236
|
+
await sleep(200);
|
|
237
|
+
|
|
238
|
+
setAgentStatus('waiting');
|
|
239
|
+
thinkingEl = createThinkingBubble();
|
|
240
|
+
setAgentStatus('thinking');
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
await gateway.send(text);
|
|
244
|
+
// Response will arrive via chat events
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
247
|
+
setAgentStatus('idle');
|
|
248
|
+
const errMsg = document.createElement('div');
|
|
249
|
+
errMsg.className = 'message symi-msg';
|
|
250
|
+
errMsg.innerHTML = `<div class="bubble stream-bubble done"><div class="bubble-content" style="color:var(--accent-pink)">Error: ${escapeHtml(err.message)}</div></div><div class="message-clearfix"></div>`;
|
|
251
|
+
responseArea.appendChild(errMsg);
|
|
252
|
+
isStreaming = false;
|
|
253
|
+
enableInput();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
sendBtn.addEventListener('click', handleSend);
|
|
258
|
+
promptInput.addEventListener('keydown', e => {
|
|
259
|
+
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); }
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Clicking anywhere on the prompt bar (padding, icon, gaps) focuses the input
|
|
263
|
+
document.getElementById('prompt-bar').addEventListener('click', e => {
|
|
264
|
+
if (e.target !== sendBtn && !sendBtn.contains(e.target)) {
|
|
265
|
+
promptInput.focus();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ── Gateway setup ──────────────────────────────────────────────────
|
|
270
|
+
const gateway = new SymiGateway();
|
|
271
|
+
|
|
272
|
+
gateway.addEventListener('connect', () => {
|
|
273
|
+
setStatus('online');
|
|
274
|
+
enableInput();
|
|
275
|
+
// History arrives via separate 'history' event pushed by the server
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
gateway.addEventListener('history', (e) => {
|
|
279
|
+
const msgs = Array.isArray(e.detail?.messages) ? e.detail.messages : [];
|
|
280
|
+
renderHistory(msgs);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
gateway.addEventListener('disconnect', () => {
|
|
284
|
+
setStatus('offline');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
gateway.addEventListener('event', (e) => {
|
|
288
|
+
handleGatewayEvent(e.detail);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Start connecting
|
|
292
|
+
disableInput();
|
|
293
|
+
gateway.start();
|