incognitolm commited on
Commit Β·
f0aa618
1
Parent(s): 1ce36f5
Revert
Browse files- public/css/input.css +0 -16
- public/index.html +0 -3
- public/js/app.js +4 -5
- public/js/chat.js +5 -50
- server/wsHandler.js +2 -2
public/css/input.css
CHANGED
|
@@ -71,22 +71,6 @@
|
|
| 71 |
}
|
| 72 |
.attach-btn:hover { color: var(--text); background: var(--bg-hover); }
|
| 73 |
|
| 74 |
-
/* Back button */
|
| 75 |
-
.back-row {
|
| 76 |
-
text-align: center;
|
| 77 |
-
margin-bottom: 8px;
|
| 78 |
-
}
|
| 79 |
-
.back-btn {
|
| 80 |
-
padding: 4px 8px;
|
| 81 |
-
font-size: 12px;
|
| 82 |
-
color: var(--text-muted);
|
| 83 |
-
background: none;
|
| 84 |
-
border: none;
|
| 85 |
-
cursor: pointer;
|
| 86 |
-
transition: color var(--transition);
|
| 87 |
-
}
|
| 88 |
-
.back-btn:hover { color: var(--text); }
|
| 89 |
-
|
| 90 |
/* ββ Bottom input bar ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 91 |
.bottom-input-bar {
|
| 92 |
flex-shrink: 0;
|
|
|
|
| 71 |
}
|
| 72 |
.attach-btn:hover { color: var(--text); background: var(--bg-hover); }
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
/* ββ Bottom input bar ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 75 |
.bottom-input-bar {
|
| 76 |
flex-shrink: 0;
|
public/index.html
CHANGED
|
@@ -122,9 +122,6 @@
|
|
| 122 |
|
| 123 |
<!-- Bottom input (active during chat) -->
|
| 124 |
<div id="bottom-input-bar" class="bottom-input-bar hidden">
|
| 125 |
-
<div id="back-row" class="back-row hidden">
|
| 126 |
-
<button id="back-btn" class="back-btn" title="Back to conversation">β Back</button>
|
| 127 |
-
</div>
|
| 128 |
<div class="bottom-input-wrap">
|
| 129 |
<button id="bottom-attach-btn" class="attach-btn-bottom" title="Attach">
|
| 130 |
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
|
|
| 122 |
|
| 123 |
<!-- Bottom input (active during chat) -->
|
| 124 |
<div id="bottom-input-bar" class="bottom-input-bar hidden">
|
|
|
|
|
|
|
|
|
|
| 125 |
<div class="bottom-input-wrap">
|
| 126 |
<button id="bottom-attach-btn" class="attach-btn-bottom" title="Attach">
|
| 127 |
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
public/js/app.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
| 5 |
createNewSession, showWelcomeScreen,
|
| 6 |
switchSession, currentSessionId, onSessionChange,
|
| 7 |
} from './sessions.js';
|
| 8 |
-
import { submitMessage, renderSession, setActiveSession, getIsStreaming
|
| 9 |
import { openAuthModal, closeModal, openPasteEditor } from './modals.js';
|
| 10 |
import { openSettings, applyTheme } from './settings.js';
|
| 11 |
import { showNotification, autoResize, escHtml } from './ui.js';
|
|
@@ -128,8 +128,7 @@ function triggerBottomSend() {
|
|
| 128 |
if (!text && attachments.length === 0) return;
|
| 129 |
if (bottomInput) { bottomInput.value = ''; autoResize(bottomInput, 6); }
|
| 130 |
clearFilePreviewRow();
|
| 131 |
-
|
| 132 |
-
doSend(text || '', attachments, editIndex);
|
| 133 |
}
|
| 134 |
|
| 135 |
document.querySelectorAll('.tool-btn-sm').forEach(btn =>
|
|
@@ -137,8 +136,8 @@ document.querySelectorAll('.tool-btn-sm').forEach(btn =>
|
|
| 137 |
|
| 138 |
// ββ Core send βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 139 |
|
| 140 |
-
function doSend(text, attachments = []
|
| 141 |
-
submitMessage(text, attachments
|
| 142 |
}
|
| 143 |
|
| 144 |
// ββ Attachments βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 5 |
createNewSession, showWelcomeScreen,
|
| 6 |
switchSession, currentSessionId, onSessionChange,
|
| 7 |
} from './sessions.js';
|
| 8 |
+
import { submitMessage, renderSession, setActiveSession, getIsStreaming } from './chat.js';
|
| 9 |
import { openAuthModal, closeModal, openPasteEditor } from './modals.js';
|
| 10 |
import { openSettings, applyTheme } from './settings.js';
|
| 11 |
import { showNotification, autoResize, escHtml } from './ui.js';
|
|
|
|
| 128 |
if (!text && attachments.length === 0) return;
|
| 129 |
if (bottomInput) { bottomInput.value = ''; autoResize(bottomInput, 6); }
|
| 130 |
clearFilePreviewRow();
|
| 131 |
+
doSend(text || '', attachments);
|
|
|
|
| 132 |
}
|
| 133 |
|
| 134 |
document.querySelectorAll('.tool-btn-sm').forEach(btn =>
|
|
|
|
| 136 |
|
| 137 |
// ββ Core send βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 138 |
|
| 139 |
+
function doSend(text, attachments = []) {
|
| 140 |
+
submitMessage(text, attachments);
|
| 141 |
}
|
| 142 |
|
| 143 |
// ββ Attachments βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
public/js/chat.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
// chat.js β Chat rendering, streaming, versioning, editing
|
| 2 |
import { send, on, off } from './ws.js';
|
| 3 |
-
import { currentSessionId
|
| 4 |
import {
|
| 5 |
renderMarkdown, attachCodeCopyListeners, attachSvgPanelListeners,
|
| 6 |
escHtml, showNotification, autoResize,
|
|
@@ -12,9 +12,6 @@ let streamingBubble = null;
|
|
| 12 |
let streamingText = '';
|
| 13 |
let autoScroll = true;
|
| 14 |
let pendingAssets = [];
|
| 15 |
-
let reEditingIndex = null;
|
| 16 |
-
|
| 17 |
-
export function getReEditingIndex() { return reEditingIndex; }
|
| 18 |
|
| 19 |
export function setActiveSession(id) { activeSessionId = id; }
|
| 20 |
export function getIsStreaming() { return isStreaming; }
|
|
@@ -140,7 +137,7 @@ function appendUserMsg(box, msg, index) {
|
|
| 140 |
controls.className = 'msg-controls msg-controls-right';
|
| 141 |
controls.appendChild(buildActions([
|
| 142 |
{ icon: 'π', title: 'Copy', fn: () => copyText(text) },
|
| 143 |
-
{ icon: 'βοΈ', title: 'Edit', fn: () =>
|
| 144 |
], 'right'));
|
| 145 |
if (msg.versions?.length > 1) controls.appendChild(buildVersionNav(msg, index));
|
| 146 |
wrap.appendChild(controls);
|
|
@@ -354,27 +351,6 @@ function startAssistantEdit(wrap, index, msg) {
|
|
| 354 |
ta.setSelectionRange(ta.value.length, ta.value.length);
|
| 355 |
}
|
| 356 |
|
| 357 |
-
function startUserReEdit(wrap, index, msg, text) {
|
| 358 |
-
reEditingIndex = index;
|
| 359 |
-
// Truncate the displayed history to this message
|
| 360 |
-
const session = getCurrentSession();
|
| 361 |
-
if (session) {
|
| 362 |
-
const truncated = session.history.slice(0, index + 1);
|
| 363 |
-
renderHistory(truncated);
|
| 364 |
-
}
|
| 365 |
-
// Put the text in bottom input
|
| 366 |
-
const bottomInput = document.getElementById('bottom-input');
|
| 367 |
-
if (bottomInput) {
|
| 368 |
-
bottomInput.value = text;
|
| 369 |
-
autoResize(bottomInput);
|
| 370 |
-
bottomInput.focus();
|
| 371 |
-
}
|
| 372 |
-
// Show back button
|
| 373 |
-
document.getElementById('back-row')?.classList.remove('hidden');
|
| 374 |
-
// Clear attachments
|
| 375 |
-
clearFilePreviewRow();
|
| 376 |
-
}
|
| 377 |
-
|
| 378 |
function makeUserEditTextarea(value) {
|
| 379 |
const ta = document.createElement('textarea');
|
| 380 |
ta.value = value;
|
|
@@ -550,19 +526,11 @@ function appendAsset(asset) {
|
|
| 550 |
|
| 551 |
// ββ Submit ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 552 |
|
| 553 |
-
export function submitMessage(text, attachments = [], regenerate = false
|
| 554 |
if (!text.trim() && attachments.length === 0 && !regenerate) return;
|
| 555 |
if (isStreaming) { send({ type: 'chat:stop' }); return; }
|
| 556 |
if (!activeSessionId) return;
|
| 557 |
|
| 558 |
-
if (editIndex !== undefined) {
|
| 559 |
-
// Send edit
|
| 560 |
-
send({ type: 'chat:editMessage', sessionId: activeSessionId, messageIndex: editIndex, newContent: text });
|
| 561 |
-
reEditingIndex = null;
|
| 562 |
-
document.getElementById('back-row')?.classList.add('hidden');
|
| 563 |
-
return;
|
| 564 |
-
}
|
| 565 |
-
|
| 566 |
const images = attachments.filter(a => a.type === 'image');
|
| 567 |
const textFiles= attachments.filter(a => a.type === 'text');
|
| 568 |
|
|
@@ -586,7 +554,7 @@ export function submitMessage(text, attachments = [], regenerate = false, editIn
|
|
| 586 |
|
| 587 |
// Append optimistic user bubble
|
| 588 |
const box = document.getElementById('chat-messages');
|
| 589 |
-
if (box
|
| 590 |
const wrap = makeWrap(-1);
|
| 591 |
const bubble = document.createElement('div'); bubble.className = 'msg-user';
|
| 592 |
bubble.innerHTML = renderMarkdown(fullText);
|
|
@@ -601,7 +569,7 @@ export function submitMessage(text, attachments = [], regenerate = false, editIn
|
|
| 601 |
if (autoScroll) box.scrollTop = box.scrollHeight;
|
| 602 |
}
|
| 603 |
|
| 604 |
-
send({ type: 'chat:send', sessionId: activeSessionId, content,
|
| 605 |
}
|
| 606 |
|
| 607 |
// ββ Utils βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -662,19 +630,6 @@ function openImageModal(src) {
|
|
| 662 |
import('./modals.js').then(m => m.openImageModal(src));
|
| 663 |
}
|
| 664 |
|
| 665 |
-
// Back button for re-editing
|
| 666 |
-
document.getElementById('back-btn')?.addEventListener('click', () => {
|
| 667 |
-
reEditingIndex = null;
|
| 668 |
-
// Restore full history
|
| 669 |
-
const session = getCurrentSession();
|
| 670 |
-
if (session) renderHistory(session.history);
|
| 671 |
-
// Clear input
|
| 672 |
-
const bottomInput = document.getElementById('bottom-input');
|
| 673 |
-
if (bottomInput) bottomInput.value = '';
|
| 674 |
-
// Hide back
|
| 675 |
-
document.getElementById('back-row')?.classList.add('hidden');
|
| 676 |
-
});
|
| 677 |
-
|
| 678 |
// Scroll tracking
|
| 679 |
document.getElementById('chat-view')?.addEventListener('scroll', e => {
|
| 680 |
const el = e.target;
|
|
|
|
| 1 |
// chat.js β Chat rendering, streaming, versioning, editing
|
| 2 |
import { send, on, off } from './ws.js';
|
| 3 |
+
import { currentSessionId } from './sessions.js';
|
| 4 |
import {
|
| 5 |
renderMarkdown, attachCodeCopyListeners, attachSvgPanelListeners,
|
| 6 |
escHtml, showNotification, autoResize,
|
|
|
|
| 12 |
let streamingText = '';
|
| 13 |
let autoScroll = true;
|
| 14 |
let pendingAssets = [];
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
export function setActiveSession(id) { activeSessionId = id; }
|
| 17 |
export function getIsStreaming() { return isStreaming; }
|
|
|
|
| 137 |
controls.className = 'msg-controls msg-controls-right';
|
| 138 |
controls.appendChild(buildActions([
|
| 139 |
{ icon: 'π', title: 'Copy', fn: () => copyText(text) },
|
| 140 |
+
{ icon: 'βοΈ', title: 'Edit', fn: () => startUserEdit(wrap, index, msg, text) },
|
| 141 |
], 'right'));
|
| 142 |
if (msg.versions?.length > 1) controls.appendChild(buildVersionNav(msg, index));
|
| 143 |
wrap.appendChild(controls);
|
|
|
|
| 351 |
ta.setSelectionRange(ta.value.length, ta.value.length);
|
| 352 |
}
|
| 353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
function makeUserEditTextarea(value) {
|
| 355 |
const ta = document.createElement('textarea');
|
| 356 |
ta.value = value;
|
|
|
|
| 526 |
|
| 527 |
// ββ Submit ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 528 |
|
| 529 |
+
export function submitMessage(text, attachments = [], regenerate = false) {
|
| 530 |
if (!text.trim() && attachments.length === 0 && !regenerate) return;
|
| 531 |
if (isStreaming) { send({ type: 'chat:stop' }); return; }
|
| 532 |
if (!activeSessionId) return;
|
| 533 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 534 |
const images = attachments.filter(a => a.type === 'image');
|
| 535 |
const textFiles= attachments.filter(a => a.type === 'text');
|
| 536 |
|
|
|
|
| 554 |
|
| 555 |
// Append optimistic user bubble
|
| 556 |
const box = document.getElementById('chat-messages');
|
| 557 |
+
if (box) {
|
| 558 |
const wrap = makeWrap(-1);
|
| 559 |
const bubble = document.createElement('div'); bubble.className = 'msg-user';
|
| 560 |
bubble.innerHTML = renderMarkdown(fullText);
|
|
|
|
| 569 |
if (autoScroll) box.scrollTop = box.scrollHeight;
|
| 570 |
}
|
| 571 |
|
| 572 |
+
send({ type: 'chat:send', sessionId: activeSessionId, content, tools: getActiveTools(), clientId: localStorage.getItem('ipai_client_id') || '' });
|
| 573 |
}
|
| 574 |
|
| 575 |
// ββ Utils βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 630 |
import('./modals.js').then(m => m.openImageModal(src));
|
| 631 |
}
|
| 632 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 633 |
// Scroll tracking
|
| 634 |
document.getElementById('chat-view')?.addEventListener('scroll', e => {
|
| 635 |
const el = e.target;
|
server/wsHandler.js
CHANGED
|
@@ -146,7 +146,7 @@ const handlers = {
|
|
| 146 |
},
|
| 147 |
|
| 148 |
'chat:send': async (ws, msg, client) => {
|
| 149 |
-
const { sessionId, content,
|
| 150 |
if (!client.userId) {
|
| 151 |
if (!sessionStore.tempCanSend(client.tempId)) return safeSend(ws, { type: 'chat:limitReached' });
|
| 152 |
sessionStore.tempBump(client.tempId);
|
|
@@ -165,7 +165,7 @@ const handlers = {
|
|
| 165 |
const assetsCollected = [], toolCallsCollected = [];
|
| 166 |
|
| 167 |
await streamChat(ws, {
|
| 168 |
-
history: session.history || [], userMessage:
|
| 169 |
accessToken: client.accessToken, clientId: msg.clientId, abortSignal: abort.signal,
|
| 170 |
onToken(t) { fullText += t; safeSend(ws, { type: 'chat:token', token: t, sessionId }); },
|
| 171 |
onToolCall(call) {
|
|
|
|
| 146 |
},
|
| 147 |
|
| 148 |
'chat:send': async (ws, msg, client) => {
|
| 149 |
+
const { sessionId, content, tools } = msg;
|
| 150 |
if (!client.userId) {
|
| 151 |
if (!sessionStore.tempCanSend(client.tempId)) return safeSend(ws, { type: 'chat:limitReached' });
|
| 152 |
sessionStore.tempBump(client.tempId);
|
|
|
|
| 165 |
const assetsCollected = [], toolCallsCollected = [];
|
| 166 |
|
| 167 |
await streamChat(ws, {
|
| 168 |
+
history: session.history || [], userMessage: content, tools: tools || {},
|
| 169 |
accessToken: client.accessToken, clientId: msg.clientId, abortSignal: abort.signal,
|
| 170 |
onToken(t) { fullText += t; safeSend(ws, { type: 'chat:token', token: t, sessionId }); },
|
| 171 |
onToolCall(call) {
|