Задать вопрос
@truesup

Как реализовать форму ввода как в веб-версии chatGPT?

Делаю проект в связке next.js 16 + tailwind v4. Это просто обертка над llm под капотом (если не углубляться). На днях пытался сделать форму ввода как в веб-версии chatGPT. Изображения прикрепил ниже, вживую тут.

6915cea4893c8285522825.png
6915ceab9c9f3894766437.png
6915ceb358b25502435903.png

В какой-то момент пришел к выводу, что наилучшим решением будет делать гридами. Ещё понял, что нужно заводить два как бы кадра, между которыми будет происходить анимация, пускай называются до/после. Для флага, который переключает эти кадры заведем состояние isExpanded. Теперь получается вот такая разметка, если рассмотреть ее по грид-ареа:

  1. первый кадр (isExpanded === false) --> у нас в грид сетке 1 ряд, 3 колонки, выглядит вот так:

    | button | textarea | button button |

    это повторяет разметку обычной формы ввода, которую сейчас пихают в ai приложения. внутри формы слева-направо: кнопка с иконкой, поле ввода (textarea, contenteditable элемент), контейнер, внутри которого еще две кнопки с иконкой.
  2. второй кадр (isExpanded === true) --> в грид сетке становится 2 ряда и 3 колонки, выглядит вот так:

    | textarea | textarea | textarea |
    | button | тут пусто| button button |

    это уже разложенное состояние. в целом все понятно - сверху широкое поле ввода, а снизу остается ряд с кнопками для взаимодействия.


Все хорошо, но что должно триггерить изменение значения у isExpanded? Кажется, что все просто, нам ведь известна ширина элемента, она стабильна, потому что грид-ареа textarea стоит статично между двумя блоками, а родитель ограничен по ширине. То есть когда контент в textarea достигает границы ширины, то происходит перенос строки, далее textarea вырастет в высоту в два раза, потому что начнется вторая строка. Будто можно брать этот момент за флаг, который будет переключать isExpanded в состояние true.

Тут не знаю, стоит ли пояснять, но по начальной задумке одна строка в текстаре равна по высоте всем остальным элементам формы (как в chatGPT), все стоит ровно, горизонтально и классно.


Тут случится проблема - isExpanded переключилось и, соответственно, вся форма поменяла грид-сетку на состояние второго кадра и ширина грид-ареа textarea увеличилась, потому что элемент теперь в верхнем ряду, а значит к нему добавилась ширина других двух колонок грида. Вот тут самый прикол - при вводе следующего символа состояние isExpanded снова изменится, потому что теперь первая строка символов норм помещается в этот ряд, а потом еще раз так, и еще раз. И с каждым таким переключение isExpanded будет переключаться, форму будет переключать между первым и вторым кадром туда-сюда. В какой-то момент предел будет достигнут и форма статично встанет во второй кадр. Такое не пойдет.

Вот тут кажется логичным и простым решением зафиксировать флаг isExpanded в состояние true в самый первый момент изменения высоты textarea, чтобы форма зафиксировалась во втором кадре и дальше такой и оставалась, а при удалении символов можно просто сбрасывать обратно первый кадр, когда удалены все до последнего символы в поле ввода.

Вроде бы схема рабочая, подумал я и пошел делать. Далее встал вопрос анимации, потому что до текущего момента это было просто слайд-шоу из двух кадров. А анимировать я не сумел, только очень коряво. Я пробовал анимировать гриды, потому что знал, что вроде как их можно анимировать нативным css, но выходило очень некрасиво, кнопки могли терять форму (сжаться/развернуться) в процессе, текст перескакивал невнятно и очень неуверенно. Я далее попробовал поиграться с паддингами у textarea, вроде выровнял таким образом, что сдвига текста сильно не видно и при этом сама форма выглядит органично.

В целом результат получился на 5/10. Очень странные и смешаные чувства испытал я в этот момент и пошел искать другие решения. Вспомнил про библиотеку framer, которая умеет анимировать изменения лейаута. Зашел в документацию, прочитал, подумал, начал делать. В итоге пришлось все равно долго разбираться в настройке анимации, хотя сперва думал, что минут 30 это максимум займет. Настроил изменение лейаута через этот же пропс layout на каждом элементе и вроде как сработало. Выглядело лучше, чем анимация на чистом css, но все равно очень тупо и неказисто.

Вот тут и встает вопрос, а как можно реализовать форму ввода, которая будет повторять поведение формы ввода как в веб-версии chatGPT? Если у кого-то есть хорошие идеи, предложения, рекомендации, примеры кода, какие-то пояснения, хоть что-то - дайте знать, пожалуйста.

Ещё важный момент - поле ввода, именно куда вводится текст будущего промпта, может быть реализовано по сути либо элементом textarea, либо же contenteditable div, потому что оба имеют возможность расти в высоту. Я и попробовал, но результат был плюс-минус таким же. Большую часть времени я все-таки тестил это дело с textarea внутри, потому что в chatGPT используется именно contenteditable компонент, потому что есть возможность вставлять туда изображения и прикреплять вложения. В мои задачи это не входило, потому что я не планирую добавлять этот функционал, хоты бы пока. Поэтому самым простым решением и было вставить textarea. Тут ещё просто для справки стоит сказать, что в gpt все-таки учтена адаптивность, поэтому не помню на каком разрешении contenteditable элемент меняется на обычный textarea.
  • Вопрос задан
  • 59 просмотров
Подписаться 1 Простой Комментировать
Помогут разобраться в теме Все курсы
  • Яндекс Практикум
    Профессиональная вёрстка на HTML и CSS
    3 месяца
    Далее
  • Stepik
    Основы HTML и CSS
    2 недели
    Далее
  • OTUS
    HTML/CSS
    3 месяца
    Далее
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы