<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: WangGithub0</title>
    <description>The latest articles on Forem by WangGithub0 (@wanggithub0).</description>
    <link>https://forem.com/wanggithub0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1155781%2F7ac42bb2-23c5-4998-8114-ca429c9e53bd.jpeg</url>
      <title>Forem: WangGithub0</title>
      <link>https://forem.com/wanggithub0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wanggithub0"/>
    <language>en</language>
    <item>
      <title>Contributing to Open Source Project ChatCraft</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Thu, 18 Apr 2024 14:35:02 +0000</pubDate>
      <link>https://forem.com/wanggithub0/contributing-to-open-source-project-chatcraft-1dh3</link>
      <guid>https://forem.com/wanggithub0/contributing-to-open-source-project-chatcraft-1dh3</guid>
      <description>&lt;p&gt;This semester has been a whirlwind of coding, collaboration, and learning as I dove headfirst into the world of open source development with &lt;a href="https://github.com/tarasglek/chatcraft.org"&gt;ChatCraft&lt;/a&gt;. My journey was marked by significant contributions, including 14 pull requests (PRs), the development of 4 major features, and the squashing of numerous bugs. Here's a closer look at what I've accomplished and the invaluable lessons I've learned along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contributing to Open Source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open source projects like ChatCraft are the heart of the developer community. They thrive on the contributions of individuals who are willing to share their time and skills to improve the software for everyone. This semester, I was proud to submit 14 PRs to the project. Each PR was an opportunity to refine my coding abilities, engage with other developers, and make a tangible impact on the platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4whi2ms17a9sj707xadn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4whi2ms17a9sj707xadn.png" alt="Image description" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Major Feature Developments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most exciting aspects of my work this semester was developing and shipping four significant features that enhanced the functionality of ChatCraft:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package and Ship TypeScript2OpenAI on npm&lt;/strong&gt;
I took on the challenge of packaging and shipping a TypeScript wrapper for the OpenAI API. This involved ensuring the package was robust, well-documented, and ready for community use through npm, the Node package manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3r47ylh2ae3i6bh0dump.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3r47ylh2ae3i6bh0dump.png" alt="Image description" width="550" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsp3l7ainfim3wcd9olz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsp3l7ainfim3wcd9olz1.png" alt="Image description" width="792" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google OAuth Authentication&lt;/strong&gt;
Implementing Google OAuth was a major step forward in making ChatCraft more accessible and secure. By adding Google Login according to OAuth 2.0 for server-side web applications, users can now enjoy a streamlined sign-in process with the added security of Google's authentication system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmnotmznxf1b76soppg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmnotmznxf1b76soppg3.png" alt="Image description" width="790" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fteu99s18xigw8k0iqxbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fteu99s18xigw8k0iqxbc.png" alt="Image description" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk7wrsnqa4yhjgss3lla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk7wrsnqa4yhjgss3lla.png" alt="Image description" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enable Python and Ruby Support Via Wasi&lt;/strong&gt;
Expanding the platform's language support, I enabled Python and Ruby coding through WebAssembly System Interface (WASI). This feature opens up new possibilities for developers who prefer or require these languages for their projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4m1t5xq5rratxmimgvjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4m1t5xq5rratxmimgvjd.png" alt="Image description" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create Shared Feed&lt;/strong&gt;
To foster a sense of community and collaboration, I created a shared feed feature. This allows users to share their experiences, tips, and tricks with the wider ChatCraft community, enhancing the collective knowledge base.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frn2fyd7r123551egbms1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frn2fyd7r123551egbms1.png" alt="Image description" width="602" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funb7xm7wu1xjuihdccct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funb7xm7wu1xjuihdccct.png" alt="Image description" width="778" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug Fixes and Problem-Solving&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No software is without its bugs, and part of my role this semester was to identify and fix various issues within ChatCraft. Tackling bugs is like a puzzle; it requires patience, a keen eye for detail, and a deep understanding of the codebase. Each bug I fixed was a lesson in troubleshooting and perseverance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lessons Learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Throughout this semester, my work with ChatCraft has been more than just a series of tasks; it has been a comprehensive learning experience. Here are some key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaboration is Key&lt;/strong&gt;: Open source development is a team sport. Working with others taught me the importance of communication, mutual respect, and shared goals.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous Learning&lt;/strong&gt;: The tech world is ever-evolving, and staying current means being a lifelong learner. Each new feature and bug fix was a chance to learn something new.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Problem-Solving Mindset&lt;/strong&gt;: Developing features and debugging requires a problem-solving mindset. I've learned to approach challenges methodically and creatively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Joy of Sharing&lt;/strong&gt;: By contributing to an open source project, I've experienced the joy of giving back to the community and the satisfaction of seeing my work used by others.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In conclusion, my semester with ChatCraft has been an enriching chapter in my development journey. I've grown as a coder, a collaborator, and a member of the open source community. I'm excited to take these lessons with me into future projects and continue contributing to the vibrant world of open source software.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Optimize feed in ChatCraft</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Sat, 13 Apr 2024 00:38:49 +0000</pubDate>
      <link>https://forem.com/wanggithub0/optimize-feed-in-chatcraft-56bo</link>
      <guid>https://forem.com/wanggithub0/optimize-feed-in-chatcraft-56bo</guid>
      <description>&lt;p&gt;Last two week, I created share feed for ChatChaft in CloudFlare, this week, I tried to optimize it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refine the Feed Icon&lt;/strong&gt;&lt;br&gt;
At first, I just copied the URL after user clicking the Feed Icon on the header, but it is not a good way to show it to users. So I change it to open a new tab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      const currentUrl = window.location.href;
      const parsedUrl = new URL(currentUrl);
      const userFeedUrl = `${parsedUrl.origin}/api/share/${user.username}/feed.atom`;
      window.open(userFeedUrl, "_blank");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Endeavor to Parse HTML with Linkedom&lt;/strong&gt;&lt;br&gt;
Last week, I used cheerio to parse HTML on cloudflare, but it was published 2 years ago and hasn't seen updates since that.&lt;br&gt;
So I try to find a better one library. I turn to use linkedom to parse HTML on cloudflare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      const document = new DOMParser().parseFromString(chatContent, "text/html");

      const getTitle = document.querySelector('meta[property="og:title"]');
      const title = getTitle ? getTitle.getAttribute("content") : "No Title";

      const getSummary = document.querySelector('meta[property="og:description"]');
      const summary = getSummary ? getSummary.getAttribute("content") : "No Summary";

      const getUrl = document.querySelector('meta[property="og:url"]');
      const url = getUrl ? getUrl.getAttribute("content") : "No URL";
      const preContentElement = document.querySelector("pre");
      const preContent = preContentElement ? preContentElement.textContent : "";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;here I check getTitle first which can solve the some document don't have title sometimes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
I think I've finished the feed feature and we almost finish this semester, next week I'll make a conclusion of what I've done this semester on ChatCraft project.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>feed</category>
      <category>programming</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Show Feed to ChatCraft users</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Fri, 05 Apr 2024 19:28:52 +0000</pubDate>
      <link>https://forem.com/wanggithub0/show-feed-to-chatcraft-users-512</link>
      <guid>https://forem.com/wanggithub0/show-feed-to-chatcraft-users-512</guid>
      <description>&lt;p&gt;After &lt;a href="https://dev.to/wanggithub0/generating-a-custom-rss-feed-with-cloudflare-workers-a-step-by-step-guide-2m34"&gt;Generating a Custom RSS Feed with Cloudflare Workers&lt;/a&gt;, I introduced a new feature that allows users to share content effortlessly this week. This addition comes in two distinct implementations: a share feed button on the header and within the shareModal. Let's delve into these updates and discuss a crucial piece of advice from our collaborator regarding URL manipulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Share Feed Button on the Header&lt;/strong&gt;&lt;br&gt;
The first update I did is the integration of a share feed button prominently placed on the header of our website. This button is designed to be intuitive, ensuring that users can easily share the feed with just a click. The implementation was thoughtfully considered to enhance visibility and accessibility, encouraging more users to engage with and disseminate our content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7n9dsvf73htszilj7gr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7n9dsvf73htszilj7gr.png" alt="Image description" width="800" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ShareModal Integration&lt;/strong&gt;&lt;br&gt;
The second place where I've added the share feed functionality is within our shareModal. This modal pops up when a user opts to share content, providing them with various options to disseminate the feed across different platforms. The integration within the shareModal is aimed at offering users a seamless sharing experience, directly from the interface where they're most engaged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbtj9p93801o2yesy62b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbtj9p93801o2yesy62b.png" alt="Image description" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Note on URL Manipulation&lt;/strong&gt;&lt;br&gt;
During the development of these features, an important piece of advice came from my professor @humphd. The concern was raised regarding the manipulation of URLs as strings, specifically in the context of extracting the base URL using &lt;code&gt;const baseUrl = url.split("/api/")[0];&lt;/code&gt;. This approach, while functional, can lead to potential issues and is not considered best practice.&lt;/p&gt;

&lt;p&gt;The Recommended Approach&lt;br&gt;
@humphd suggests utilizing the new URL() constructor for handling URLs. This method allows for a more robust and error-proof way of parsing and constructing URLs. By using new URL(), developers can easily access different parts of the URL, such as the pathname, and manipulate them safely. This approach not only enhances code reliability but also aligns with modern JavaScript best practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
The integration of share feed functionality marks a significant step forward in our platform's evolution. I'm excited to see how these changes will positively impact our community and look forward to further innovations that enhance user engagement on our platform.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>feed</category>
      <category>cloudflarechallenge</category>
    </item>
    <item>
      <title>Generating a Custom RSS Feed with Cloudflare Workers: A Step-by-Step Guide</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Fri, 29 Mar 2024 15:16:05 +0000</pubDate>
      <link>https://forem.com/wanggithub0/generating-a-custom-rss-feed-with-cloudflare-workers-a-step-by-step-guide-2m34</link>
      <guid>https://forem.com/wanggithub0/generating-a-custom-rss-feed-with-cloudflare-workers-a-step-by-step-guide-2m34</guid>
      <description>&lt;p&gt;In the ever-evolving landscape of web development, the need for personalized content delivery mechanisms remains a constant. RSS feeds, a time-tested method for distributing content updates, can be tailored to deliver user-specific content, enhancing the user experience. This blog post outlines a practical approach to generating a custom RSS feed for the 20 latest shares of a user in &lt;a href="https://github.com/tarasglek/chatcraft.org"&gt;ChatCraft&lt;/a&gt;, leveraging Cloudflare Workers for a serverless solution. We'll also touch on adding CSS to our feed for improved styling.&lt;/p&gt;

&lt;p&gt;Why Cloudflare Workers?&lt;br&gt;
Cloudflare Workers provide a powerful, serverless execution environment that allows you to run code closer to your users, improving performance and reducing latency. This makes them an ideal choice for generating dynamic content like RSS feeds.&lt;/p&gt;

&lt;p&gt;Step 1: Setting Up Your Cloudflare Worker&lt;br&gt;
First, ensure you have a Cloudflare account and have set up Workers. You'll need to create a new Worker script, which will be the backbone of our RSS feed generator.&lt;/p&gt;

&lt;p&gt;Step 2: Fetching the Latest Shares&lt;br&gt;
Our goal is to generate an RSS feed for the 20 latest shares of a user. To achieve this, we'll list objects in a specific bucket (or directory) that corresponds to the user's shared content. Here's a simplified version of how you might fetch this data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { objects } = await CHATCRAFT_ORG_BUCKET.list({ prefix: `${user}/` });
  const sortedObjects = objects.sort((a, b) =&amp;gt; b.uploaded.getTime() - a.uploaded.getTime());
  const recentObjects = sortedObjects.slice(0, 20);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Parsing Content Without DOMParser&lt;br&gt;
Since Cloudflare Workers do not support DOMParser, we'll use regular expressions to extract necessary information from the HTML content of each share. This includes the title, description, and any other metadata required for our RSS feed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { load } from "cheerio";
const chatContent = await chatData.text();
      const $ = load(chatContent);

      const title = $('meta[property="og:title"]').attr("content") || "No Title";
      const summary = $('meta[property="og:description"]').attr("content") || "No Summary";
      const url = $('meta[property="og:url"]').attr("content") || "No URL";
      const id = url.split("/").pop() || "No ID";
      const preContent = $("pre").text();
      const dateMatch = preContent.match(/date:\s*(.+)/i);
      const date = dateMatch ? new Date(dateMatch[1]) : new Date();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Generating the RSS Feed&lt;br&gt;
With the data extracted, we can now populate our RSS feed. We'll use a library like Feed to create the feed, adding each share as an item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const feed = new Feed({
  title: `User Feed for ${user}`,
  // Other feed properties...
});

// Add items to the feed
objects.forEach(object =&amp;gt; {
  // Extract and add data for each object
  feed.addItem({
    title: title,
    // Other item properties...
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Adding CSS to the Feed&lt;br&gt;
To add CSS to our RSS feed, we'll include a link to an XSL stylesheet in our XML output. This can enhance the presentation of the feed when viewed directly in a browser. Also feed doesn't support add css so I add it by myself and delete the second XML declaration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Remove the first line (second XML declaration) if it exists
  const lines = feedXml.split("\n");
  if (lines[0].startsWith("&amp;lt;?xml")) {
    lines.shift();
    feedXml = lines.join("\n");
  }

  feedXml =
    `&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;\n&amp;lt;?xml-stylesheet type="text/xsl" href="${xsltUrl}"?&amp;gt;\n` +
    feedXml;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you have an XSL stylesheet (style.xsl) hosted and accessible at the specified URL.&lt;/p&gt;

&lt;p&gt;Step 6: Deploying the Feed&lt;br&gt;
Finally, we'll store our generated RSS feed in the Cloudflare Worker's storage, making it accessible to users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await CHATCRAFT_ORG_BUCKET.put(feedKey, new TextEncoder().encode(feedXml), {
  httpMetadata: {
    contentType: "application/atom+xml",
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conclusion&lt;br&gt;
By leveraging Cloudflare Workers, we've created a dynamic, user-specific RSS feed that fetches the 20 latest shares, parses content without DOMParser, and includes custom CSS for styling. This approach demonstrates the flexibility and power of serverless functions for personalized content delivery. Whether you're a blogger, developer, or content creator, this method opens up new possibilities for engaging your audience with tailored feeds.&lt;/p&gt;

&lt;p&gt;Remember, while regular expressions are a quick solution for parsing HTML content, they may not be foolproof for complex HTML structures. Always test thoroughly and consider alternative parsing methods if your use case requires it.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>rss</category>
      <category>cloudflare</category>
      <category>typescript</category>
    </item>
    <item>
      <title>My Journey with Cloudflare Workers: Learning and Implementing Feeds</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Fri, 22 Mar 2024 23:11:06 +0000</pubDate>
      <link>https://forem.com/wanggithub0/my-journey-with-cloudflare-workers-learning-and-implementing-feeds-296o</link>
      <guid>https://forem.com/wanggithub0/my-journey-with-cloudflare-workers-learning-and-implementing-feeds-296o</guid>
      <description>&lt;p&gt;In the ever-evolving landscape of web development, staying abreast of the latest technologies and trends is crucial. My recent adventure into the world of Cloudflare Workers provided me with a unique opportunity to delve into serverless functions and explore their capabilities in managing and generating content feeds for ChatCraft[&lt;a href="https://chatcraft.org/"&gt;https://chatcraft.org/&lt;/a&gt;]. This blog post chronicles my journey, the challenges I faced, the solutions I devised, and the invaluable lessons I learned along the way.&lt;/p&gt;

&lt;p&gt;Introduction to Cloudflare Workers&lt;br&gt;
Cloudflare Workers offer a compelling model for executing JavaScript (and other languages compiled to WebAssembly) on Cloudflare's global network. This serverless platform allows developers to deploy code closer to users, reducing latency and improving performance for global applications. My project's goal was to leverage Cloudflare Workers to dynamically generate and update content feeds, a common requirement for content-driven websites.&lt;/p&gt;

&lt;p&gt;The Challenge: Dynamic Feed Generation and Updates&lt;br&gt;
The core challenge of my project was to implement a system that could not only generate content feeds (such as RSS or Atom feeds) on the fly but also update these feeds as new content became available. This task involved parsing and manipulating XML/HTML structures, efficiently managing data storage and retrieval, and ensuring the system could scale to handle updates without excessive resource consumption.&lt;/p&gt;

&lt;p&gt;Step 1: Learning About Feeds&lt;br&gt;
Before diving into the implementation, I dedicated time to understanding the structure and standards of content feeds. RSS and Atom feeds are XML-based formats used to publish frequently updated information, such as blog entries or news headlines. Each feed contains a series of items, which represent individual pieces of content. Grasping the intricacies of these formats was crucial for accurately generating and manipulating feed data.&lt;/p&gt;

&lt;p&gt;Step 2: Implementing Feed Generation&lt;br&gt;
With a solid understanding of feed structures, I began implementing feed generation using Cloudflare Workers. The first step was to fetch content data stored in Cloudflare's R2 storage, which involved asynchronous operations to retrieve and parse stored content. I utilized cheerio, a fast, flexible, and lean implementation of core jQuery designed specifically for the server, to parse and manipulate HTML content on the fly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const chatContent = await chatData.text();
const $ = load(chatContent);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Dynamically Updating Feeds&lt;br&gt;
The next challenge was to update these feeds dynamically as new content was added. This required efficiently identifying new content and inserting it into the existing feed structure. I developed a function to fetch new content items, convert them into the appropriate XML format, and prepend them to the existing feed, ensuring that the most recent content always appeared first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const newEntryXml = chatItemToXml(newEntry); // Serialize newEntry to XML string
$('feed').prepend(newEntryXml); // Prepend the new entry XML string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lessons Learned&lt;br&gt;
Throughout this project, I learned several valuable lessons:&lt;/p&gt;

&lt;p&gt;Serverless Functions Are Powerful: Cloudflare Workers are incredibly powerful for tasks like dynamic content generation, offering low latency and high performance.&lt;br&gt;
Efficiency Is Key: Efficiently managing data retrieval and manipulation is crucial, especially when working with serverless functions where execution time is limited.&lt;br&gt;
Flexibility of JavaScript Libraries: Libraries like cheerio can be incredibly flexible and useful for server-side DOM manipulation, making tasks like feed generation and updates much simpler.&lt;br&gt;
Conclusion&lt;br&gt;
Building a dynamic feed generation and update system with Cloudflare Workers was a rewarding experience that pushed me to learn and adapt. This project not only enhanced my understanding of content feeds and serverless architectures but also demonstrated the power of modern JavaScript libraries in simplifying complex tasks. As I continue to explore the capabilities of Cloudflare Workers, I'm excited about the potential to tackle more challenging projects and further expand my web development skills.&lt;/p&gt;

&lt;p&gt;Embarking on this project was a journey of discovery and learning. I hope sharing my experience inspires others to explore the capabilities of Cloudflare Workers and the fascinating world of dynamic content generation.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>feed</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Running Ruby Scripts in ChatCraft with Python-WASI</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Fri, 15 Mar 2024 21:17:55 +0000</pubDate>
      <link>https://forem.com/wanggithub0/running-ruby-scripts-in-chatcraft-with-python-wasi-25pd</link>
      <guid>https://forem.com/wanggithub0/running-ruby-scripts-in-chatcraft-with-python-wasi-25pd</guid>
      <description>&lt;p&gt;This week I began to integrate Python support into &lt;a href="https://github.com/tarasglek/chatcraft.org/blob/b12cd3056c5bbc0d551db846d04f8e00ead45739/src/lib/ChatCraftFunction.ts#L162"&gt;ChatCraftFunction&lt;/a&gt;, a feature that promised to enhance the platform's versatility and appeal. However, after careful consideration and exploration, we changed it.&lt;/p&gt;

&lt;p&gt;The Initial Python Proposal&lt;br&gt;
The idea to support Python in ChatCraftFunction was driven by the language's popularity and its wide range of applications, from web development to data analysis. Python's simplicity and readability made it an attractive option for our platform, aiming to empower users with the ability to run Python scripts seamlessly within ChatCraft. I tried to revise part of ChatCraftFunction and found I need create a package which is similar to typescript2openai and require user to add the parameter types into python comments also considered about adding a 'language' into function DB.&lt;/p&gt;

&lt;p&gt;A Shift in Focus&lt;br&gt;
As the development process unfolded, discussions among team members and feedback from the community prompted a reevaluation of our priorities. The niche nature of ChatCraft functions at the time, coupled with the desire to concentrate on other features such as custom OpenAI-compatible providers and sharing improvements, led to a strategic pivot. The decision was made to hold off on Python integration, opting instead to wait and see how demand for such functionality would evolve over time.&lt;/p&gt;

&lt;p&gt;Embracing Ruby&lt;br&gt;
In this period of reflection and exploration, I tested more wasi file and add Ruby into the run-code. Ruby, known for its elegance and productivity, offered a compelling alternative that aligned well with our platform's goals and the needs of our community. The journey of integrating Ruby into ChatCraftFunction has been similar to integrate Python I did last weel&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
The development of ChatCraftFunction is a continuous journey of learning, adapting, and evolving. As ChatCraft continues to grow and evolve, we remain excited about the possibilities that lie ahead and are committed to exploring new ways to empower our users.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>wasi</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Running Python Scripts in ChatCraft with Python-WASI</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Fri, 08 Mar 2024 18:14:47 +0000</pubDate>
      <link>https://forem.com/wanggithub0/running-python-scripts-in-chatcraft-with-python-wasi-3lcm</link>
      <guid>https://forem.com/wanggithub0/running-python-scripts-in-chatcraft-with-python-wasi-3lcm</guid>
      <description>&lt;p&gt;In the ever-evolving landscape of web development, the ability to run various programming languages directly in the browser has opened up a lot of opportunities for creating more dynamic and interactive web applications. One such advancement is the integration of Python scripts into web applications, a feature that has long been desired by developers who prefer Python for its simplicity and power. Today, I'm excited to share how I've achieved this milestone in &lt;a href="https://github.com/tarasglek/chatcraft.org"&gt;ChatCraft&lt;/a&gt; by leveraging Python-WASI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction to Python-WASI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebAssembly System Interface (WASI) is a modular system interface for WebAssembly (WASM). It provides a set of APIs that are designed to be portable across different computing environments, enabling WASM modules to interact with the underlying system (e.g., file system, network). Python-WASI is a project that compiles Python to WASM, allowing Python code to run in a WASM runtime, which can be embedded in web browsers. This means I can now execute Python scripts client-side, directly within the user's browser, without needing a server-side interpreter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrating Python-WASI in ChatCraft&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ChatCraft, our innovative platform for interactive chat applications, has always supported JavaScript and TypeScript out of the box. However, we wanted to expand our language support to include Python, given its popularity and the vast ecosystem of libraries it offers. The integration of Python-WASI into ChatCraft was a game-changer, enabling users to run Python scripts seamlessly within the chat interface.&lt;/p&gt;

&lt;p&gt;Step 1: Fetching the Python-WASI Module&lt;br&gt;
The first step in integrating Python-WASI was to fetch the Python WASM module. I used the following URL to fetch the module:&lt;br&gt;
&lt;code&gt;const pythonWasmUrl = "https://unpkg.com/@antonz/python-wasi/dist/python.wasm";&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This URL points to the compiled Python WASM module, which is hosted on the unpkg CDN. By fetching this module, I can load Python into the browser's WebAssembly runtime.&lt;/p&gt;

&lt;p&gt;Step 2: Running Python Code&lt;br&gt;
With the Python WASM module loaded, I can now execute Python code. I used the @antonz/runno package, which provides a convenient API for running Python code in a WASM environment. Here's a simplified version of how I run Python code in ChatCraft:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function runPython(code: string) {
  const { WASI } = await import("@antonz/runno");
  const url = "https://unpkg.com/@antonz/python-wasi/dist/python.wasm";

  // Use captureConsole to capture console output
  const executionResult = await captureConsole(async () =&amp;gt; {
    const executionPromise = new Promise&amp;lt;void&amp;gt;((resolve, reject) =&amp;gt; {
      WASI.start(fetch(url), {
        args: ["python", "-c", code],
        stdout: (out) =&amp;gt; {
          console.log(out);
        },
        stderr: (err) =&amp;gt; {
          console.error(err);
        },
      })
        .then((result) =&amp;gt; {
          if (result.exitCode === 0) {
            resolve(); // Resolves the promise with no value
          } else {
            reject(new Error("Script execution failed")); // Rejects the promise if execution failed
          }
        })
        .catch((error) =&amp;gt; {
          reject(error); // Reject on error
        });
    });
    return executionPromise;
  });

  return executionResult;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this function, I use WASI.start to execute the Python code. The stdout and stderr callbacks allow us to capture the output and errors, respectively, which I can then display in the ChatCraft interface.&lt;/p&gt;

&lt;p&gt;Step 3: Enhancing User Experience&lt;br&gt;
To enhance the user experience, I added a loading spinner to indicate when the Python script is being executed. This provides immediate feedback to the user that their code is running, improving the overall interactivity of the platform.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The integration of Python-WASI into ChatCraft marks a significant milestone in our journey to create a versatile and interactive chat platform. Users can now run Python scripts directly in their browsers, opening up new possibilities for educational tools, data analysis applications, and interactive bots. This is just the beginning, and we're excited to explore further enhancements and language support in the future.&lt;/p&gt;

&lt;p&gt;I believe that the ability to run Python in the browser, thanks to Python-WASI, will inspire developers and creators to build even more innovative and engaging web applications. &lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>wasi</category>
      <category>webapp</category>
    </item>
    <item>
      <title>Try to run python in a Pure Browser-Based Web App running on Cloudflare</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Fri, 23 Feb 2024 23:47:23 +0000</pubDate>
      <link>https://forem.com/wanggithub0/try-to-run-python-in-a-pure-browser-based-web-app-running-on-cloudflare-4hi8</link>
      <guid>https://forem.com/wanggithub0/try-to-run-python-in-a-pure-browser-based-web-app-running-on-cloudflare-4hi8</guid>
      <description>&lt;p&gt;In the ever-evolving landscape of web development, the ability to run various programming languages directly in the browser has become a sought-after feature. Thanks to advancements in WebAssembly System Interface (WASI), developers can now explore new possibilities for running languages like Python, Go, and TypeScript in a pure browser-based web application.&lt;/p&gt;

&lt;p&gt;WASI, short for WebAssembly System Interface, provides a standardized interface for running WebAssembly modules outside of a web browser. This allows developers to execute code written in different languages in a secure and efficient manner, opening up a world of possibilities for web applications.&lt;/p&gt;

&lt;p&gt;Recently, there has been a push to enable support for languages like Python, Go, and TypeScript via WASI. By leveraging tools like esbuild wasm and integrating projects like python-wasi, developers can now run arbitrary Python code directly in the browser.&lt;/p&gt;

&lt;p&gt;One of the key projects in this space is &lt;a href="https://github.com/nalgeon/python-wasi"&gt;python-wasi&lt;/a&gt;, developed by nalgeon. This project provides a way to run Python code using WASI, enabling developers to execute Python scripts in a browser environment. By fetching the python.wasm file from a specified URL and providing the Python script to execute, developers can seamlessly run Python code and see the output in real-time.&lt;/p&gt;

&lt;p&gt;I successfully tested it by using the following code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://unpkg.com/@antonz/python-wasi/dist/python.wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pythonScript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
a = 1
b = 2
c = a + b
print(c)
print("Hello World!")
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@antonz/runno&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;WASI&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;WASI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;python&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pythonScript&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`exit code = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I guess I can combine it with &lt;a href="https://github.com/tarasglek/chatcraft.org"&gt;ChatCraft&lt;/a&gt; in this way. But still need help to make sure if I need support the "function" and "run code".&lt;/p&gt;

&lt;p&gt;This week I also &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/477"&gt;fix a small bug&lt;/a&gt; "Show More..." button's text is improperly layered and visible through the options menu", I found the button uses zIndex={10}, so I just try to use zIndex={11} at first. Thanks &lt;a href="https://dev.to/amnish04"&gt;Amnish&lt;/a&gt; provided a more suitable way by using Chakra theme &lt;code&gt;theme.zIndices.dropdown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next week, I will keep working on combine python.wasm in ChatCraft.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>python</category>
    </item>
    <item>
      <title>Exploring OAuth 2.0: Enabling Google Authentication in a Pure Browser-Based Web App running on Cloudflare</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Sat, 17 Feb 2024 03:19:16 +0000</pubDate>
      <link>https://forem.com/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-running-on-cloudflare-49jb</link>
      <guid>https://forem.com/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-running-on-cloudflare-49jb</guid>
      <description>&lt;p&gt;As I mentioned &lt;a href="https://dev.to/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-2-14mb"&gt;last week&lt;/a&gt;, our &lt;a href="https://github.com/tarasglek/chatcraft.org" rel="noopener noreferrer"&gt;ChatCraft&lt;/a&gt; run on cloudflare function, which is quite similar to node.js, so I tried to do the Google OAuth using &lt;a href="https://developers.google.com/identity/protocols/oauth2/web-server" rel="noopener noreferrer"&gt;Using OAuth 2.0 for Web Server Applications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are four steps:&lt;br&gt;
Step 1: Redirect to Google's OAuth 2.0 server&lt;br&gt;
Step 2: Google prompts user for consent&lt;br&gt;
Step 3: Handle the OAuth 2.0 server response - get code&lt;br&gt;
Step 4: Calling Google APIs - get user info using access_token&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step1, Step2&lt;/strong&gt;
The fist 2 steps are quiet similar to what I did before. 
In order to get the provider, I also added it in state, and parse the state later.
&lt;code&gt;
const url = buildUrl(
  "https://accounts.google.com/o/oauth2/v2/auth",
  // If there's a chatId, piggy-back it on the request as state
  chatId
    ? {
        client_id: GOOGLE_CLIENT_ID,
        redirect_uri: GOOGLE_REDIRECT_URI,
        response_type: GOOGLE_RESPONSE_TYPE,
        scope: GOOGLE_SCOPE,
        state: "provider=google&amp;amp;chat_id=" + chatId,
      }
    : {
        client_id: GOOGLE_CLIENT_ID,
        redirect_uri: GOOGLE_REDIRECT_URI,
        response_type: GOOGLE_RESPONSE_TYPE,
        scope: GOOGLE_SCOPE,
        state: "provider=google",
      }
);
return Response.redirect(url, 302);
&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, I run the login with a provider &lt;code&gt;/api/login?provider=${provider}&amp;amp;chat_id=${chatId}&lt;/code&gt;, so I can get the provider directly using &lt;code&gt;reqUrl.searchParams.get("provider")&lt;/code&gt; then it directed to Google, after user consent, it redirect back with state &lt;code&gt;state=provider%3Dgoogle%26chat_id%3Dl77...&lt;/code&gt;, so I used get state and then decodeURI+get provider:&lt;/p&gt;

&lt;p&gt;Here is how I parse the state. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; let provider = reqUrl.searchParams.get("provider");
  if (!provider) {
    let state = reqUrl.searchParams.get("state");
    if (state) {
      state = decodeURIComponent(state);
      const stateParams = new URLSearchParams(state);
      provider = stateParams.get("provider");
    }
  }

//state=provider%3Dgoogle%26chat_id%3Dl77...
  let chatId = reqUrl.searchParams.get("chat_id");
  if (!chatId) {
    let state = reqUrl.searchParams.get("state");
    if (state) {
      state = decodeURIComponent(state);
      const stateParams = new URLSearchParams(state);
      chatId = stateParams.get("chatId");
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx89lqo9qaffokzrcwpke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx89lqo9qaffokzrcwpke.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugc43q7e7ifnrct0qbce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugc43q7e7ifnrct0qbce.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step3&lt;/strong&gt;
This step I tried to test using Postman firstly, then I got the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function requestGoogleAccessToken(
  code: string,
  CLIENT_ID: string,
  CLIENT_SECRET: string,
  GOOGLE_REDIRECT_URI: string
) {
  const url = buildUrl("https://accounts.google.com/o/oauth2/token", {
    code: code,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    redirect_uri: GOOGLE_REDIRECT_URI,
    grant_type: "authorization_code",
  });

  const res = await fetch(url, {
    method: "POST",
    headers: {
      "User-Agent": "chatcraft.org",
    },
  });

  if (!res.ok) {
    throw new Error(`Failed to get Google token: ${res.status} ${await res.text()}`);
  }

  const result = (await res.json()) as {
    error?: string;
    access_token: string;
  };
  if (result.error) {
    throw new Error(`Error in Google token response: ${result.error}`);
  }

  return result.access_token;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step4&lt;/strong&gt;
This step I also tried to test using Postman firstly, then I got the user information.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function requestGoogleUserInfo(token: string): Promise&amp;lt;User&amp;gt; {
  const res = await fetch("https://www.googleapis.com/oauth2/v1/userinfo", {
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${token}`,
      "User-Agent": "chatcraft.org",
    },
  });

  if (!res.ok) {
    throw new Error(`Failed to get Google User info: ${res.status} ${await res.text()}`);
  }

  const { email, name, picture } = (await res.json()) as {
    email: string;
    name: string;
    picture: string;
  };

  return { username: email, name: name, avatarUrl: picture };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally, I made it! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh4nui348uwmfw3xccfd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh4nui348uwmfw3xccfd.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have been testing with my own account locally. However, when I deployed to the production environment, I noticed that the Google environment parameters are all undefined. After connecting with my professor, I found the environment variables name with my variables are different. After revising it, it threw "Error 400: redirect_uri_mismatch". Later we will check the redirect uri set in Google Authorized redirect URIs.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>cloudflare</category>
      <category>webapp</category>
    </item>
    <item>
      <title>Exploring OAuth 2.0: Enabling Google Authentication in a Pure Browser-Based Web App (2)</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Sat, 10 Feb 2024 14:46:03 +0000</pubDate>
      <link>https://forem.com/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-2-14mb</link>
      <guid>https://forem.com/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-2-14mb</guid>
      <description>&lt;p&gt;Continue on &lt;a href="https://dev.to/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-1-59mg"&gt;last week work&lt;/a&gt;, I keep working on the step3:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Handle the OAuth 2.0 server response&lt;/strong&gt;&lt;br&gt;
According to &lt;a href="https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#handlingresponse"&gt;Google document&lt;/a&gt; the OAuth 2.0 server sends a response to the redirect_uri specified in your access token request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user approves the request, the response contains an access token:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;https://oauth2.example.com/callback#access_token=4/P7q7W91&amp;amp;token_type=Bearer&amp;amp;expires_in=3600&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user does not approve the request, the response contains an error message: &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;https://oauth2.example.com/callback#error=access_denied&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Because I set &lt;code&gt;redirect_uri: "http://localhost:5173"&lt;/code&gt;, and my project will automatically jump to the my most recent chat, now I can only put my parse into my &lt;a href="https://github.com/tarasglek/chatcraft.org/blob/main/src/router.tsx"&gt;&lt;code&gt;createBrowserRouter&lt;/code&gt;&lt;/a&gt; component. And I'm still searching other better way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4. Calling Google APIs&lt;/strong&gt;&lt;br&gt;
After getting access_token, I can use it get the information I need by calling to the same API for the authenticated user using the access_token query string parameter:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET https://www.googleapis.com/drive/v2/files?access_token=access_token&lt;/code&gt;&lt;br&gt;
After doing all of these, I can got the basic information of the user now.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9kj8ks2jw7w7jz0zwqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9kj8ks2jw7w7jz0zwqo.png" alt="Image description" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the step3 and step4 code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      // Because google will redirect_uri to "/" with accessToken
      const requestedURI = location.pathname;
      // Accessing anchor values
      const anchor = location.hash.substring(1); // Exclude the '#' character
      console.log("Requested URI:", requestedURI);
      console.log("Anchor:", anchor);
      const match = anchor.match(/access_token=([^&amp;amp;]*)/);
      const accessToken = match ? match[1] : null;
      console.log("accessToken:", accessToken);
      const xhr = new XMLHttpRequest();
      xhr.open(
        "GET",
        "https://www.googleapis.com/drive/v3/about?fields=user&amp;amp;" + "access_token=" + accessToken
      );
      xhr.onreadystatechange = function () {
        console.log(xhr.response);
      };
      xhr.send(null);Ï
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My professor reviewed my PR and gave me some feedback: "The JS you run in a CloudFlare Worker Function is a lot like running node.js."&lt;br&gt;
This means that my initial direction was wrong; I shouldn't have approached it as a pure browser-based web app. Afterward, I looked into Cloudflare Worker Functions.&lt;/p&gt;

&lt;p&gt;Cloudflare Workers uses the V8 JavaScript engine, which is the same engine used by Node.js. This means that many of the JavaScript language features and APIs available in Node.js are also available in Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;Both Cloudflare Workers and Node.js provide a runtime environment for executing JavaScript code on the server-side. They support similar JavaScript syntax and provide access to common APIs, such as the fetch API for making HTTP requests.&lt;/p&gt;

&lt;p&gt;However, there are some differences between Cloudflare Workers and Node.js. Cloudflare Workers have a more limited environment compared to Node.js, as they are designed to be lightweight and run at the edge of the network. This means that certain Node.js-specific features and APIs may not be available in Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;It's important to consult the Cloudflare Workers documentation and API reference to understand the specific capabilities and limitations of the Cloudflare Workers runtime environment.&lt;/p&gt;

&lt;p&gt;This time I'll do the Oauth according to &lt;a href="https://developers.google.com/identity/protocols/oauth2/web-server#node.js"&gt;service-side web application&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>openai</category>
      <category>typescript</category>
      <category>webapp</category>
    </item>
    <item>
      <title>Exploring OAuth 2.0: Enabling Google Authentication in a Pure Browser-Based Web App (1)</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Sat, 03 Feb 2024 02:58:25 +0000</pubDate>
      <link>https://forem.com/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-1-59mg</link>
      <guid>https://forem.com/wanggithub0/exploring-oauth-20-enabling-google-authentication-in-a-pure-browser-based-web-app-1-59mg</guid>
      <description>&lt;p&gt;In the realm of web development, user authentication is a critical aspect, and leveraging OAuth 2.0 for secure, seamless authentication has become a common practice. Recently, I delved into the world of OAuth 2.0 with the specific goal of enabling users to authenticate with Google in a pure browser-based web app, devoid of any server-side code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insights from GitHub Authentication&lt;/strong&gt;&lt;br&gt;
First, I tried to learn the GitHub authentication in our project &lt;a href="https://github.com/tarasglek/chatcraft.org"&gt;ChatCraft&lt;/a&gt;. I found GitHub provides &lt;a href="https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user"&gt;REST API&lt;/a&gt; to get public and private information about authenticated users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Quest for Google Authentication&lt;/strong&gt;&lt;br&gt;
Then, I searched the document for Google Authentication and found the &lt;a href="https://developers.google.com/identity/sign-in/web/sign-in#sign_out_a_user"&gt;web&lt;/a&gt;, I tried to understand the way to figure it our, but I found it was for an web app which contained the backend.&lt;br&gt;
So I tried to find the suitable way to use adapting Google OAuth for a client-side application. &lt;br&gt;
Finally, I found the &lt;a href="https://developers.google.com/identity/protocols/oauth2/web-server"&gt;document&lt;/a&gt; for JavaScript web apps &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghlaxnxh41osl1yv68ou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghlaxnxh41osl1yv68ou.png" alt="Image description" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The process involved creating an OAuth 2.0 Client ID, which acted as the key to unlocking the authentication flow. &lt;a href="https://github.com/tarasglek"&gt;Taras&lt;/a&gt; helped me to set one for the ChatCraft. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Redirect to Google's OAuth 2.0 server&lt;/strong&gt;&lt;br&gt;
Redirecting to Google's OAuth 2.0 server was a pivotal moment. The dance between my application and Google's authentication server took shape. I found I need set the query string redirect_uri to generate a URL to request access from Google's OAuth 2.0 endpoint, which is set in &lt;a href="https://console.cloud.google.com/apis/credentials"&gt;Credentials page&lt;/a&gt;. So it can only test in product environment, can test in my local and testing environment which makes it hard for me to write and test code.&lt;br&gt;
Luckily, I found a good way to solve it. I created a credential for testing and add &lt;code&gt;redirect_uri: "http://localhost:5173"&lt;/code&gt; in my request. So I can correctly redirct to google from my local.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73zspicg75g5cjvthfns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73zspicg75g5cjvthfns.png" alt="Image description" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Google prompts user for consent&lt;/strong&gt;&lt;br&gt;
In this step, the user decides whether to grant your application the requested access. At this stage, Google displays a consent window that shows the name of your application and the Google API services that it is requesting permission to access with the user's authorization credentials and a summary of the scopes of access to be granted. The user can then consent to grant access to one or more scopes requested by your application or refuse the request. I think I will add some check for this step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Handle the OAuth 2.0 server response&lt;/strong&gt;&lt;br&gt;
According to google document, the OAuth 2.0 server sends a response to the redirect_uri specified in your access token request which is difference from the API return. I think I still need time to figure it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan&lt;/strong&gt;&lt;br&gt;
My exploration into OAuth is an ongoing adventure. While I've laid the foundation for Google authentication, there is much more to discover – from handling access tokens securely to optimizing the user experience. The journey continues as I navigate the evolving landscape of web development and authentication mechanisms.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>programming</category>
      <category>react</category>
    </item>
    <item>
      <title>Enhancing ChatCraft: Publish Package and Improve User Experience</title>
      <dc:creator>WangGithub0</dc:creator>
      <pubDate>Sat, 27 Jan 2024 05:22:57 +0000</pubDate>
      <link>https://forem.com/wanggithub0/enhancing-chatcraft-publish-package-and-improve-user-experience-clc</link>
      <guid>https://forem.com/wanggithub0/enhancing-chatcraft-publish-package-and-improve-user-experience-clc</guid>
      <description>&lt;p&gt;In the world of software development, continuous improvement is key to delivering a seamless user experience. This blog post discusses recent updates and enhancements to ChatCraft, a powerful chat application. We'll delve into the process of creating a Node.js package, setting up TypeScript2openAI, and improving the user experience through PR reviews and feature additions.&lt;/p&gt;

&lt;h3&gt;
  
  
  - Publishing a Node.js Package
&lt;/h3&gt;

&lt;p&gt;Workflow Setup&lt;br&gt;
To streamline the development process, I implemented a robust workflow for releasing Node.js packages. The workflow triggers on release creation events and involves several key steps:&lt;/p&gt;

&lt;p&gt;Setting Up Environment Variables: I use environment variables to securely store sensitive information. In this case, the NPM_TOKEN is stored in the environment to ensure secure package publishing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set NPM Token in .npmrc&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" &amp;gt; .npmrc&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm/action-setup@v2&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
      &lt;span class="na"&gt;run_install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm publish&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow ensures that every release is built, tested, and published automatically, reducing manual intervention and minimizing the chances of errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  - Improving User Experience
&lt;/h3&gt;

&lt;p&gt;Opening Code in New Window&lt;br&gt;
One significant user experience enhancement involved creating a new Blob URL for the code and adding an IconButton to open the new blob. &lt;/p&gt;

&lt;p&gt;I implemented a mechanism to generate a new Blob URL for the code, allowing users to view the code in a new window. An IconButton was added to provide an intuitive way for users to open the code in a new window, enhancing accessibility and usability.&lt;br&gt;
User feedback is a crucial aspect of our development process. Special thanks to Amnish for highlighting inconsistent borders on the IconButton and to Roy for pointing out the "Open Code in New Window" button's visibility issues in dark mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  - PR Review Process
&lt;/h3&gt;

&lt;p&gt;I tried to review two PRs:&lt;br&gt;
"Fix: Better UX for Long Messages": Mingming, a valued contributor, worked tirelessly to enhance the user experience for long messages. &lt;/p&gt;

&lt;p&gt;"Opening Sidebar Should Not Displace Main Content": A significant challenge was identified regarding the displacement of main content when opening the sidebar. My Local testing revealed consistent issues, need further discussion on potential solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  - Future Plans
&lt;/h3&gt;

&lt;p&gt;Next week's I'll do a small ticket titled "Use npm version of TypeScript2openAI." Additionally, I will explore Google OAuth for user authentication, enhancing security and accessibility.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>chatgpt</category>
      <category>react</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
