Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

New Component: Chat/Prompt UI #1354

Closed
endigo9740 opened this issue Apr 21, 2023 · 18 comments · Fixed by #1397
Closed

New Component: Chat/Prompt UI #1354

endigo9740 opened this issue Apr 21, 2023 · 18 comments · Fixed by #1397
Assignees
Labels
feature request Request a feature or introduce and update to the project.

Comments

@endigo9740
Copy link
Contributor

endigo9740 commented Apr 21, 2023

Describe what feature you'd like. Pseudo-code, mockups, or screenshots of similar solutions are encouraged!

We have need of a conversational chat UI interface, similar to a user-to-user chat or an AI prompt conversation.

Screenshot 2023-04-21 at 4 28 25 PM

What type of pull request would this be?

New Feature

Any links to similar examples or other references we should review?

No response

@endigo9740 endigo9740 added the feature request Request a feature or introduce and update to the project. label Apr 21, 2023
@endigo9740 endigo9740 self-assigned this Apr 21, 2023
@endigo9740 endigo9740 pinned this issue Apr 21, 2023
@riziles
Copy link

riziles commented Apr 21, 2023

Just noting that SlickGPT is built in Skeleton. Might be some work there that is useful:

https://github.com/ShipBit/slickgpt

@endigo9740
Copy link
Contributor Author

endigo9740 commented Apr 21, 2023

Thanks @riziles! That's exactly the sort of scenario I'm targeting. I'll definitely reference your prior work and of course welcome feedback on what I produce. Watch for updates here on our #contributors channel on Discord when I get to this.

@riziles
Copy link

riziles commented Apr 21, 2023

definitely reference your prior work and of course welcome feedback on what I produce. Watch for updates here on our #

Oh, it's not my work. I have no involvement. I only know about it because the creator shared it in the Skeleton discord showcase.

@Shackless
Copy link

Hi, SlickGPT guy here. @endigo9740 asked me to comment on possible features for this topic. First of all, I think you have it covered pretty much already. As mentioned, I built the entire SlickGPT UI with Skeleton and wasn't missing anything really. There are a couple of "oh yeah, right..." things I can name that were pretty easy to implement with your components but could be part of a preset component composition (that's basically what a "Skeleton Chat UI" would be, right?):

  • Scrolling: You want the chat history to scroll down to new messages when opened initially and if the page is already scrolled to the very bottom but only then. A community contributor implemented this in SlickGPT here
  • The chat input is a text-area but it should resize with content/linebreaks. In several cases, it has to be reset from outside (e.g. on error or when a message was sent). I used textareaAutosizeAction from Svelte Legos for that. UX-wise this is a bit tricky because of the icons (send, share, code...) next to the input. If you resize the input, how do you make these icons not look bad/misplaced?
  • I used the same "one-border-off" trick paired with different colors to visualize the "direction" of chat bubbles like in your screenshot above. This obviously works better if you have these avatar icons next to the border off. It would be nice to have a real "arrow" showing the direction that sticks out of the message bubble if you know what I mean. So basically a "chat balloon" or "chat bubble" variant/component would be cool.
  • The chat messages need to be very customizable. I have different types (user, assistant, system, info...) and all of those might or might not have their own styles and interaction elements (delete buttons, additional info icons and texts etc.). Definitely make then slottable.
  • You need some logic to visualize "live answers" - be it a simple "..." or "Bob is typing" message, or in my case, an entire partial message that is streamed from an outside API. I implemented this with a $liveAnswerStore that I can subscribe to everywhere. While the ChatHistory displays the live answer and a loading spinner, the ChatInput and Toolbar on top disable themselves and so on.
  • Error handling should definitely part of this. There is a lot that can go wrong while receiving messages and a Chat UI component would have to expose / relay any errors occurred so that they can be handled accordingly. Examples: When an answer fails initially, you don't want to add it to the chat history at all. If it fails while streaming, you might want to keep it. Usually, you want to restore the last user input in the ChatInput so that the user doesn't have to type again but if their message could be added to the history correctly, you want to clear the input instead. This part is a bit fiddly and shouldn't be too over-opinionated.
  • The data structure of messages could be tricky. The easy way (and the first one SlickGPT implemented) is that it's just a flat array of messages - basically top to bottom like WhatsApp. UX/UI-wise, this is fine. The whole ChatGPT community is used to horizontal "chat branching" though. So you can edit a message, which creates a branch in the chat and then you can scroll through the different versions and always see further messages after the selected message.

Chat branch 1:
image

Chat branch 2:
image

This is nestable, so further messages in a branch can be branched again which makes this super complex in terms of data and UI - hello, recursion and svelte-self! I guess your first idea would be to skip this entirely and just offer "flat messages". This might be fine but could also miss the LLM/ChatGPT community, so keep it in mind.

That's all that comes to mind right now, I might edit this post later with additional points.

I think to fully implement a chat UI you'd need some stores, a lot of events, properties and slots. Not sure if that would get too opinionated quickly - maybe it's more like a nice OS demo for Skeleton that people can take and use or customize to their needs without forking.
Fun fact: I've already seen forks where people integrated SlickGPT in their app and then they used Skeleton for the rest of their UI as well because it integrated to nicely and was basically already there :)

@endigo9740
Copy link
Contributor Author

endigo9740 commented Apr 26, 2023

This is great @Shackless thank you for this! My hope to have some time to work on this sometime next week. I'll follow up then if I need clarification on anything. But thanks for your detailed post! Tons of great info here!

@Shackless
Copy link

You‘re very welcome, thanks for building Skeleton! Let me know if I can help any more.

@endigo9740 endigo9740 linked a pull request Apr 28, 2023 that will close this issue
@endigo9740
Copy link
Contributor Author

endigo9740 commented May 2, 2023

@riziles @Shackless and anyone else who wishes to test this, the new PR is pending review now:

Here's the preview URL, which should cover everything available for this:
https://skeleton-docs-git-feat-chat-prompt-skeleton-labs.vercel.app/elements/chat

I know some folks were expecting this to be a fully fledge component. However, I think that would be misguided approach here, given chat implementation can vary WILDLY from app to app. Conversation chat between users and AI prompts can have vastly different requirements. Instead I've provided a simpler, but more flexible approach - I've opted to create a new Tailwind > Blocks > Chat section which covers how to implement a basic chat-like interface using the resources provided by Skeleton and Tailwind, with some simple logic examples to boot.

However, I'd welcome feedback on what is provided here. However, just keep in mind the goal is to showcase how to get started, how to utilize Skeleton provided primitives, and less about trying to provide a turnkey solution for every use case - which would be an unrealistic goal.

@RonB
Copy link

RonB commented May 4, 2023

Thank you for this. I will migrate my own chat into this but a few things come to mind.

  1. The responses for the user will most likely come from chat models like ChatGPT. The responses are formatted mostly in markdown and can als contain code in JSON htmL by using the ```json etc. In my solution I use the SvelteMarkdown component. Also remember that code injection here is lurking in the dark. Sanitising the response is therefor inevitable. MDSVEX is also useful for this although i realise that this is all beyond the scope of a ui framework.
  2. The other thing is that the textarea input should be dynamically resize with the content I think. The textarea is used to paste longer texts for example to summarise.

Thanks for the effort again. @endigo9740 When will you release this feature?

Ronald

@Sarenor
Copy link
Contributor

Sarenor commented May 4, 2023

It's probably going to be released in the next update which would be Tuesday next week.

@Shackless
Copy link

@RonB FYI: I had problems with SvelteMarkdown and MDSVEX when I tried to parse an incomplete message (the "live answer" that is streamed from the OpenAI API) because they both don't like incomplete/invalid Markdown as input. The one lib that just doesn't care about this (of course with some caveats) is snarkdown. It seems abandoned but does the job pretty well. Just in case you'll run into that...

@endigo9740
Copy link
Contributor Author

endigo9740 commented May 4, 2023

Hey @RonB, what Sarenor said, next release is this upcoming Tuesday. The way I did this there's no new features or dependencies, just showing what's possible with the tools at hand. I suppose the scripts provided are new, but nothing should prevent you from using this right now if you have access to the doc page.

We've left it open ended on how users implement this, given this will vary for every use case (markdown for you, json for another person, etc). Likewise I noted the textarea expansion, but I don't have a great solution for that YET. However, I do expect this section to evolve over time. That might be a great candidate for something to add.

@RonB
Copy link

RonB commented May 4, 2023

Hi Chris,

Thank you. I solved that a bit dubious but it works:

	// adjust the size of the message box
	function keyup(event: KeyboardEvent) {
		event.target.style.height = '15px';
		event.target.style.height = 25 + event.target.scrollHeight + 'px';
	}
<textarea
	bind:this={text_input}
	on:keyup={keyup}
	placeholder="How can i help you?"
/>

@endigo9740
Copy link
Contributor Author

That's interesting @RonB, I'll try this out. Thanks for sharing!

@endigo9740
Copy link
Contributor Author

Unfortunately this has some bugginess when a paragraph is dropped in.

Screenshot 2023-05-04 at 12 01 03 PM

I do think this is something we should offer a solution for, so I'll aim to circle back to this in the near future. In the meantime, @Shackless offered this solution in his post above. It might serve as a temporary solution if you don't mind an extra dependency in your project:

The chat input is a text-area but it should resize with content/linebreaks. In several cases, it has to be reset from outside (e.g. on error or when a message was sent). I used textareaAutosizeAction from Svelte Legos for that.

@RonB
Copy link

RonB commented May 4, 2023

Yes I saw that, i added the on:input event and the paragraph drops works well. I did not want the extra dependency. Renamed the keyup function to "resize" so:

function resizeTextarea(event: any) {
	event.target.style.height = '15px';
	event.target.style.height = 25 + event.target.scrollHeight + 'px';
}

and

<textarea
	bind:this={text_input}
	bind:value={text}
	on:keydown={keydown}
	on:input={resizeTextarea}
	on:keyup={resizeTextarea}
	placeholder="How can i help you?"
/>

Maybe more element events or external events should call the function as well.

@Shackless
Copy link

Shackless commented May 4, 2023

The Legos implementation might be a good start if you want to roll your own. I didn’t try but yours doesn’t reduce the size if you delete text or clear the bound value from outside, does it?

@RonB
Copy link

RonB commented May 8, 2023

When clearing the text-area using the keyboard it works fine. The keyup event should handle that.
Clearing from the outside is not covered. But I don't think that scenario is covered by svelte-legos solution either. The svelte-legos takes in account the styles, linespacing, padding, initial styles / classes etc. I am not disputing that that is a better solution but the tradeoff is another dependency.

@endigo9740 endigo9740 unpinned this issue May 8, 2023
@cassepipe
Copy link

I personally used this approach : https://svelte.dev/repl/40f4c7846e6f4052927ff5f9c5271b66?version=3.6.8
It works ok for me as far as I can tell

This is based on this clever hack where you mirror your textarea content in a <pre> tag :
https://alistapart.com/article/expanding-text-areas-made-elegant/

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
feature request Request a feature or introduce and update to the project.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants