1
0
Fork 0

Redesign chat input area

This commit is contained in:
Pabloader 2026-04-09 13:46:30 +00:00
parent 5b067b8a2c
commit ffaa137d08
2 changed files with 157 additions and 192 deletions

View File

@ -204,39 +204,97 @@
}
.inputContainer {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: auto;
padding-top: 8px;
border-top: 1px solid var(--border);
}
.optionsRow {
.inputWrapper {
display: flex;
flex-direction: column;
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
transition: border-color 0.15s ease;
&:focus-within {
border-color: var(--accent);
}
}
.input {
width: 100%;
padding: 10px 12px;
font-size: 13px;
font-family: inherit;
background: transparent;
border: none;
color: var(--text);
outline: none;
&::placeholder {
color: var(--text-muted);
}
}
.inputFooter {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
border-top: 1px solid var(--border);
gap: 8px;
}
.inputFooterLeft {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
.inputFooterRight {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.tokenCounter {
display: flex;
align-items: center;
gap: 8px;
font-size: 11px;
color: var(--text-muted);
white-space: nowrap;
}
.summarizeButton {
.toggleContainer {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text-muted);
cursor: pointer;
user-select: none;
white-space: nowrap;
input[type="checkbox"] {
cursor: pointer;
accent-color: var(--accent);
}
}
.actionButton {
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
padding: 5px;
color: var(--text-muted);
background: transparent;
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
height: 28px;
width: 28px;
&:hover:not(:disabled) {
color: var(--text);
@ -245,107 +303,24 @@
&:disabled {
opacity: 0.4;
cursor: default;
cursor: not-allowed;
}
}
.toggleContainer {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text);
cursor: pointer;
user-select: none;
input[type="checkbox"] {
cursor: pointer;
accent-color: var(--accent);
}
}
.input {
width: 100%;
padding: 8px;
font-size: 13px;
font-family: inherit;
resize: vertical;
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text);
outline: none;
&:focus {
border-color: var(--accent);
}
&::placeholder {
color: var(--text-muted);
}
}
.buttonRow {
display: flex;
gap: 6px;
}
.sendButton {
flex: 1;
padding: 8px 16px;
font-size: 14px;
padding: 5px 14px;
font-size: 13px;
font-weight: bold;
color: var(--bg);
background: var(--accent);
border: none;
border-radius: 4px;
cursor: pointer;
height: 32px;
height: 28px;
&:hover {
&:hover:not(:disabled) {
background: var(--accent-alt);
}
}
.continueButton {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
color: var(--text-muted);
background: transparent;
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
height: 32px;
&:hover:not(:disabled) {
color: var(--text);
border-color: var(--text-muted);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.regenerateButton {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
color: var(--text-muted);
background: transparent;
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
height: 32px;
&:hover:not(:disabled) {
color: var(--text);
border-color: var(--text-muted);
}
&:disabled {
opacity: 0.5;
@ -354,7 +329,7 @@
}
.stopButton {
padding: 8px 16px;
padding: 5px 14px;
font-size: 13px;
font-weight: bold;
color: var(--bg);
@ -362,26 +337,9 @@
border: none;
border-radius: 4px;
cursor: pointer;
height: 32px;
height: 28px;
&:hover {
background: #d32f2f;
}
}
.clearButton {
padding: 8px 16px;
font-size: 12px;
color: var(--text-muted);
background: transparent;
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
align-self: center;
margin: 8px 0 28px;
&:hover {
color: var(--text);
border-color: var(--text-muted);
}
}

View File

@ -363,6 +363,7 @@ export const ChatPanel = ({ visible }: { visible: boolean }) => {
const handleDeleteMessage = useCallback((messageId: string) => {
if (!currentStory || !currentWorld) return;
if (!confirm('Delete this and all following messages?')) return;
dispatch({
type: 'DELETE_CHAT_MESSAGES_FROM',
worldId: currentWorld.id,
@ -542,84 +543,90 @@ export const ChatPanel = ({ visible }: { visible: boolean }) => {
)}
{currentStory && (
<div class={styles.inputContainer}>
<div class={styles.optionsRow}>
<label class={styles.toggleContainer}>
{!currentWorld?.chatOnly && (<>
<input
type="checkbox"
checked={enableThinking}
onChange={(e) => dispatch({
type: 'SET_ENABLE_THINKING',
enable: (e.target as HTMLInputElement).checked,
})}
disabled={isDisabled}
/>
<span>Enable thinking</span>
</>)}
</label>
<div class={styles.tokenCounter}>
{tokenCount && <span>{tokenCount.taken} / {tokenCount.total} tokens</span>}
{!currentWorld?.chatOnly && (<button
class={styles.summarizeButton}
onClick={summarizeAll}
disabled={isSummarizing || !currentStory || !connection || !model}
title={isSummarizing ? 'Summarizing...' : 'Summarize'}>
<Sparkles size={14} />
</button>
)}
<div class={styles.inputWrapper}>
<ContentEditable
autoLines
class={styles.input}
value={highlight(input)}
onInput={setInput}
onKeyDown={handleKeyDown}
placeholder={
isLoading
? 'Generating...'
: isDisabled
? 'Connect to an LLM server to chat'
: 'Type a message...'}
disabled={isDisabled}
/>
<div class={styles.inputFooter}>
<div class={styles.inputFooterLeft}>
{tokenCount && (
<span class={styles.tokenCounter}>
{tokenCount.taken} / {tokenCount.total}
</span>
)}
{!currentWorld?.chatOnly && (
<label class={styles.toggleContainer}>
<input
type="checkbox"
checked={enableThinking}
onChange={(e) => dispatch({
type: 'SET_ENABLE_THINKING',
enable: (e.target as HTMLInputElement).checked,
})}
disabled={isDisabled}
/>
<span>Thinking</span>
</label>
)}
</div>
<div class={styles.inputFooterRight}>
{isLoading ? (
<button class={styles.stopButton} onClick={handleStopGeneration}>
Stop
</button>
) : (
<>
{!currentWorld?.chatOnly && (
<button
class={styles.actionButton}
onClick={summarizeAll}
disabled={isSummarizing || !currentStory || !connection || !model}
title={isSummarizing ? 'Summarizing...' : 'Summarize'}
>
<Sparkles size={14} />
</button>
)}
{!currentWorld?.chatOnly && (
<button
class={styles.actionButton}
onClick={handleContinue}
disabled={isDisabled}
title="Continue"
>
<ChevronsRight size={14} />
</button>
)}
<button
class={styles.actionButton}
onClick={handleRegenerate}
disabled={isDisabled}
title="Regenerate last response"
>
<RefreshCw size={14} />
</button>
<button
class={styles.sendButton}
onClick={handleSendMessage}
disabled={isDisabled}
>
Send
</button>
</>
)}
</div>
</div>
</div>
<ContentEditable
autoLines
class={styles.input}
value={highlight(input)}
onInput={setInput}
onKeyDown={handleKeyDown}
placeholder={
isLoading
? 'Generating...'
: isDisabled
? 'Connect to an LLM server to chat'
: 'Type a message...'}
disabled={isDisabled}
/>
{isLoading ? (
<button
class={styles.stopButton}
onClick={handleStopGeneration}
>
Stop
</button>
) : (
<div class={styles.buttonRow}>
<button
class={styles.sendButton}
onClick={handleSendMessage}
disabled={isDisabled}
>
Send
</button>
{!currentWorld?.chatOnly && (
<button
class={styles.continueButton}
onClick={handleContinue}
disabled={isDisabled}
title="Continue"
>
<ChevronsRight size={14} />
</button>
)}
<button
class={styles.regenerateButton}
onClick={handleRegenerate}
disabled={isDisabled}
title="Regenerate last response"
>
<RefreshCw size={14} />
</button>
</div>
)}
</div>
)}
</div>