You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

合并用户文本与AI建议的多行输入字段排版优化求助

合并用户文本与AI建议的多行输入字段排版优化求助

嘿,我正在做一个把用户输入(userText)和AI生成建议(aiText)结合在一起的输入框,想要让它们看起来像是同一句话的一部分,哪怕用户还没接受AI的建议。

示意图:输入框内用户输入的文本后跟着AI建议,长文本时AI内容在输入框中间换行,显得很割裂

目前这个输入框是单行的,当aiText内容较长时,会在输入框中间尴尬地换行,看起来非常不连贯。理想状态是如果aiText需要换行,应该从输入组件的最左侧开始,而不是从userText结束的位置接着排。

当前实现代码:

<div
  onClick={focusContentEditable}
  className="relative flex justify-start items-start p-3 border border-border
             bg-[#FFFFFF] text-[#1F1F1F] dark:bg-[#2A2A40] dark:text-[#EAEAEA] 
             focus-within:outline focus-within:outline-[#9462fd] 
             cursor-text rounded-2xl text-left w-full h-20 mx-auto 
             overflow-hidden"
>
  {/* Placeholder */}
  {userText.trim() === "" && !isFocused && (
    <span className="text-gray-400 text-xs">Ask me anything...</span>
  )}
  <div className="inline-flex items-baseline">
    <span
      ref={contentEditableRef}
      className="text-xs border-0 outline-none whitespace-nowrap break-words inline-flex min-h-[20px] w-full"
      contentEditable={true}
      suppressContentEditableWarning={true}
      onInput={handleInput}
      onKeyDown={handleKeyDown}
    >
      {/* {userText} */}
    </span>
    <span
      className={`text-xs text-gray-400 dark:text-gray-600 transition-opacity whitespace-normal inline-flex duration-500 ${
        aiText ? "opacity-100" : "opacity-0"
      }`}
      contentEditable={false}
    >
      {aiText.length > 0 && userText.trim() !== "" && <> {aiText}</>}
    </span>
  </div>
</div>

遇到的问题:

  • 输入框是单行设置,没有适配多行文本的换行逻辑
  • aiText内容较长时,会在输入框中间换行,破坏文本整体流畅性
  • 希望输入框能根据内容自动增高,让aiText换行时能从左侧边缘开始

我已经尝试过的方法:

  • 使用contentEditable实现动态文本输入
  • inline-flex容器包裹userTextaiText来保持对齐

期望达到的效果:

  • 输入框能自动增高以支持多行文本
  • aiText如果放不下当前行,换行后从输入框左侧开始
  • userTextaiText视觉上是同一句话的一部分

我的解决方案建议:

针对你的问题,核心是布局和换行规则的限制,调整几下样式就能解决:

1. 移除inline-flex的布局束缚

你现在用inline-flex把用户文本和AI文本绑在同一行内,这会导致AI文本换行时只能从当前位置继续。把外层的<div className="inline-flex items-baseline">改成<div className="block">,让两个span按照正常的文本流排列,这样AI文本就会在用户文本后自然换行,新行直接从输入框左侧开始。

2. 让输入框支持自动高度

把输入框的固定高度h-20改成min-h-[80px](可以根据你的需求调整最小高度),同时去掉overflow-hidden(或者改成overflow-y-auto,避免内容过多时溢出)。这样当内容增多,输入框会自动撑开高度。

3. 修复文本换行的样式

  • 给用户输入的contentEditable元素去掉whitespace-nowrap(这个属性会强制文本不换行,导致内容溢出),换成whitespace-pre-wrap或者保留break-words,确保文本能正常换行。
  • 移除两个span上的inline-flex类,改成inline或者直接去掉,让它们作为行内文本元素正常流动。

调整后的核心代码片段:

<div
  onClick={focusContentEditable}
  className="relative flex justify-start items-start p-3 border border-border
             bg-[#FFFFFF] text-[#1F1F1F] dark:bg-[#2A2A40] dark:text-[#EAEAEA] 
             focus-within:outline focus-within:outline-[#9462fd] 
             cursor-text rounded-2xl text-left w-full min-h-[80px] mx-auto 
             overflow-y-auto"
>
  {/* Placeholder */}
  {userText.trim() === "" && !isFocused && (
    <span className="text-gray-400 text-xs">Ask me anything...</span>
  )}
  {/* 替换inline-flex为block */}
  <div className="block">
    <span
      ref={contentEditableRef}
      className="text-xs border-0 outline-none break-words inline min-h-[20px]"
      contentEditable={true}
      suppressContentEditableWarning={true}
      onInput={handleInput}
      onKeyDown={handleKeyDown}
    >
      {/* {userText} */}
    </span>
    <span
      className={`text-xs text-gray-400 dark:text-gray-600 transition-opacity duration-500 ${
        aiText ? "opacity-100" : "opacity-0"
      }`}
      contentEditable={false}
    >
      {aiText.length > 0 && userText.trim() !== "" && <> {aiText}</>}
    </span>
  </div>
</div>

4. 优化交互体验

记得处理点击AI文本区域时的聚焦逻辑,让焦点自动跳到用户输入的末尾,这样用户可以直接继续输入,不用手动点击用户文本区域。你可以在focusContentEditable函数里调整光标位置:

const focusContentEditable = () => {
  const el = contentEditableRef.current;
  if (el) {
    el.focus();
    // 将光标移动到文本末尾
    const range = document.createRange();
    const sel = window.getSelection();
    range.selectNodeContents(el);
    range.collapse(false);
    sel.removeAllRanges();
    sel.addRange(range);
  }
};

这样调整后,输入框就能支持多行文本,AI文本换行时也会从左侧开始,视觉上和用户文本完全连贯啦!

备注:内容来源于stack exchange,提问作者Zethyst

火山引擎 最新活动