incognitolm commited on
Commit
f0aa618
Β·
1 Parent(s): 1ce36f5
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, getReEditingIndex } 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,8 +128,7 @@ function triggerBottomSend() {
128
  if (!text && attachments.length === 0) return;
129
  if (bottomInput) { bottomInput.value = ''; autoResize(bottomInput, 6); }
130
  clearFilePreviewRow();
131
- const editIndex = getReEditingIndex();
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 = [], editIndex = undefined) {
141
- submitMessage(text, attachments, false, editIndex);
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, getCurrentSession } from './sessions.js';
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: () => startUserReEdit(wrap, index, msg, text) },
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, editIndex = undefined) {
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 && !regenerate) {
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, regenerate, tools: getActiveTools(), clientId: localStorage.getItem('ipai_client_id') || '' });
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, regenerate, tools } = msg;
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: regenerate ? undefined : 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) {
 
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) {