Mobile/Thinking Update

This commit is contained in:
Rokojori 2026-07-16 21:08:27 +02:00
parent 86fb8551fe
commit db7a92e457
2 changed files with 41 additions and 6 deletions

View File

@ -2,6 +2,8 @@ rojo-chat-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
min-height: 0;
overflow: hidden;
background: #0f1117; background: #0f1117;
} }
@ -72,6 +74,7 @@ rojo-chat-panel {
.rcp-history { .rcp-history {
flex: 1; flex: 1;
min-height: 0;
overflow-y: auto; overflow-y: auto;
padding: 14px 12px; padding: 14px 12px;
display: flex; display: flex;

View File

@ -39,12 +39,13 @@ function parentDir( filePath: string ): string
class RojoChatPanel extends HTMLElement class RojoChatPanel extends HTMLElement
{ {
_initialized = false; _initialized = false;
_conversationId = ''; _conversationId = '';
_sending = false; _sending = false;
_md: { render: ( s: string ) => string } | null = null; _md: { render: ( s: string ) => string } | null = null;
_rojos: RojoEntry[] = []; _rojos: RojoEntry[] = [];
_selectedPath = ''; _selectedPath = '';
_thinkingInterval: ReturnType<typeof setInterval> | null = null;
connectedCallback(): void connectedCallback(): void
{ {
@ -85,6 +86,11 @@ class RojoChatPanel extends HTMLElement
if ( e.key === 'Enter' && !e.shiftKey ) { e.preventDefault(); this._send(); } if ( e.key === 'Enter' && !e.shiftKey ) { e.preventDefault(); this._send(); }
} ); } );
inputText.addEventListener( 'focus', () =>
{
setTimeout( () => inputText.scrollIntoView( { behavior: 'smooth', block: 'nearest' } ), 300 );
} );
picker.addEventListener( 'change', () => picker.addEventListener( 'change', () =>
{ {
this._selectedPath = picker.value; this._selectedPath = picker.value;
@ -164,6 +170,25 @@ class RojoChatPanel extends HTMLElement
catch {} catch {}
} }
_startThinking( bubble: HTMLElement ): void
{
const frames = [ '.', '..', '...', 'thinking', '.', '..', '...', 'imagining' ];
let i = 0;
bubble.textContent = frames[ 0 ];
this._thinkingInterval = setInterval( () =>
{
i = ( i + 1 ) % frames.length;
bubble.textContent = frames[ i ];
}, 250 );
}
_stopThinking(): void
{
if ( this._thinkingInterval === null ) return;
clearInterval( this._thinkingInterval );
this._thinkingInterval = null;
}
async _send(): Promise<void> async _send(): Promise<void>
{ {
if ( this._sending ) return; if ( this._sending ) return;
@ -187,10 +212,11 @@ class RojoChatPanel extends HTMLElement
const assistantBubble = document.createElement( 'div' ); const assistantBubble = document.createElement( 'div' );
assistantBubble.className = 'rcp-assistant-bubble'; assistantBubble.className = 'rcp-assistant-bubble';
assistantBubble.textContent = '…';
history.appendChild( assistantBubble ); history.appendChild( assistantBubble );
history.scrollTop = history.scrollHeight; history.scrollTop = history.scrollHeight;
this._startThinking( assistantBubble );
try try
{ {
const body: Record<string, string> = { id: this._conversationId, message: text }; const body: Record<string, string> = { id: this._conversationId, message: text };
@ -231,6 +257,11 @@ class RojoChatPanel extends HTMLElement
const msg = JSON.parse( trimmed ) as { type: string; text?: string }; const msg = JSON.parse( trimmed ) as { type: string; text?: string };
if ( msg.type === 'CHAT' && msg.text ) if ( msg.type === 'CHAT' && msg.text )
{ {
if ( !markdown )
{
this._stopThinking();
assistantBubble.innerHTML = '';
}
await typeText( msg.text, piece => await typeText( msg.text, piece =>
{ {
markdown += piece; markdown += piece;
@ -243,10 +274,11 @@ class RojoChatPanel extends HTMLElement
} }
} }
if ( !markdown ) assistantBubble.textContent = '(no response)'; if ( !markdown ) { this._stopThinking(); assistantBubble.textContent = '(no response)'; }
} }
catch ( err ) catch ( err )
{ {
this._stopThinking();
assistantBubble.textContent = '(error: could not reach Rojo)'; assistantBubble.textContent = '(error: could not reach Rojo)';
console.error( err ); console.error( err );
} }