DEV Community

Majd Al Mnayer
Majd Al Mnayer

Posted on

Open Source Contribution: Round 2

For Hacktoberfest, I started my open source contribution journey slowly, working my way up from fixing a small issue in a README.md file to contributing to a large open source project.

For this release, I wanted to take things up a notch. For the first project, I wanted to choose a new big project to work with: react-chatbotify. This project had many issues related to testing, which seemed right because, at the time, our weekly assignment was to write test cases, use mocks, etc., in our own project. For the second project I chose to contribute to, I went back to HuggingFace's chat-ui. This time, I wanted to contribute something much larger than I was used to, ideally something that I hadn't done before.

First Issue

For my first issue, I was tasked with creating several unit test cases for the ChatHistoryService component in the react-chatbotify repo. The issue itself was very well described; it even came with examples of how to do it, where the service was located, and the location of the test folder.

However, I had never mocked nor tested React components using Jest before, so this was an entirely new experience for me. After reading some of the existing tests, I had an idea of how to start and how mocking really worked (because I had to do a lot of it). Halfway through testing, I started to become a little irritated because of the amount of time it took to run all the test cases just to see if my test cases passed or not. But then I remembered that in that same week, Professor Humphrey had suggested, as an optional step for our weekly assignment, that we find out how to run a single test case using Jest, and I did! So I immediately went to the same project's repo and opened a new issue suggesting that a new script be added to allow running a single test file. The repo maintainer then replied and asked me if I could handle that too, which I included in my pull request!

I have to say though, I did not enjoy mocking with Jest. I personally think Jappa is superior in every way when it comes to mocking services and components! However, I did learn something new and tried it out! I also learned that if I am working on an open source project and I see something that can be improved that isn't mentioned as an issue already, I should open it up myself!

Second Issue

For my second issue in Hugging Face's chat-ui, I was tasked with completely revamping the main ChatInput component to make it support some Markdown features such as bolding and italicizing, additionally supporting code blocks, similar to the Claude AI chat input.

When I first accepted this feature, I did not realize that I would have to work with something completely new to me: Svelte, an open-source component-based front-end framework! This came as a shock to me as I opened the ChatInput component and realized that this was a file where there was TypeScript code, HTML elements, and styles all in the same file!

While it did look readable and fairly intuitive to understand, it was still a little terrifying, particularly in such a large project! Nevertheless, I immediately headed to the documentation to understand what was going on and what all of these things meant!

After practicing a little bit, I felt like I was ready to tackle the issue, and I was! I understood that Svelte offers lots of functions to help with mounting and unmounting components, manipulating the DOM, etc.

Now that I knew what I knew, it was time to start. Before I even began to consider how to solve this, I knew how these large projects feel about dependencies, so I went to the package.json file to check and see if there were things that I could work with. And what do you know? highlight.js was already included! What I immediately thought of was perhaps using the marked parser in addition to highlight.js to create and use my code blocks. But after quite a lot of time attempting this, I realized that hand-rolling this solution would not only take a lot of time but would also require a lot of code.

I then tried something different: markdown-it, which is another Markdown parser that offers some other features that I could find useful. I almost succeeded in creating this; however, I still needed a lot of code to listen in on key presses and conditions of when and when not to convert into markdown, etc.

Finally, I wanted to look for a tool that could offer much more help, and so I started looking into WYSIWYG (What you see is what you get) editors. And lo and behold, there were many editors that I could use which could help me accomplish my task.

However, after doing some extensive research, I realized that almost none of them were compatible with Svelte, and those that were did not offer what I was looking for. That is, until I found tiptap, which is a headless wrapper around another WYSIWYG editor called ProseMirror. However, ProseMirror is very low level, and tiptap is super Svelte friendly!

After reading the docs and replacing the existing textarea component with the tiptap editor, I realized, hey, this could actually work!

Now, you know, I know, and everybody knows, nothing in software development is going to work straight out of the box! I started to note some weird patterns, certain pieces of text disappearing, and other issues. That is, until I realized that I had to actually manually configure tiptap to work with my specifications. That meant disabling everything markdown with the exception of the codeblock, bolding, and italicizing. Afterwards, most of the issues started disappearing; however... my code was not colored! highlight.js was not working very well with tiptap, and so my joy faded momentarily... Until I found the incredible tool -- lowlight, which offers virtual syntax highlighting for virtual DOMs and non-HTML things based on highlight.js! And what do you know, lowlight already has an actual extension in tiptap -- @tiptap/extension-code-block-lowlight!

This was great and so easy to work with. I installed the extension, simply plugged it into the tiptap editor settings, and et voila! Everything worked spectacularly!

That is, until... I tried to work out the edge cases... Back and forth navigation, resizing, creating several code blocks in a row...

And so, I started tackling these issues one by one. The issue that arose with back and forth navigation was that the editor was being created every time the same ChatInput component was called, such as when an error happens, resulting in the creation of more than one. Luckily, Svelte offers certain functions that help with unmounting a component appropriately. I also made sure the newly added codeblock would resize and work in all resolutions (if you REALLY want to create a codeblock and code on your phone, that is). Finally, for the most problematic issue of them all... I was, for some reason, unable to create a new code block except at the beginning of the input. As in, if I went to a newline and tried to open a new code block, nothing happened. Apparently, that is the default behavior of tiptap; however, after some extensive research, GitHub post and issue reading, and StackOverflow posts, I figured out that there was a way to customize the codeblock's behaviors! And so I did.

Once I got several codeblocks to work at the same time, the final issue arose! If I wrote some text before a code block and opened a codeblock right after, magically, the text before got inserted into the codeblock. After quite a lot of time and investigation, I realized that tiptap was removing the previously created paragraph element, taking its contents, and feeding it into the codeblock! And so I had to modify the behavior manually one more time by creating an empty element before the creation of the code block. This helped separate the codeblock from anything that came before it!

Finally, my job was done. I'd tested every edge case I could think of, polished my code, and created my pull request!

Conclusion

At the end of this release, I definitely feel like I put much more into my open source contributions in these two issues than the four last issues combined! I've learned many things, such as opening an issue myself if I could see a potential improvement that was not addressed, doing extensive research before deciding on a specific implementation, and docs, docs, docs!

Top comments (0)