fix: refine input stadium interactions
This commit is contained in:
parent
2beaf6c702
commit
cd59bbfa41
@ -204,6 +204,8 @@ async function bootstrapApp() {
|
||||
settingsOpen: false,
|
||||
quickMenuOpen: false,
|
||||
inputLineCount: 1,
|
||||
inputIsMultiline: false,
|
||||
inputIsFocused: false,
|
||||
// 思考块滚动锁
|
||||
thinkingScrollLocks: new Map(),
|
||||
|
||||
@ -1327,6 +1329,7 @@ async function bootstrapApp() {
|
||||
this.toolMenuOpen = false;
|
||||
this.quickMenuOpen = false;
|
||||
this.inputLineCount = 1;
|
||||
this.inputIsMultiline = false;
|
||||
this.toolSettingsLoading = false;
|
||||
this.toolSettings = [];
|
||||
|
||||
@ -2458,19 +2461,37 @@ async function bootstrapApp() {
|
||||
this.autoResizeInput();
|
||||
},
|
||||
|
||||
handleInputFocus() {
|
||||
this.inputIsFocused = true;
|
||||
},
|
||||
|
||||
handleInputBlur() {
|
||||
this.inputIsFocused = false;
|
||||
},
|
||||
|
||||
autoResizeInput() {
|
||||
this.$nextTick(() => {
|
||||
const textarea = this.$refs.stadiumInput;
|
||||
if (!textarea) {
|
||||
return;
|
||||
}
|
||||
const previousHeight = textarea.offsetHeight;
|
||||
textarea.style.height = 'auto';
|
||||
const computedStyle = window.getComputedStyle(textarea);
|
||||
const lineHeight = parseFloat(computedStyle.lineHeight || '20') || 20;
|
||||
const maxHeight = lineHeight * 6;
|
||||
const newHeight = Math.min(textarea.scrollHeight, maxHeight);
|
||||
textarea.style.height = `${newHeight}px`;
|
||||
this.inputLineCount = Math.max(1, Math.round(newHeight / lineHeight));
|
||||
const targetHeight = Math.min(textarea.scrollHeight, maxHeight);
|
||||
this.inputLineCount = Math.max(1, Math.round(targetHeight / lineHeight));
|
||||
this.inputIsMultiline = targetHeight > lineHeight * 1.4;
|
||||
if (Math.abs(targetHeight - previousHeight) <= 0.5) {
|
||||
textarea.style.height = `${targetHeight}px`;
|
||||
return;
|
||||
}
|
||||
textarea.style.height = `${previousHeight}px`;
|
||||
void textarea.offsetHeight;
|
||||
requestAnimationFrame(() => {
|
||||
textarea.style.height = `${targetHeight}px`;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -2478,7 +2499,7 @@ async function bootstrapApp() {
|
||||
if (!this.quickMenuOpen) {
|
||||
return;
|
||||
}
|
||||
const shell = this.$refs.compactInputShell;
|
||||
const shell = this.$refs.stadiumShellOuter || this.$refs.compactInputShell;
|
||||
if (shell && shell.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -488,7 +488,15 @@
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<div class="input-area compact-input-area">
|
||||
<div class="stadium-shell" ref="compactInputShell" :class="{ expanded: inputLineCount > 1 }">
|
||||
<div class="stadium-input-wrapper" ref="stadiumShellOuter">
|
||||
<div
|
||||
class="stadium-shell"
|
||||
ref="compactInputShell"
|
||||
:class="{
|
||||
'is-multiline': inputIsMultiline,
|
||||
'is-focused': inputIsFocused,
|
||||
'has-text': inputMessage.trim().length > 0
|
||||
}">
|
||||
<input type="file"
|
||||
ref="fileUploadInput"
|
||||
class="file-input-hidden"
|
||||
@ -503,6 +511,8 @@
|
||||
ref="stadiumInput"
|
||||
v-model="inputMessage"
|
||||
@input="handleInputChange"
|
||||
@focus="handleInputFocus"
|
||||
@blur="handleInputBlur"
|
||||
@keydown.enter.ctrl="sendMessage"
|
||||
placeholder="输入消息... (Ctrl+Enter 发送)"
|
||||
class="stadium-input"
|
||||
@ -516,7 +526,7 @@
|
||||
<span v-if="streamingMessage">⏹</span>
|
||||
<span v-else class="send-icon"></span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<transition name="quick-menu">
|
||||
<div class="quick-menu" v-if="quickMenuOpen" ref="quickMenu" @click.stop>
|
||||
<button type="button"
|
||||
@ -606,8 +616,6 @@
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 右侧拖拽手柄 -->
|
||||
<div class="resize-handle" @mousedown="startResize('right', $event)"></div>
|
||||
|
||||
|
||||
@ -1474,39 +1474,70 @@ o-conversations {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.stadium-shell {
|
||||
.stadium-input-wrapper {
|
||||
position: relative;
|
||||
width: min(900px, 94%);
|
||||
border: 1px solid var(--claude-border);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 18px 36px rgba(61, 57, 41, 0.12);
|
||||
padding: 10px 70px;
|
||||
min-height: 56px;
|
||||
transition: padding 0.25s ease, box-shadow 0.25s ease, border-radius 0.25s ease;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.stadium-shell.expanded {
|
||||
padding-top: 18px;
|
||||
padding-bottom: 18px;
|
||||
border-radius: 28px;
|
||||
.stadium-shell {
|
||||
--stadium-radius: 24px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: calc(var(--stadium-radius) * 2.1);
|
||||
padding: 12px 18px;
|
||||
border-radius: var(--stadium-radius);
|
||||
border: 1px solid rgba(15, 23, 42, 0.12);
|
||||
background: #ffffff;
|
||||
box-shadow: 0 18px 46px rgba(15, 23, 42, 0.16);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: padding 0.2s ease, min-height 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.stadium-shell.is-multiline {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
min-height: calc(var(--stadium-radius) * 2.7);
|
||||
border-color: rgba(15, 23, 42, 0.2);
|
||||
box-shadow: 0 26px 70px rgba(15, 23, 42, 0.22);
|
||||
}
|
||||
|
||||
.stadium-shell.is-focused,
|
||||
.stadium-shell.has-text {
|
||||
border-color: rgba(218, 119, 86, 0.35);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(218, 119, 86, 0.18),
|
||||
0 0 14px rgba(218, 119, 86, 0.18),
|
||||
0 18px 40px rgba(15, 23, 42, 0.18);
|
||||
}
|
||||
|
||||
.stadium-shell.is-multiline.is-focused,
|
||||
.stadium-shell.is-multiline.has-text {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(218, 119, 86, 0.2),
|
||||
0 0 18px rgba(218, 119, 86, 0.2),
|
||||
0 26px 68px rgba(15, 23, 42, 0.24);
|
||||
}
|
||||
|
||||
.stadium-input {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
border: none;
|
||||
resize: none;
|
||||
background: transparent;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
font-family: inherit;
|
||||
color: var(--claude-text);
|
||||
padding: 4px 0;
|
||||
min-height: 24px;
|
||||
padding: 0;
|
||||
min-height: 20px;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
transition: height 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: height;
|
||||
}
|
||||
|
||||
.stadium-input:disabled {
|
||||
@ -1520,24 +1551,19 @@ o-conversations {
|
||||
}
|
||||
|
||||
.stadium-btn {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex: 0 0 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--claude-text);
|
||||
font-size: 20px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.stadium-shell.expanded .stadium-btn {
|
||||
bottom: 14px;
|
||||
transition: background 0.2s ease, transform 0.2s ease, margin-top 0.2s ease;
|
||||
}
|
||||
|
||||
.stadium-btn:disabled {
|
||||
@ -1550,12 +1576,10 @@ o-conversations {
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
left: 12px;
|
||||
font-size: 26px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.stadium-btn.send-btn {
|
||||
right: 12px;
|
||||
background: var(--claude-accent);
|
||||
color: #fffaf0;
|
||||
box-shadow: 0 10px 20px rgba(189, 93, 58, 0.28);
|
||||
@ -1589,6 +1613,11 @@ o-conversations {
|
||||
border-left-color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.stadium-shell.is-multiline .stadium-btn {
|
||||
align-self: flex-end;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.file-input-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user