psugihara / FreeChat

llama.cpp based AI chat app for macOS
https://www.freechat.run
MIT License
402 stars 34 forks source link

Text selection fix (WKWebView) #64

Closed finallyblueskies closed 2 months ago

finallyblueskies commented 2 months ago

This PR adds a view that renders chat messages by:

This implementation enables free text selection, thereby resolving https://github.com/psugihara/FreeChat/issues/22.

This is my first open-source PR and it's also my first time working with SwiftUI. I would greatly appreciate any suggestions for tackling the above problem, as well as for improving the code or anything else.

To-do list:

vercel[bot] commented 2 months ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
free-chat ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 16, 2024 4:58am
finallyblueskies commented 2 months ago

Doing some testing, even with a fixed height to simulate precalculated dimensions there is a flash of unstyled content. It seems that most of the overhead is parsing and rendering markdown into HTML, noticeable even on my M3:

https://github.com/psugihara/FreeChat/assets/14274086/4d349a51-1609-4730-a3c9-ee78bb3a9571

I think that the only way this approach would move forward is if:

psugihara commented 2 months ago

@psugihara is OK with the drop in responsiveness as the cost for free text selection - perhaps this can be masked, such as an animation to to fade in the views once content is rendered? Might look pleasing while feeling almost as responsive as the previous approach

yeah that seem ok to me if we can get the styles aligned exactly. nice work so far, excited to dig into the diff a bit more.

finallyblueskies commented 2 months ago

I'm not a fan of the complexity of making a WKWebView work, there lots of issues to address before it is production ready.

All of this does not make me confident that rendering in a web view is a sustainable approach.

With this in mind I'm experimenting with more native solutions. swift-markdown-ui renders its content in discrete Text(NSAttributedString) blocks. It may be possible to update the library upstream to render multi-paragraph content in a single Text block, or create something specifically on FreeChat.

finallyblueskies commented 2 months ago

Exploring native solutions I found that to implement more complex visual elements like tables, code blocks, or images in SwiftUI, it becomes challenging because the framework doesn't support selection across different views easily. As a result, I doubled down on a WKWebView, enabling the entire conversation thread to be rendered using HTML and CSS. This approach introduces an intermediate layer between the conversation view and the app's other components, requiring a bridge between the web view’s event handling and SwiftUI. This also means that we have two different UI systems in place, which may cause maintenance difficulties later on.

Still, while complex this approach offers significant advantages:

You can observe the interaction in the video below, where the left side is current FreeChat main:

https://github.com/psugihara/FreeChat/assets/14274086/100968c9-ef2a-4b70-b737-eba051df662b

I understand this is a pretty opinionated approach, I will merge it for my FreeChat fork, @psugihara let me know if you're OK with it for FreeChat, in which case I will keep this PR open and complete it here.

finallyblueskies commented 2 months ago

Further exploration of a HTML rendering approach has led me to understand that rendering the whole conversation in HTML makes for a nice, fast prototype but quickly becomes very complex when we need to start re-introducing previous UI elements, functionality and actually efficient rendering. A whole SwiftUI <-> WKWebView API starts to take shape, with lots of duplication and indirection, and while this is a fun exercise it is a completely overblown solution to the problem of having freely selectable messages. I am closing this PR in favor of https://github.com/psugihara/FreeChat/pull/65