<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Ekemini Samuel - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Ekemini Samuel - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 12 May 2026 20:30:51 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/envitab/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Real-time AI Gym Coach with Vision Agents ]]>
                </title>
                <description>
                    <![CDATA[ Computer vision is transforming how people train, from at-home workouts to smart gym mirrors. Imagine walking into your home gym, turning on your camera, and having an AI coach that sees your movements, counts your reps, and corrects your form in rea... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-real-time-ai-gym-coach-with-vision-agents/</link>
                <guid isPermaLink="false">69458b6967b30377c55c8aa3</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agentic AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ekemini Samuel ]]>
                </dc:creator>
                <pubDate>Fri, 19 Dec 2025 17:29:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766158143362/b5d2947c-cc24-4948-a7fd-7ef2b3a79d5f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Computer vision is transforming how people train, from at-home workouts to smart gym mirrors.</p>
<p>Imagine walking into your home gym, turning on your camera, and having an AI coach that sees your movements, counts your reps, and corrects your form in real time.</p>
<p>That's exactly what we're building in this tutorial: a real-time gym companion and fitness coach.</p>
<p>We'll integrate <a target="_blank" href="https://visionagents.ai/">Vision Agents</a>' low-latency video inference to detect movement patterns, count reps, and give instant voice feedback like "Straighten your back!" or "Keep your form tight!", just like a human trainer would.</p>
<p>Here is a <a target="_blank" href="https://youtu.be/etqq68p-RGE">demo video</a> of the AI gym companion during a workout session:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/etqq68p-RGE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-what-well-cover">What We’ll Cover:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-project">Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-run-the-app">How to Run the App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ol>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<ul>
<li><p>Python 3.13 or higher</p>
</li>
<li><p>API keys for:</p>
<ul>
<li><p><a target="_blank" href="https://ai.google.dev/">Gemini</a> (for real-time LLM with vision)</p>
</li>
<li><p><a target="_blank" href="https://getstream.io/video/">Stream</a> (for video/audio infrastructure)</p>
</li>
<li><p>Alternatively: <a target="_blank" href="https://openai.com">OpenAI</a> (if using <a target="_blank" href="https://platform.openai.com/docs/guides/realtime">OpenAI Realtime</a> instead)</p>
</li>
</ul>
</li>
<li><p>Code editor like VS Code or Windsurf</p>
</li>
</ul>
<h2 id="heading-setting-up-the-project"><strong>Setting Up the Project</strong></h2>
<p>Create a new directory on your computer called <code>gym_buddy</code>. You can also do it directly in your terminal with this command:</p>
<pre><code class="lang-bash">mkdir gym_buddy
</code></pre>
<p>Then open the directory in your IDE (for this guide, I’m using <a target="_blank" href="https://windsurf.com/">Windsurf IDE</a>).</p>
<p>If you don’t have uv (a fast Python package installer and resolver) installed on your computer, install it with this command:</p>
<pre><code class="lang-bash">pip install uv
</code></pre>
<p>Note: After installing uv, you can also run <code>uv -init</code> to set up the project with sample files and a <code>.toml</code> file with the metadata.</p>
<p>Next, we’ll create the <code>pyproject.toml</code> file. This is a configuration file for Python projects that specifies build system requirements and other project metadata. It's a standard file used by modern Python packaging tools.</p>
<p>Enter the code below:</p>
<pre><code class="lang-bash">[project]
name = <span class="hljs-string">"gym-buddy"</span>
version = <span class="hljs-string">"0.1.0"</span>
requires-python = <span class="hljs-string">"&gt;=3.13"</span>
dependencies = [
    <span class="hljs-string">"python-dotenv&gt;=1.0"</span>,
    <span class="hljs-string">"vision-agents"</span>,
    <span class="hljs-string">"vision-agents-plugins-openai"</span>,
    <span class="hljs-string">"vision-agents-plugins-getstream"</span>,
    <span class="hljs-string">"vision-agents-plugins-ultralytics"</span>,
    <span class="hljs-string">"vision-agents-plugins-gemini"</span>,
]

[tool.uv.sources]
<span class="hljs-string">"vision-agents"</span> = {path = <span class="hljs-string">"../../agents-core"</span>, editable=<span class="hljs-literal">true</span>}
<span class="hljs-string">"vision-agents-plugins-deepgram"</span> = {path = <span class="hljs-string">"../../plugins/deepgram"</span>, editable=<span class="hljs-literal">true</span>}
<span class="hljs-string">"vision-agents-plugins-ultralytics"</span> = {path = <span class="hljs-string">"../../plugins/ultralytics"</span>, editable=<span class="hljs-literal">true</span>}
<span class="hljs-string">"vision-agents-plugins-openai"</span> = {path = <span class="hljs-string">"../../plugins/openai"</span>, editable=<span class="hljs-literal">true</span>}
<span class="hljs-string">"vision-agents-plugins-getstream"</span> = {path = <span class="hljs-string">"../../plugins/getstream"</span>, editable=<span class="hljs-literal">true</span>}
<span class="hljs-string">"vision-agents-plugins-gemini"</span> = {path = <span class="hljs-string">"../../plugins/gemini"</span>, editable=<span class="hljs-literal">true</span>}
</code></pre>
<p>You can also create a <code>requirements.in</code> file with just the direct dependencies, like so:</p>
<pre><code class="lang-bash">python-dotenv&gt;=1.0
vision-agents
vision-agents-plugins-openai
vision-agents-plugins-getstream
vision-agents-plugins-ultralytics
vision-agents-plugins-gemini
</code></pre>
<p>Then install dependencies using uv and either of these commands:</p>
<pre><code class="lang-bash">uv sync
</code></pre>
<p>This will generate the <code>uv.lock</code> from the uv package manager that handles the project’s dependencies and builds.</p>
<p>If you are using a Windows OS, you might come across a dependency installation error, particularly with NumPy. This is likely due to missing build tools on your system.</p>
<h4 id="heading-why-numpy-is-required">Why NumPy is required</h4>
<p>NumPy is a Python library for numerical computing. In this project, it’s used by the computer-vision and AI components (such as YOLO-based detection and Vision Agents) to handle image data, bounding boxes, coordinates, and other numerical outputs produced during real-time video analysis.</p>
<p>Many of the libraries used here depend on it for fast array operations and mathematical computations. That’s why NumPy is installed as part of the setup and why issues with its installation can affect the entire pipeline.</p>
<p>To resolve it, install <a target="_blank" href="https://visualstudio.microsoft.com/visual-cpp-build-tools/">Visual Studio Build Tools</a> (required for building Python packages with C extensions). During installation, make sure that you select "Desktop development with C++". This installs all the necessary build tools.</p>
<p>Visual Studio displays like this after the installation is done. You may need to restart your computer for the updates to take effect.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863109016/81d76ab4-9cd8-48f6-8cd9-83654ab27071.png" alt="The Visual Studio installer" class="image--center mx-auto" width="1600" height="831" loading="lazy"></p>
<p>Now run this command in your terminal:</p>
<pre><code class="lang-bash">python -m pip install -e .
</code></pre>
<p>The command above installs all the necessary dependencies for the project.</p>
<h3 id="heading-how-to-get-your-api-keys">How to Get Your API Keys</h3>
<p>For this project, we need to get API keys from Stream and Gemini/OpenAI.</p>
<p>To get your Stream API key, go ahead and <a target="_blank" href="https://getstream.io/">sign up</a> with your preferred method.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863152347/b46c8cc0-0f2f-448f-b7c5-f723fee94fb5.png" alt="Stream’s sign-up page" class="image--center mx-auto" width="1600" height="733" loading="lazy"></p>
<p>Then, navigate to your <a target="_blank" href="https://dashboard.getstream.io/organization/1270689/apps">dashboard</a> and click 'Create App' to create a new app for the AI gym companion.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863177461/8c8c51d5-46fe-44fe-8a2c-2336d3492da4.png" alt="Stream dashboard" class="image--center mx-auto" width="1600" height="722" loading="lazy"></p>
<p>Enter the name for the app, choose the environment (Development/Production), select a region, and click on <strong>‘Create App’</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863207947/529df7e3-bbdd-4d84-8023-3cb80241040b.png" alt="Create the App on Stream" class="image--center mx-auto" width="1600" height="725" loading="lazy"></p>
<p>After creating the app, click on the dashboard overview tab in the left sidebar, then navigate to the Video tab and click on "<strong>API Keys"</strong>. Copy your API key and secret, and save them securely.</p>
<p>To get your <a target="_blank" href="https://gemini.google.com/">Gemini</a> API key, visit the <a target="_blank" href="https://aistudio.google.com/welcome?utm_source=PMAX&amp;utm_medium=display&amp;utm_campaign=FY25-global-DR-pmax-1710442&amp;utm_content=pmax&amp;gclsrc=aw.ds&amp;gad_source=1&amp;gad_campaignid=22301327511&amp;gclid=CjwKCAiA55rJBhByEiwAFkY1QOJAyRZcUSQvxW3RlHpE-GvzAoERF7Pt_mRq7p9dFYp2cu8CCNidEBoC65MQAvD_BwE">Google AI Studio website</a>, then click on Get started.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765864009410/9d588512-c2ea-42bc-8e72-d2c213587cf0.png" alt="Setup your Google AI studio account" class="image--center mx-auto" width="1600" height="392" loading="lazy"></p>
<p>Then, go to your dashboard and click on '<strong>Create API key'.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863451321/99f73092-5e05-47c4-a3de-6350dfec50f0.png" alt="Create your API key" class="image--center mx-auto" width="1600" height="269" loading="lazy"></p>
<p>Enter a name for the key, then create a new project for the API key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863470658/40c7e61c-6be3-40a6-8e61-236e334241d9.png" alt="Name your API key" class="image--center mx-auto" width="772" height="417" loading="lazy"></p>
<p>After you have created the new API key, copy it and save it securely.</p>
<h3 id="heading-building-the-ai-gym-companion">Building the AI gym companion</h3>
<p>Now that you have the API keys you’ll need for the AI gym companion, create a .env file in the project’s root directory and add all the API keys like so:</p>
<pre><code class="lang-bash">GEMINI_API_KEY=your_gemini_key
STREAM_API_KEY=your_stream_key
STREAM_API_SECRET=your_stream_secret
</code></pre>
<p>If you’re using <a target="_blank" href="https://openai.com/">OpenAI</a> instead of Gemini, also add:</p>
<pre><code class="lang-bash">OPENAI_API_KEY=your_openai_key
</code></pre>
<p>This is the project and codebase structure for the gym companion app we are building:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766598231388/c0ca918c-de0d-4bbe-b55b-21ae3082c002.webp" alt="The codebase and project folder for the AI gym companion" class="image--center mx-auto" width="1030" height="1008" loading="lazy"></p>
<p>In the root directory, create an empty <code>_init.py</code> file. This file makes Python treat the directory as a package. You can add a comment in the file to remember, like so:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># This file makes Python treat the directory as a package.</span>
</code></pre>
<p>Next, create a <code>gym_buddy.py</code> file. This is the main app file, containing agent setup and call joining logic for the Gym Companion. Enter the code below in the file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">from</span> vision_agents.core <span class="hljs-keyword">import</span> User, Agent, cli
<span class="hljs-keyword">from</span> vision_agents.core.agents <span class="hljs-keyword">import</span> AgentLauncher
<span class="hljs-keyword">from</span> vision_agents.plugins <span class="hljs-keyword">import</span> getstream, ultralytics, gemini
logger = logging.getLogger(__name__)
load_dotenv()
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_agent</span>(<span class="hljs-params">**kwargs</span>) -&gt; Agent:</span>
    agent = Agent(
        edge=getstream.Edge(),  <span class="hljs-comment"># use stream for edge video transport</span>
        agent_user=User(name=<span class="hljs-string">"AI gym companion"</span>),
        instructions=<span class="hljs-string">"Read @gym_buddy.md"</span>,  <span class="hljs-comment"># read the gym buddy markdown instructions</span>
        llm=gemini.Realtime(fps=<span class="hljs-number">3</span>),  <span class="hljs-comment"># Share video with gemini</span>
        <span class="hljs-comment"># llm=openai.Realtime(fps=3), use this to switch to openai</span>
        processors=[
            ultralytics.YOLOPoseProcessor(model_path=<span class="hljs-string">"yolo11n-pose.pt"</span>)
        ],  <span class="hljs-comment"># realtime pose detection with yolo</span>
    )
    <span class="hljs-keyword">return</span> agent
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">join_call</span>(<span class="hljs-params">agent: Agent, call_type: str, call_id: str, **kwargs</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
    call = <span class="hljs-keyword">await</span> agent.create_call(call_type, call_id)
    <span class="hljs-comment"># join the call and open a demo env</span>
    <span class="hljs-keyword">with</span> <span class="hljs-keyword">await</span> agent.join(call):
        <span class="hljs-keyword">await</span> agent.llm.simple_response(
            text=<span class="hljs-string">"Say hi. After the user does their exercise, offer helpful feedback."</span>
        )
        <span class="hljs-keyword">await</span> agent.finish()  <span class="hljs-comment"># run till the call ends</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    cli(AgentLauncher(create_agent=create_agent, join_call=join_call))
</code></pre>
<p>Then create a <code>gym_buddy.md</code> file. This is an instructions file for the gym agent's coaching guide, which it will follow when analysing the workouts and providing real-time feedback. Enter the markdown code below:</p>
<pre><code class="lang-markdown">You are a voice fitness coach. You will watch the user's workout and offer feedback.
The video clarifies the body position using Yolo's pose analysis, so you'll see their exact movement.
Speak with a high-energy, motivating tone. Be strict about form but encouraging. Do not give feedback if you are not sure or do not see an exercise.
<span class="hljs-section"># Gym Workout Coaching Guide</span>
<span class="hljs-section">## 1. Introduction</span>
A fitness coach's primary responsibility is to ensure safety and efficacy in every movement. While everybody is different, the fundamental mechanics of human movement—stability, alignment, and range of motion—remain constant. By monitoring key checkpoints like spinal alignment, joint tracking, and tempo, coaches can guide athletes toward stronger, injury-free workouts. The following guidelines break down the core compound movements into phases, with clear teaching points and coaching cues.
<span class="hljs-section">## 2. The Squat: Setup and Stance</span>
The squat is the king of lower-body exercises, but it starts before the descent. The athlete should stand with feet shoulder-width apart or slightly wider, toes pointed slightly outward (5-30 degrees). The spine must be neutral, chest proud, and core braced. Coaches should watch for collapsing arches in the feet or a rounded upper back. A solid setup creates the tension needed for a powerful lift.
<span class="hljs-section">## 3. The Squat: Descent (Eccentric Phase)</span>
The movement begins by breaking at the hips and knees simultaneously. The hips should travel back and down, as if sitting in a chair, while the knees track in line with the toes. Coaches must ensure the heels stay glued to the floor. Common errors include "knee valgus" (knees caving in) or the torso collapsing forward. The descent should be controlled and deliberate.
<span class="hljs-section">## 4. The Squat: Depth and Reversal</span>
"Depth" is achieved when the hip crease drops below the top of the knee (parallel). While not everyone has the mobility for this, it is the standard for a full range of motion. At the bottom, the athlete should maintain tension—no bouncing or relaxing. The reversal (concentric phase) is driven by driving the feet into the floor and extending the hips and knees, exhaling forcefully.
<span class="hljs-section">## 5. The Push-up: The Plank Foundation</span>
A perfect push-up is essentially a moving plank. The setup requires hands placed slightly wider than shoulder-width, directly under the shoulders. The body must form a straight line from head to heels. Coaches should watch for sagging hips (lumbar extension) or piking hips (flexion). Glutes and quads should be squeezed tight to lock the body into a rigid lever.
<span class="hljs-section">## 6. The Push-up: Mechanics</span>
As the athlete lowers themselves, the elbows should track back at roughly a 45-degree angle to the torso, forming an arrow shape, not a "T". The chest should descend until it nearly touches the floor. The neck must remain neutral—no reaching with the chin. The push back up should be explosive, fully extending the arms without locking the elbows violently.
<span class="hljs-section">## 7. The Lunge: Step and Stability</span>
The lunge challenges balance and unilateral strength. Whether forward or reverse, the step should be long enough to allow both knees to bend to approximately 90 degrees at the bottom. The feet should remain hip-width apart throughout the movement, like moving on train tracks, not a tightrope. Coaches should look for wobbling or the front heel lifting off the ground.
<span class="hljs-section">## 8. The Lunge: Alignment</span>
In the bottom position, the front knee should be directly over the ankle, not shooting far past the toes (though some forward travel is acceptable). The torso should remain upright or have a very slight forward lean; collapsing over the front thigh is a fault. The back knee should hover just an inch off the ground. Drive through the front heel to return to the start.
<span class="hljs-section">## 9. Tempo and Control</span>
Time under tension builds muscle and control. Coaches should encourage a specific tempo, such as 2-0-1 (2 seconds down, 0 pause, 1 second up). Rushing through reps often masks muscle imbalances and relies on momentum rather than strength. If an athlete speeds up, cue them to "slow down and own the movement."
<span class="hljs-section">## 10. Breathing Mechanics</span>
Proper breathing stabilises the core. The general rule is to inhale during the eccentric phase (lowering) and exhale during the concentric phase (lifting/pushing). For heavy lifts, the Valsalva manoeuvre (bracing the core with a held breath) may be appropriate, but for general fitness, rhythmic breathing ensures oxygen delivery and blood pressure management.
<span class="hljs-section">## 11. Common Faults and Fixes</span>
<span class="hljs-bullet">-</span> <span class="hljs-strong">**Squat - Butt Wink**</span>: Posterior pelvic tilt at the bottom. Fix: Limit depth or improve hamstring/ankle mobility.
<span class="hljs-bullet">-</span> <span class="hljs-strong">**Push-up - Winging Scapula**</span>: Shoulder blades popping up. Fix: Push the floor away at the top (protraction) and engage serratus anterior.
<span class="hljs-bullet">-</span> <span class="hljs-strong">**Lunge - Valgus Knee**</span>: Front knee collapsing in. Fix: Cue "push the knee out" and engage the glute medius.
<span class="hljs-bullet">-</span> <span class="hljs-strong">**General - Ego Lifting**</span>: Sacrificing form for reps or weight. Fix: Regress the exercise or slow the tempo
</code></pre>
<h3 id="heading-how-the-ai-agent-works">How the AI Agent works</h3>
<p>Now we have the instruction file for the AI agent set up. Let’s look at how the code works with the AI agent-creation and markdown instruction file above. In <code>gym_buddy.py</code>, the agent is created and initialised with specific components like so:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_agent</span>() -&gt; Agent:</span>
    <span class="hljs-comment"># Initialize video transport</span>
    video_transport = StreamVideoTransport()

    <span class="hljs-comment"># Set up AI components</span>
    gemini = GeminiRealtime()
    pose_processor = YOLOPoseProcessor(model_path=<span class="hljs-string">"yolo11n-pose.pt"</span>)

    <span class="hljs-comment"># Create agent with instructions</span>
    <span class="hljs-keyword">return</span> Agent(
        name=<span class="hljs-string">"AI Gym Buddy"</span>,
        instructions=<span class="hljs-string">"gym_buddy.md"</span>,  <span class="hljs-comment"># Loads coaching instructions</span>
        video_transport=video_transport,
        llm=gemini,
        processors=[pose_processor]
    )
</code></pre>
<p>The <code>gym_buddy.md</code> file contains structured instructions that guide the gym companion agent's behaviour.</p>
<pre><code class="lang-markdown"><span class="hljs-section">## Coaching Style</span>
<span class="hljs-bullet">-</span> Be encouraging and positive
<span class="hljs-bullet">-</span> Provide clear, actionable feedback
<span class="hljs-bullet">-</span> Focus on one correction at a time

<span class="hljs-section">## Squat Form</span>
<span class="hljs-bullet">-</span> Keep chest up and back straight
<span class="hljs-bullet">-</span> Knees should track over toes
<span class="hljs-bullet">-</span> Lower until thighs are parallel to ground
<span class="hljs-bullet">-</span> Push through heels to stand

<span class="hljs-section">## Safety Guidelines</span>
<span class="hljs-bullet">-</span> Stop user if a dangerous form is detected
<span class="hljs-bullet">-</span> Suggest modifications for beginners
<span class="hljs-bullet">-</span> Remind to keep core engaged
</code></pre>
<p>These instructions are loaded with the <code>instructions="gym_buddy.md"</code> parameter in the <code>gym_buddy.py</code> file. The agent then parses this file to understand how to analyse your form during the workout session and provides feedback.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Processing video frames</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_frame</span>(<span class="hljs-params">self, frame</span>):</span>
    <span class="hljs-comment"># Analyze pose using YOLO</span>
    poses = <span class="hljs-keyword">await</span> self.pose_processor.process(frame)

    <span class="hljs-comment"># Generate feedback based on instructions</span>
    feedback = <span class="hljs-keyword">await</span> self.llm.generate_feedback(
        poses=poses,
        instructions=self.instructions
    )
    <span class="hljs-keyword">return</span> feedback
</code></pre>
<p>When giving feedback, the agent compares the detected poses with the ideal form from the markdown. Then, it generates natural language feedback using the specified tone and style. The safety guidelines in the <code>gym_buddy.md</code> are checked first, then specific form corrections are mentioned by the agent.</p>
<p>To add a new exercise, you can update the <code>gym_buddy.md</code> file with a new section like so:</p>
<pre><code class="lang-markdown"><span class="hljs-section">## Push-up Form</span>
<span class="hljs-bullet">-</span> Keep body in a straight line
<span class="hljs-bullet">-</span> Lower until chest nearly touches floor
<span class="hljs-bullet">-</span> Push through palms to return up
<span class="hljs-bullet">-</span> Keep core engaged
</code></pre>
<p>The agent will automatically incorporate these instructions the next time it runs. This makes it easy to update and expand the agent's capabilities by simply editing the markdown file.</p>
<p>You can view the complete code for the AI Gym Companion in the <a target="_blank" href="https://github.com/Tabintel/gym_buddy">GitHub repository</a>.</p>
<h2 id="heading-how-to-run-the-app">How to Run the App</h2>
<p>First, create a virtual environment in Python with this command:</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<p>It creates the <code>.venv</code> directory.</p>
<p>Then activate the virtual Python environment like so:</p>
<pre><code class="lang-bash">.\venv\Scripts\activate
</code></pre>
<p>Now run the AI agent with this command:</p>
<pre><code class="lang-bash">uv run gym_buddy.py
</code></pre>
<p>You can also start the app with this command:</p>
<pre><code class="lang-bash">python gym_buddy.py
</code></pre>
<p>It begins loading like so:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863544434/7fa5fa8e-4286-40d1-9f34-86e7e7e6182b.png" alt="The AI gym companion is loading" class="image--center mx-auto" width="1291" height="373" loading="lazy"></p>
<p>The AI agent will:</p>
<ol>
<li><p>Create a video call</p>
</li>
<li><p>Open a demo UI in your browser</p>
</li>
<li><p>Join the call and start watching</p>
</li>
<li><p>Ask you to do a squat exercise</p>
</li>
<li><p>Analyse your moves and positions, and then provide feedback</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766598491312/9cd86035-c182-428e-b059-15842caec0b5.png" alt="Gemini AI is connected and the browser for the gym companion is opened" class="image--center mx-auto" width="1123" height="199" loading="lazy"></p>
<p>From the command terminal output above, it also shows that Gemini AI is connected.</p>
<p>The agent then loads in your browser like so:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863577856/e32b1b35-7356-4c23-8b8b-a8513dd9aabb.png" alt="The AI gym companion is launched" class="image--center mx-auto" width="1600" height="735" loading="lazy"></p>
<p>It also displays a pop-up modal that introduces the Vision Agents. You can skip the intro or click on <strong>Next</strong> to proceed.</p>
<p>The Vision Agent uses a global edge to ensure optimal call latency. This is useful for the AI gym companion to provide real-time feedback on the exercises the users perform.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863612816/4bd395ca-ed40-46d7-ab3b-0ceed23f1d0c.png" alt="The gym companion detects the visuals and movements" class="image--center mx-auto" width="1600" height="900" loading="lazy"></p>
<p>The AI gym companion can also provide chat messages on the exercises through the chatbox displayed on the right side of the UI. This is provided through the chat SDK/API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863702571/7586bddc-a830-4bf4-8af3-146dccf0f337.png" alt="The AI gym companion gives feedback" class="image--center mx-auto" width="1600" height="793" loading="lazy"></p>
<p>When you perform a squat, the Vision Agent (powered by Gemini) analyses the video frames in real-time. It detects the completion of the movement and triggers the <code>send_rep_count</code> tool. This instantly updates the exercise counter on your screen and provides an encouraging text and voice response!</p>
<p>Here is a <a target="_blank" href="https://youtu.be/etqq68p-RGE">demo video</a> of the AI gym companion during a workout session:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/etqq68p-RGE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>You can also copy the link and share it, or scan the QR code below to test the Gym Companion on your mobile phone.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765863762688/a6c7b56e-9b0b-4819-ae9f-61a32ce71280.png" alt="Copy the QR code to test on your mobile phone" class="image--center mx-auto" width="505" height="794" loading="lazy"></p>
<p>If you want to test it on your phone, install the <a target="_blank" href="https://apps.apple.com/us/app/stream-video-calls/id1644313060">Stream Video calls app</a> for iOS devices for a better mobile experience.</p>
<h2 id="heading-next-steps"><strong>Next Steps</strong></h2>
<p>In this tutorial, you’ve learned how to build an AI gym companion using Vision Agents.</p>
<p>The Real-Time Gym Companion illustrates how vision AI unlocks human-like interactivity by merging:</p>
<ul>
<li><p>Video perception (seeing)</p>
</li>
<li><p>LLM understanding (thinking)</p>
</li>
<li><p>Speech feedback (speaking)</p>
</li>
</ul>
<p>This low-latency technology lets you create real-time fitness apps that give instant feedback, much like a personal trainer would.</p>
<p>You can check out more project use cases with Vision Agents in the <a target="_blank" href="https://github.com/GetStream/Vision-Agents/tree/main/examples">GitHub repository</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ From Concept to Code: How to Use AI Tools to Design and Build UI Components ]]>
                </title>
                <description>
                    <![CDATA[ How should a website look? What size should the buttons be? What layout should you use? Do your users need an OTP to reset their passwords? These are all questions that proper user interface and user experience (UI/UX) design answer. Design prototypi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-design-and-build-ui-components-with-ai/</link>
                <guid isPermaLink="false">670d75f8ed568d2eaf032897</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ekemini Samuel ]]>
                </dc:creator>
                <pubDate>Mon, 14 Oct 2024 19:50:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727885187463/f41f59b0-1ec0-4dbf-9d2a-437738584310.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>How should a website look? What size should the buttons be? What layout should you use? Do your users need an OTP to reset their passwords? These are all questions that proper user interface and user experience (UI/UX) design answer.</p>
<p>Design prototyping and testing are critical steps in optimizing website UX functionality. One <a target="_blank" href="https://www.forrester.com/report/The-Six-Steps-For-Justifying-Better-UX/RES117708">study</a> reported that improving <a target="_blank" href="https://www.hotjar.com/ux-design">UX design</a> led to a 400% increase in website conversions.</p>
<p>For such an important task, we need the best possible tools and resources we can get. And lately I’ve been enjoying using <a target="_blank" href="https://sourcegraph.com/cody">Sourcegraph’s Cody</a>. Cody is an AI tool that speeds up coding by helping you understand, write, and fix code. It accesses information from your entire codebase, and also references documentation pages, to provide context about functions and variables, help create new code, and improve your design system.</p>
<p>When combined with Tailwind CSS, which is a utility-first CSS framework, you can rapidly build UI components that are both functional and visually appealing.</p>
<p>In this tutorial, I’ll teach you how to build UIs faster with Cody and Tailwind CSS so you can leverage AI to streamline your workflow.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>Basic understanding of JavaScript and front-end development.</p>
</li>
<li><p>Familiarity with Tailwind CSS.</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/en">Node.js</a> installed on your system.</p>
</li>
<li><p>A code editor like Visual Studio Code (VS Code).</p>
</li>
<li><p>Cody. <a target="_blank" href="https://sourcegraph.com/cody">Sign up on Sourcegraph</a> to get access (it’s free).</p>
</li>
</ul>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-will-we-be-building">What Will We Be Building?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-environment">How to Set Up Your Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-ui-components-with-ai">How to Create UI Components with AI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-more-complicated-uis">More Complicated UIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-and-manage-existing-codebases-with-cody">How to Improve and Manage Existing Codebases with Cody</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ul>
<h2 id="heading-what-will-we-be-building">What Will We Be Building?</h2>
<p>Let’s build yet another Todo App, but with a spin. Each to-do item will have a timer that can be started, paused, and reset. This might be useful to track how much time you spend working on specific tasks.</p>
<p>From the <a target="_blank" href="https://sourcegraph.com/docs/cody/capabilities/chat#selecting-context">Cody docs</a>, Cody's chat allows you to add files and symbols as context in your messages.</p>
<ul>
<li><p>Type <code>@</code> and then a filename to include a file as a context.</p>
</li>
<li><p>Type <code>@#</code> and then a symbol name to include the symbol's definition as context. Functions, methods, classes, types, and so on are all symbols.</p>
</li>
</ul>
<p>Even though Cody will be doing most of the heavy lifting, it’s great to have a plan of how we want the UI to look. Here are the wireframes I created with <a target="_blank" href="https://wireframe.cc/">wireframe.cc</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667247231/ada73031-20ac-4e07-8203-abe7d85a4d55.png" alt="Wireframe of the task component - overview state and detail state" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667377001/5ac6b7ce-e2a8-41d6-8378-e5ba36bb546d.png" alt="Wireframe of the header and footer" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Alright! Let’s get into it.</p>
<h2 id="heading-how-to-set-up-your-environment">How to Set Up Your Environment</h2>
<p>This tutorial uses Visual Studio Code, but the development process is similar across other code editors. If you haven’t set up an editor yet, choose one that suits your preference and <a target="_blank" href="https://nodejs.org/en/learn/getting-started/how-to-install-nodejs">install Node.js</a>.</p>
<p>As of this article, Sourcegraph Cody is available on <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=sourcegraph.cody-ai">Visual Studio Code</a>, <a target="_blank" href="https://github.com/sourcegraph/sg.nvim#setup">Neovim</a>, <a target="_blank" href="https://sourcegraph.com/github.com/sourcegraph/cody@main/-/blob/cli/README.md">Cody CLI</a>, <a target="_blank" href="https://github.com/sourcegraph/emacs-cody">Emacs</a>, and <a target="_blank" href="https://plugins.jetbrains.com/plugin/9682-cody-ai-coding-assistant-with-autocomplete--chat">all JetBrains IDEs</a></p>
<h3 id="heading-how-to-add-cody-to-your-code-editor">How to add Cody to your code editor</h3>
<p>First, head over to the <a target="_blank" href="https://sourcegraph.com/cody/">Cody landing page</a>, click <strong>Get Cody for Free</strong>, and follow the prompts to sign up for a <a target="_blank" href="https://sourcegraph.com/">Sourcegraph</a> account using your preferred authentication method—GitHub, GitLab, or Google.</p>
<p>Pick the right option for your code editor. If you’re using Visual Studio Code, that would be, <strong>Install Cody in VS Code</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667454887/bccde86e-ac68-4425-a5d8-ffbc7febfd83.png" alt="Cody dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Opening the extension in your code editor prompts a sign-in:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667554900/d956efbb-9712-4096-a968-69854d3b98c0.png" alt="Signing into Cody with your preferred method." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After signing in, we’re ready to go.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667586141/428a2eac-f383-4d7f-a63e-5fdd5638f227.jpeg" alt="The Cody chat on VS Code" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-set-up-the-project"><strong>How to set up the project</strong></h3>
<p>We’ll be working with a Vite + React + TailwindCSS project, but these ideas can easily be applied to any other framework (think Vue, Astro, Svelte, or regular Vanilla JS) or styling library (like Bootstrap, Bulma, Foundation CSS, or anything you’d prefer).</p>
<p>Run the following command to create a new React project, <strong>abc-planning-todo-app</strong>:</p>
<pre><code class="lang-bash">npm create vite@latest abc-planning-todo-app -- --template react
</code></pre>
<p>Then install Tailwind CSS:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>Next, update <code>tailwind.config.js</code> with this code to configure Tailwind CSS for the project:</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Delete everything in <code>./src/index.css</code> and add the following Tailwind CSS directives:</p>
<pre><code class="lang-javascript">
@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<h2 id="heading-how-to-create-ui-components-with-ai">How to Create UI Components with AI</h2>
<p>For consistency, let’s pick a color palette we’ll use for our to-do app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667638883/06819a4e-d7fd-4a24-b9b6-6ef8cda38b08.png" alt="Color palettes for the TODO UI" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>To use these colors in our Tailwind theme, we first need to give them descriptive names. Let's ask Cody for help!</p>
<p>For the rest of this article, all quotes represent a single prompt message used when chatting with Cody.</p>
<blockquote>
<p><em>What would be good names for the following hex colors?</em></p>
<ul>
<li><p><em>2B2D42</em></p>
</li>
<li><p><em>8D99AE</em></p>
</li>
<li><p><em>EDF2F4</em></p>
</li>
<li><p><em>EF233C</em></p>
</li>
<li><p><em>D90429</em></p>
</li>
</ul>
</blockquote>
<p>And then,</p>
<blockquote>
<p><em>Update</em> <strong><em>@tailwind.config.js</em></strong> <em>to include these 5 hex colors above:</em></p>
</blockquote>
<pre><code class="lang-javascript">
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {
      <span class="hljs-attr">colors</span>: {
        <span class="hljs-string">'midnight-navy'</span>: <span class="hljs-string">'#2B2D42'</span>,
        <span class="hljs-string">'cloudy-sky'</span>: <span class="hljs-string">'#8D99AE'</span>,
        <span class="hljs-string">'ice-white'</span>: <span class="hljs-string">'#EDF2F4'</span>,
        <span class="hljs-string">'vibrant-red'</span>: <span class="hljs-string">'#EF233C'</span>,
        <span class="hljs-string">'ruby-red'</span>: <span class="hljs-string">'#D90429'</span>,
      },
    },
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<h3 id="heading-how-to-create-basic-ui-components"><strong>How to create basic UI components</strong></h3>
<p>First, we create the Header component. With templates for relatively uncomplicated components (for example text inputs, headers, buttons and dropdowns), it’s as simple as providing a name for the component, and the copy content it should contain.</p>
<blockquote>
<p><em>Create a simple React Header Component with only company name on the far left and company motto on the far right. Use the Tailwind theme</em> <strong><em>@tailwind.config.js</em></strong></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667685164/728a496e-c018-4f09-ac4a-1926f322b3f1.png" alt="Adding the prompt to Cody" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Cody will generate a Header component, which you can then copy and paste into <code>./src/components/Header.jsx</code>.</p>
<p>You can also save the code to a new file in your project directly from the Cody chat.</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">// ./src/components/Header.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">const</span> Header = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white p-4 flex justify-between items-center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold"</span>&gt;</span>
        Company
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cloudy-sky italic"</span>&gt;</span>
        Innovate. Create. Inspire.
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
</code></pre>
<p>Next, create the Footer component with this prompt:</p>
<blockquote>
<p><em>Create a simple React Footer Component with content Copyright C ABC Planning. Use the Tailwind theme</em> <strong><em>@tailwind.config.js</em></strong>*. Ensure footer is always at the bottom of the viewport*</p>
</blockquote>
<pre><code class="lang-javascript">
<span class="hljs-comment">// ./src/components/Footer.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">const</span> Footer = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white p-4 text-center fixed bottom-0 w-full"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-symbol">&amp;copy;</span> {new Date().getFullYear()} ABC Planning<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Footer;
</code></pre>
<p>Let’s take a look at what we have so far.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667795664/bb16ff44-1403-499f-9441-9dcc804ea06b.png" alt="The generated Header and Footer" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-more-complicated-uis"><strong>More Complicated UIs</strong></h2>
<p>Next, we’ll need a container that will hold all our to-dos. Let’s ask Cody to create a to-do container component.</p>
<blockquote>
<p><em>Create a React TodoContainer Component that will serve as a container for Todo items. Use the Tailwind theme</em> <strong><em>@tailwind.config.js</em></strong>*. It should be mobile-responsive*.</p>
</blockquote>
<p>We tell Cody:</p>
<ul>
<li><p>What we want (a React component)</p>
</li>
<li><p>A brief description of how that component behaves/what it does (container for to-dos)</p>
</li>
<li><p>Any additional context – stylesheets, preferences, conventions, or anything else.</p>
</li>
</ul>
<pre><code class="lang-javascript">
<span class="hljs-comment">// ./src/components/TodoContainer.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">const</span> TodoContainer = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-ice-white min-h-screen p-4 sm:p-6 md:p-8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-4xl mx-auto"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-midnight-navy text-3xl font-bold mb-6"</span>&gt;</span>Todo List<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"</span>&gt;</span>
          {children}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoContainer;
</code></pre>
<p>A good <a target="_blank" href="https://sourcegraph.com/blog/chat-oriented-programming-in-action">Chat-oriented programming</a> (CHOP) strategy is to separate the creation of markup from the creation of content. This simplifies the prompting process.</p>
<p>Cody can <a target="_blank" href="https://sourcegraph.com/blog/chat-oriented-programming-in-action">g</a>enerate both very well, but trying to fine-tune a single prompt for both tasks might be challenging. Each prompt should have a clear, specific goal for best results.</p>
<p>Let's now focus on the <code>TodoItem</code> component and split it into sections:</p>
<ul>
<li><p>There's a <strong>to-do header</strong> which consists of a title with a border.</p>
</li>
<li><p>There's a <strong>View details</strong> button.</p>
</li>
<li><p>There's a timer showing how long ago the to-do was started.</p>
</li>
<li><p>There are control buttons (which reflect the state of the to-do)</p>
<blockquote>
<p><em>Create a React TodoItem Component.</em></p>
<p><em>It consists of:</em></p>
<p><em>1. Task title. A header text with a border around it.</em></p>
<p><em>2. A simplified link/button "View details" below the todo header box.</em></p>
<p><em>3. A digital timer that shows hours, minutes, and seconds in the format 00:00:00.</em></p>
<p><em>4. Todo actions - "Start", "Pause", "Reset". Use relevant colors for each. The timer should be able to be started, paused and reset when the respective button is pressed.</em></p>
<p><em>It should look like a card (tall not wide) on a desktop.</em></p>
<p><em>It will be used with</em> <strong><em>@TodoContainer.jsx</em></strong></p>
<p><em>Use</em> <strong><em>@tailwind.config.js</em></strong> <em>theme</em></p>
</blockquote>
<pre><code class="lang-javascript">  <span class="hljs-comment">// ./src/components/TodoItem.jsx</span>
  <span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">const</span> TodoItem = <span class="hljs-function">(<span class="hljs-params">{ title }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [time, setTime] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">const</span> [isRunning, setIsRunning] = useState(<span class="hljs-literal">false</span>);
    useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">let</span> interval;
      <span class="hljs-keyword">if</span> (isRunning) {
        interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
          setTime(<span class="hljs-function">(<span class="hljs-params">prevTime</span>) =&gt;</span> prevTime + <span class="hljs-number">1</span>);
        }, <span class="hljs-number">1000</span>);
      }
      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearInterval</span>(interval);
    }, [isRunning]);
    <span class="hljs-keyword">const</span> formatTime = <span class="hljs-function">(<span class="hljs-params">seconds</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> hours = <span class="hljs-built_in">Math</span>.floor(seconds / <span class="hljs-number">3600</span>);
      <span class="hljs-keyword">const</span> minutes = <span class="hljs-built_in">Math</span>.floor((seconds % <span class="hljs-number">3600</span>) / <span class="hljs-number">60</span>);
      <span class="hljs-keyword">const</span> secs = seconds % <span class="hljs-number">60</span>;
      <span class="hljs-keyword">return</span> [hours, minutes, secs].map(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v.toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">'0'</span>)).join(<span class="hljs-string">':'</span>);
    };
    <span class="hljs-keyword">const</span> handleStart = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> handlePause = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> handleReset = <span class="hljs-function">() =&gt;</span> {
      setIsRunning(<span class="hljs-literal">false</span>);
      setTime(<span class="hljs-number">0</span>);
    };
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-ice-white rounded-lg shadow-md p-4 flex flex-col space-y-4 h-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-midnight-navy border-2 border-midnight-navy rounded p-2 text-center"</span>&gt;</span>
          {title}
        <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cloudy-sky hover:text-midnight-navy transition-colors"</span>&gt;</span>
          View details
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-mono text-midnight-navy text-center"</span>&gt;</span>
          {formatTime(time)}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between mt-auto"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleStart}</span> 
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-vibrant-red text-ice-white px-4 py-2 rounded hover:bg-ruby-red transition-colors"</span>
          &gt;</span>
            Start
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handlePause}</span> 
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-cloudy-sky text-midnight-navy px-4 py-2 rounded hover:bg-ice-white transition-colors"</span>
          &gt;</span>
            Pause
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleReset}</span> 
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white px-4 py-2 rounded hover:bg-cloudy-sky transition-colors"</span>
          &gt;</span>
            Reset
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
  };
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoItem;
</code></pre>
<p>  From our wireframes, each to-do will have a detail state when its description is visible and can be updated. We’ll tell Cody to update the existing <code>TodoItem.jsx</code> file:</p>
<blockquote>
<p><em>Update</em> <strong><em>@TodoItem.jsx</em></strong> <em>to have a second state, when the view detail button is pressed. In this state, the TodoItem is composed of the following elements only:</em></p>
<p><em>1. Task header with the task title, same as before</em></p>
<p><em>2. A textbox with a description of the task</em></p>
<p><em>3. A save button, that emits an updateTask event, and switches the component back to its default state.</em></p>
<p><em>Include animations between the transition to create a clean feel and experience</em></p>
</blockquote>
<pre><code class="lang-javascript">
  <span class="hljs-comment">// ./src/components/TodoItem.jsx</span>
  <span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">const</span> TodoItem = <span class="hljs-function">(<span class="hljs-params">{ title, description = <span class="hljs-string">''</span>, onUpdateTask }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [time, setTime] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">const</span> [isRunning, setIsRunning] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [isDetailView, setIsDetailView] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [taskDescription, setTaskDescription] = useState(description);
    useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">let</span> interval;
      <span class="hljs-keyword">if</span> (isRunning) {
        interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
          setTime(<span class="hljs-function">(<span class="hljs-params">prevTime</span>) =&gt;</span> prevTime + <span class="hljs-number">1</span>);
        }, <span class="hljs-number">1000</span>);
      }
      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearInterval</span>(interval);
    }, [isRunning]);
    <span class="hljs-keyword">const</span> formatTime = <span class="hljs-function">(<span class="hljs-params">seconds</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> hours = <span class="hljs-built_in">Math</span>.floor(seconds / <span class="hljs-number">3600</span>);
      <span class="hljs-keyword">const</span> minutes = <span class="hljs-built_in">Math</span>.floor((seconds % <span class="hljs-number">3600</span>) / <span class="hljs-number">60</span>);
      <span class="hljs-keyword">const</span> secs = seconds % <span class="hljs-number">60</span>;
      <span class="hljs-keyword">return</span> [hours, minutes, secs].map(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v.toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">'0'</span>)).join(<span class="hljs-string">':'</span>);
    };
    <span class="hljs-keyword">const</span> handleStart = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> handlePause = <span class="hljs-function">() =&gt;</span> setIsRunning(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> handleReset = <span class="hljs-function">() =&gt;</span> {
      setIsRunning(<span class="hljs-literal">false</span>);
      setTime(<span class="hljs-number">0</span>);
    };
    <span class="hljs-keyword">const</span> handleViewDetails = <span class="hljs-function">() =&gt;</span> setIsDetailView(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> handleSave = <span class="hljs-function">() =&gt;</span> {
      onUpdateTask({ title, <span class="hljs-attr">description</span>: taskDescription });
      setIsDetailView(<span class="hljs-literal">false</span>);
    };
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-ice-white rounded-lg shadow-md p-4 flex flex-col space-y-4 h-full transition-all duration-300 ease-in-out"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-midnight-navy border-2 border-midnight-navy rounded p-2 text-center"</span>&gt;</span>
          {title}
        <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        {isDetailView ? (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-grow p-2 border-2 border-cloudy-sky rounded resize-none transition-all duration-300 ease-in-out"</span>
              <span class="hljs-attr">value</span>=<span class="hljs-string">{taskDescription}</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTaskDescription(e.target.value)}
              placeholder="Enter task description..."
            /&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleSave}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-vibrant-red text-ice-white px-4 py-2 rounded hover:bg-ruby-red transition-colors"</span>
            &gt;</span>
              Save
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span></span>
        ) : (
          <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleViewDetails}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cloudy-sky hover:text-midnight-navy transition-colors"</span>
            &gt;</span>
              View details
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-mono text-midnight-navy text-center"</span>&gt;</span>
              {formatTime(time)}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between mt-auto"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleStart}</span> 
                <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-vibrant-red text-ice-white px-4 py-2 rounded hover:bg-ruby-red transition-colors"</span>
              &gt;</span>
                Start
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handlePause}</span> 
                <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-cloudy-sky text-midnight-navy px-4 py-2 rounded hover:bg-ice-white transition-colors"</span>
              &gt;</span>
                Pause
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleReset}</span> 
                <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-midnight-navy text-ice-white px-4 py-2 rounded hover:bg-cloudy-sky transition-colors"</span>
              &gt;</span>
                Reset
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/&gt;</span></span>
        )}
      &lt;/div&gt;
    );
  };
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoItem;
</code></pre>
<p>  Now let’s create a new root component that makes use of our components:</p>
<blockquote>
<p><em>Generate an App.jsx that makes use of</em> <strong><em>@Header.jsx</em></strong>*,* <strong><em>@TodoItem.jsx</em></strong>*,* <strong><em>@TodoContainer.jsx</em></strong>*, and* <strong><em>@Footer.jsx</em></strong> <em>appropriately. Use simple JavaScript variables as state data and implement whatever functions are relevant. Have a minimum of 5 to-do items.</em></p>
</blockquote>
<pre><code class="lang-javascript">
  <span class="hljs-comment">// ./src/App.jsx</span>
  <span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Header'</span>;
  <span class="hljs-keyword">import</span> TodoContainer <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/TodoContainer'</span>;
  <span class="hljs-keyword">import</span> TodoItem <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/TodoItem'</span>;
  <span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Footer'</span>;
  <span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [todos, setTodos] = useState([
      { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Complete project proposal'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Draft and finalize the project proposal for the client meeting.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Review code changes'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Go through the pull requests and provide feedback on the code changes.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Prepare presentation'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Create slides for the upcoming team meeting presentation.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">4</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Update documentation'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Review and update the project documentation with recent changes.'</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'Team meeting'</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">'Attend the weekly team sync-up and discuss project progress.'</span> },
    ]);
    <span class="hljs-keyword">const</span> handleUpdateTask = <span class="hljs-function">(<span class="hljs-params">updatedTask</span>) =&gt;</span> {
      setTodos(todos.map(<span class="hljs-function"><span class="hljs-params">todo</span> =&gt;</span> 
        todo.id === updatedTask.id ? { ...todo, ...updatedTask } : todo
      ));
    };
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col min-h-screen"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-grow"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TodoContainer</span>&gt;</span>
            {todos.map(todo =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">TodoItem</span> 
                <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>
                <span class="hljs-attr">title</span>=<span class="hljs-string">{todo.title}</span>
                <span class="hljs-attr">description</span>=<span class="hljs-string">{todo.description}</span>
                <span class="hljs-attr">onUpdateTask</span>=<span class="hljs-string">{(updatedTask)</span> =&gt;</span> handleUpdateTask({ id: todo.id, ...updatedTask })}
              /&gt;
            ))}
          <span class="hljs-tag">&lt;/<span class="hljs-name">TodoContainer</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
  };
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>  Here’s what we now have:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726667940335/69911f3c-063b-40cc-80d6-27fd7b5aaa76.png" alt="Screenshot of the web app with the TodoList and TodoItem components" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-improve-and-manage-existing-codebases-with-cody">How to Improve and Manage Existing Codebases with Cody</h2>
<p>To further boost your productivity, Cody offers <a target="_blank" href="https://sourcegraph.com/docs/cody/capabilities/commands#prompts">prompts</a> and <a target="_blank" href="https://sourcegraph.com/docs/cody/capabilities/commands#commands">commands</a>.</p>
<p>You can save frequently used prompts for future use and share them with other team members within your organization. Commands offer quick, ready-to-use shortcuts for common coding tasks like writing, describing, fixing, and identifying code issues.</p>
<h3 id="heading-lets-add-documentation-to-our-code"><strong>Let’s add documentation to our code!</strong></h3>
<p>Firstly, select the code you would like to generate documentation for, we’ll use <code>TodoItem.jsx</code> for this example. Run the <strong>Document Code</strong> command, and we get a JSDoc docstring for the Footer component class.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726668014952/8c65682a-d931-46df-bf49-df447df74443.png" alt="Adding documentation to the code" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-lets-make-our-components-more-accessible-and-inclusive"><strong>Let’s make our components more accessible and inclusive</strong></h3>
<p>We can save our favorite and frequently used chat prompts on the <a target="_blank" href="https://sourcegraph.com/prompts/new"><strong>Prompt Library</strong></a> via the Sourcegraph's Web UI.</p>
<p>Let’s create a new prompt to improve the accessibility of our web app and ensure it adheres to the <a target="_blank" href="https://www.w3.org/WAI/standards-guidelines/wcag/">WCAG standard</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726668303154/a83398cb-90b7-4d28-9c8b-a15b4751366c.png" alt="Creating a new prompt on the Prompt Library" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>We can now use this prompt back in VS Code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726668369322/5279315d-283f-4d24-9a8d-62316f7ec3b4.png" alt="Using our newly created prompt in VS Code" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Above you can see the results from our accessibility prompt.</p>
<h3 id="heading-how-to-use-ai-responsibly-for-code-generation">How to Use AI Responsibly for Code Generation</h3>
<p>AI tools can significantly speed up your workflow, but it's essential to remember that AI is still a work in progress. As powerful as these tools can be, they can also make mistakes or "hallucinate," producing code that seems correct but doesn't actually work in your specific context.</p>
<p>To use AI responsibly for coding, it’s crucial to approach it from a point of understanding what needs to be done. Before relying on AI, make sure you have a solid grasp of the task at hand. AI works best when used as a productivity booster rather than a replacement for your expertise.</p>
<p>Here are a few key things to keep in mind when working with AI-generated code:</p>
<ul>
<li><p><strong>Double-check the code:</strong> Always run and test the code generated by AI. Even if it looks correct at first glance, there could be subtle errors or inefficiencies. It's your responsibility to ensure the code is functional and meets your project’s requirements.</p>
</li>
<li><p><strong>Understand the output:</strong> Before using any AI-suggested code, take time to understand how it works. This will allow you to quickly identify any mistakes and integrate the code effectively with the rest of your project.</p>
</li>
</ul>
<p>When used thoughtfully and carefully, AI can make your development process more efficient and help you focus on higher-level tasks. However, it’s essential to balance its use with human oversight to ensure the quality and accuracy of the code you're building.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Creating user-friendly UIs has traditionally been time-consuming and difficult to manage. But using Cody, we created an interactive and attractive user interface with minimal effort. Cody supported us throughout the entire development process.</p>
<p>Here are some potential enhancements you can make:</p>
<ul>
<li><p>We can’t create or delete tasks. Try to fix that.</p>
</li>
<li><p>Develop a component to display total tasks and accumulated hours.</p>
</li>
<li><p>Add test cases for each component. We can do this quickly using the <strong>Generate Unit Tests</strong> command.</p>
</li>
</ul>
<p>If you enjoyed learning about Cody, you can try more of its features and applications. <a target="_blank" href="https://sourcegraph.com/cody">Sign up for a free, forever account</a> and boost your productivity designing, creating, documenting, and managing applications.</p>
<h3 id="heading-further-reading">Further reading</h3>
<p>If you want to learn more, you can read this article about chat-oriented programming (CHOP) and how to use Cody for it: <a target="_blank" href="https://sourcegraph.com/blog/chat-oriented-programming-in-action">Chat-oriented programming (CHOP) in action</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Best Go Tools to Use for Your Frontend Projects ]]>
                </title>
                <description>
                    <![CDATA[ The Go programming language is renowned for its efficiency in backend development. But it's pretty easy to extend its capabilities to frontend projects as well. This article explores essential Go tools that empower frontend developers to enhance thei... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/go-tools-for-your-frontend-projects/</link>
                <guid isPermaLink="false">66c5a3ca28ecdacfc27af448</guid>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ekemini Samuel ]]>
                </dc:creator>
                <pubDate>Wed, 13 Dec 2023 18:45:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/12/go-tools-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The <a target="_blank" href="https://go.dev/">Go programming language</a> is renowned for its efficiency in <a target="_blank" href="https://blog.boot.dev/golang/become-golang-backend-dev/">backend development</a>. But it's pretty easy to extend its capabilities to frontend projects as well.</p>
<p>This article explores essential Go tools that empower frontend developers to enhance their workflows, emphasizing simplicity and productivity.</p>
<h3 id="heading-prequisites">Prequisites</h3>
<p>Before diving into the exploration of the Go tools for frontend projects, ensure you have the following in place:</p>
<ul>
<li>Go installed on your machine. You can download it from the <a target="_blank" href="https://go.dev/doc/install">official Go website</a>.</li>
<li>Code editor of your choice installed, such as <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a>, <a target="_blank" href="https://www.jetbrains.com/go/">GoLand</a>, or <a target="_blank" href="https://zed.dev/">Zed</a>.</li>
<li>Basic understanding of Go, check out <a target="_blank" href="https://go.dev/doc/">Go's documentation</a> or this <a target="_blank" href="https://www.freecodecamp.org/news/go-beginners-handbook/">Go handbook</a>.</li>
</ul>
<h2 id="heading-go-tools-for-your-frontend-projects">Go Tools for Your Frontend Projects</h2>
<p>Here are five Go tools you can try out for your cool frontend projects:</p>
<h3 id="heading-1-fiber-a-performant-web-framework">1. Fiber: A Performant Web Framework</h3>
<p><a target="_blank" href="https://gofiber.io/">Fiber</a> is an <a target="_blank" href="https://expressjs.com/">Express.js</a>-inspired web framework for Go, known for its high performance and minimalist design. It facilitates efficient routing and middleware support, making it ideal for frontend-backend communication and API development.</p>
<p>Fiber is particularly useful in creating robust and performant APIs that frontend applications can interact with seamlessly.</p>
<p><strong>Code example:</strong></p>
<p>This example will be a simple web app where users can enter their names, submit a form, and receive a personalized greeting in return. </p>
<p>In your code editor, use this struture for the project:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/project-tree.png" alt="project-tree" width="600" height="400" loading="lazy"></p>
<p>Enter this code for the <code>main.go</code> file:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>
    <span class="hljs-string">"github.com/gofiber/template/html/v2"</span>
)

<span class="hljs-comment">// RenderForm renders the HTML form.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">RenderForm</span><span class="hljs-params">(c *fiber.Ctx)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> c.Render(<span class="hljs-string">"form"</span>, fiber.Map{})
}

<span class="hljs-comment">// ProcessForm processes the form submission.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessForm</span><span class="hljs-params">(c *fiber.Ctx)</span> <span class="hljs-title">error</span></span> {
    name := c.FormValue(<span class="hljs-string">"name"</span>)
    greeting := fmt.Sprintf(<span class="hljs-string">"Hello, %s!"</span>, name)
    <span class="hljs-keyword">return</span> c.Render(<span class="hljs-string">"greeting"</span>, fiber.Map{<span class="hljs-string">"Greeting"</span>: greeting})
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    app := fiber.New(fiber.Config{
        Views: html.New(<span class="hljs-string">"./views"</span>, <span class="hljs-string">".html"</span>),
    })

    <span class="hljs-comment">// Serve static files (HTML templates and stylesheets).</span>
    app.Static(<span class="hljs-string">"/"</span>, <span class="hljs-string">"./static"</span>)

    <span class="hljs-comment">// Define routes.</span>
    app.Get(<span class="hljs-string">"/"</span>, RenderForm)
    app.Post(<span class="hljs-string">"/submit"</span>, ProcessForm)

    <span class="hljs-comment">// Start the Fiber app on port 8080.</span>
    app.Listen(<span class="hljs-string">":8080"</span>)
}
</code></pre>
<p>The <strong>views</strong> folder contains two HTML files that display the form and response on the browser. Enter the code for them:</p>
<p><code>form.html</code></p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- views/form.html --&gt;</span>

<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Fiber Example<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/styles/main.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/submit"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Enter your name:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p><code>greeting.html</code></p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- views/greeting.html --&gt;</span>

<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Fiber Example<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/styles/main.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{.Greeting}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Next, in the <strong>styles</strong> folder inside the <strong>static</strong> directory, enter this code for the <code>main.css</code>:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* main.css */</span>

<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f0f0f0</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span>;
  }

  <span class="hljs-selector-tag">form</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
  }

  <span class="hljs-selector-tag">label</span> {
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
  }

  <span class="hljs-selector-tag">input</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
  }

  <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#007bff</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
  }

  <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#0056b3</span>;
  }
</code></pre>
<p>The code for this project can be accessed on this <a target="_blank" href="https://github.com/Tabintel/go-tools/tree/master/fiber">GitHub repo</a>.</p>
<h4 id="heading-fibers-view-enginehttpsdocsgofiberioguidetemplates"><a target="_blank" href="https://docs.gofiber.io/guide/templates/">Fiber's View Engine</a></h4>
<p>Fiber integrates with view engines, and we'll use the HTML template engine in this example. Setting up the view engine is straightforward, allowing for a clean separation of concerns between the backend and frontend.</p>
<pre><code class="lang-go">app := fiber.New(fiber.Config{
    Views: html.New(<span class="hljs-string">"./views"</span>, <span class="hljs-string">".html"</span>),
})
</code></pre>
<p><a target="_blank" href="https://docs.gofiber.io/api/app/#:~:text=Use%20the%20Static%20method%20to,a%20request%20on%20a%20directory.&amp;text=If%20you%20want%20to%20have,settings%20for%20serving%20static%20files.">Serving Static Assets</a></p>
<p>Fiber simplifies the serving of static assets, ensuring efficient delivery of stylesheets, images, and client-side JavaScript files. In our example, styles are stored in the static folder and linked in the HTML templates.</p>
<pre><code class="lang-go"><span class="hljs-comment">// Serve static files (HTML templates and stylesheets)</span>
app.Static(<span class="hljs-string">"/"</span>, <span class="hljs-string">"/.static"</span>)
</code></pre>
<p>Run the program using <code>main.go</code> to start the server on port 8080. It diplays a console output generated by the Fiber framework.</p>
<pre><code class="lang-cmd">go run main.go
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/fib.png" alt="fib" width="600" height="400" loading="lazy"></p>
<p>Visit http://127.0.0.1:8080/ in your browser to see the response. Here is how it works:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/fibd.gif" alt="fibd" width="600" height="400" loading="lazy"></p>
<h3 id="heading-2-buffalo-holistic-web-development-ecosystem">2. Buffalo: Holistic Web Development Ecosystem</h3>
<p><a target="_blank" href="https://gobuffalo.io/">Buffalo</a> is a holistic web development ecosystem for Go, providing a complete set of tools, libraries, and conventions for building modern web applications. It simplifies frontend and backend development by offering <a target="_blank" href="https://pkg.go.dev/github.com/facily-tech/go-scaffold">scaffolding</a> and <a target="_blank" href="https://medium.com/@adamszpilewicz/effortless-hot-reloading-in-golang-harnessing-the-power-of-viper-4b54703f7424">hot-reload features</a>.</p>
<p>Buffalo is beneficial for rapidly setting up full-stack web applications, enabling developers to focus on building features rather than dealing with boilerplate code.</p>
<p>A typical Buffalo project has the following structure:</p>
<pre><code class="lang-shell">myapp/
|-- actions/
|-- grifts/
|-- migrations/
|-- models/
|-- public/
|-- templates/
|-- go.mod
|-- go.sum
|-- main.go
</code></pre>
<ul>
<li>actions: Contains handlers for web routes.</li>
<li>grifts: Houses Grift tasks for automation.</li>
<li>migrations: Stores database migration files.</li>
<li>models: Defines data models.</li>
<li>public: Holds static assets like stylesheets and images.</li>
<li>templates: Stores HTML templates.</li>
<li><code>go.mod and go.sum</code>: Track project dependencies and their versions.</li>
<li><code>main.go</code>: Entry point of Buffalo app, initializes and starts the server.</li>
</ul>
<p>Run this command to create a new Buffalo application:</p>
<pre><code class="lang-go">buffalo <span class="hljs-built_in">new</span> myapp
</code></pre>
<h3 id="heading-3-grift-automating-tasks-in-go">3. Grift: Automating Tasks in Go</h3>
<p><a target="_blank" href="https://github.com/markbates/grift">Grift</a> is a task runner ideal for automating various project-related tasks. It manages frontend build processes, asset handling, and deployment automation. </p>
<p>Grift proves valuable when automating repetitive tasks in the frontend development workflow, enhancing efficiency and reducing manual intervention.</p>
<p><strong>Code example:</strong></p>
<p>This example demonstrates how Grift can be utilized to bundle and minify JavaScript files in a frontend project.</p>
<p>Project Structure:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/gf.png" alt="gf" width="600" height="400" loading="lazy"></p>
<p>Here are the JavaScript files:</p>
<p><code>file1.js</code> and <code>file2.js</code>, are in the src directory.</p>
<p><code>file</code>.js`</p>
<pre><code class="lang-js"><span class="hljs-comment">// file1.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params">name</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Hello, <span class="hljs-subst">${name}</span>!`</span>);
}
greet(<span class="hljs-string">"John"</span>);
</code></pre>
<p><code>file2.js</code></p>
<pre><code class="lang-js"><span class="hljs-comment">// file2.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">multiply</span>(<span class="hljs-params">a, b</span>) </span>{
  <span class="hljs-keyword">return</span> a * b;
}
<span class="hljs-built_in">console</span>.log(multiply(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>));
</code></pre>
<h4 id="heading-grift-taskhttpsgithubcommarkbatesgrift"><a target="_blank" href="https://github.com/markbates/grift">Grift Task</a></h4>
<p>Grift task <a target="_blank" href="https://medium.com/everything-for-developers/minification-and-bundle-c8e8908ae5c8">bundles and minifies these JavaScript</a> files into a single file named bundle.js. The task is defined in the main.go file of the project.</p>
<p><code>main.go</code></p>
<pre><code class="lang-go"><span class="hljs-comment">// main.go</span>
<span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"github.com/markbates/grift/grift"</span>
    <span class="hljs-string">"github.com/tdewolff/minify/v2"</span>
    <span class="hljs-string">"github.com/tdewolff/minify/v2/js"</span>
    <span class="hljs-string">"io/ioutil"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"path/filepath"</span>
)

<span class="hljs-comment">// BundleAndMinifyJS is a Grift task that bundles and minifies JavaScript files.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">BundleAndMinifyJS</span><span class="hljs-params">(c *grift.Context)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-comment">// ... (code omitted for brevity)</span>

    fmt.Printf(<span class="hljs-string">"JavaScript files bundled and minified successfully. Output: %s\n"</span>, outputPath)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// main function registers the Grift task and runs it.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    grift.Desc(<span class="hljs-string">"bundle-js"</span>, <span class="hljs-string">"Bundle and minify JavaScript files"</span>)
    grift.Add(<span class="hljs-string">"bundle-js"</span>, BundleAndMinifyJS)

    taskName := <span class="hljs-string">"bundle-js"</span>
    context := &amp;grift.Context{}
    <span class="hljs-keyword">if</span> err := grift.Run(taskName, context); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">if</span> err.Error() == <span class="hljs-string">"task not found"</span> {
            fmt.Println(<span class="hljs-string">"Task not found."</span>)
            os.Exit(<span class="hljs-number">1</span>)
        }
        <span class="hljs-built_in">panic</span>(err)
    }
}
</code></pre>
<h4 id="heading-running-grift-task">Running Grift Task</h4>
<p>To execute Grift task, run the following command in your terminal:</p>
<pre><code class="lang-cmd">go run main.go bundle-js
</code></pre>
<p>This Grift task will bundle and minify <code>file1.js</code> and <code>file2.js</code> into a single file named <code>bundle.js</code> within the dist directory.</p>
<p>Here is the output, showing the new directory and file <code>bundle.js</code> created and also the message printed in the terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/gjs.png" alt="gjs" width="600" height="400" loading="lazy"></p>
<h3 id="heading-4-gomponents-creating-web-ui-components-in-go">4. Gomponents: Creating Web UI Components in Go</h3>
<p><a target="_blank" href="https://www.gomponents.com/">Gomponents</a> is a library for generating HTML components in Go. It simplifies the creation of <a target="_blank" href="https://www.sencha.com/blog/7-reasons-to-use-ui-component-libraries-to-style-web-apps/#:~:text=A%20UI%20component%20library%20is,front%2Dend%20applications%20and%20websites.">UI components for frontend applications</a>.</p>
<p><a target="_blank" href="https://www.gomponents.com/">Gomponents</a> is particularly useful when you want to generate HTML components dynamically within your Go code, allowing for more flexibility and abstraction in building UIs.</p>
<p><strong>Code example:</strong>
This web app shows how to use Gomponents and is inspired by the <a target="_blank" href="https://github.com/maragudk/gomponents/blob/main/examples/tailwindcss/tailwindcss.go">Gomponents tailwindcss</a> example by <a target="_blank" href="https://www.linkedin.com/in/markus-w%C3%BCstenberg/">Markus Wüstenberg.</a>.</p>
<p>Create a file <code>main.go</code> and enter the code below:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>

    g <span class="hljs-string">"github.com/maragudk/gomponents"</span>
    c <span class="hljs-string">"github.com/maragudk/gomponents/components"</span>
    . <span class="hljs-string">"github.com/maragudk/gomponents/html"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    http.Handle(<span class="hljs-string">"/"</span>, createHandler(<span class="hljs-string">"Welcome!"</span>, simpleComponent(<span class="hljs-string">"Hello, this is the main page!"</span>)))
    http.Handle(<span class="hljs-string">"/contact"</span>, createHandler(<span class="hljs-string">"Contact"</span>, simpleComponent(<span class="hljs-string">"Contact us!"</span>)))
    http.Handle(<span class="hljs-string">"/about"</span>, createHandler(<span class="hljs-string">"About"</span>, simpleComponent(<span class="hljs-string">"About this site!"</span>)))

    <span class="hljs-comment">// Print a message indicating that the server is running</span>
    fmt.Println(<span class="hljs-string">"Server is running on http://localhost:8080"</span>)

    _ = http.ListenAndServe(<span class="hljs-string">"localhost:8080"</span>, <span class="hljs-literal">nil</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">createHandler</span><span class="hljs-params">(title <span class="hljs-keyword">string</span>, body g.Node)</span> <span class="hljs-title">http</span>.<span class="hljs-title">HandlerFunc</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        _ = Page(title, r.URL.Path, body).Render(w)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">simpleComponent</span><span class="hljs-params">(content <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">g</span>.<span class="hljs-title">Node</span></span> {
    <span class="hljs-keyword">return</span> Div(
        H1(g.Text(content)),
        P(g.Text(<span class="hljs-string">"This is a simple component."</span>)),
    )
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Page</span><span class="hljs-params">(title, path <span class="hljs-keyword">string</span>, body g.Node)</span> <span class="hljs-title">g</span>.<span class="hljs-title">Node</span></span> {
    <span class="hljs-keyword">return</span> c.HTML5(c.HTML5Props{
        Title:    title,
        Language: <span class="hljs-string">"en"</span>,
        Body: []g.Node{
            Navbar(path),
            Container(body),
        },
    })
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Navbar</span><span class="hljs-params">(currentPath <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">g</span>.<span class="hljs-title">Node</span></span> {
    <span class="hljs-keyword">return</span> Nav(Class(<span class="hljs-string">"navbar"</span>),
        Container(
            NavbarLink(<span class="hljs-string">"/"</span>, <span class="hljs-string">"Home"</span>, currentPath == <span class="hljs-string">"/"</span>),
            NavbarLink(<span class="hljs-string">"/contact"</span>, <span class="hljs-string">"Contact"</span>, currentPath == <span class="hljs-string">"/contact"</span>),
            NavbarLink(<span class="hljs-string">"/about"</span>, <span class="hljs-string">"About"</span>, currentPath == <span class="hljs-string">"/about"</span>),
        ),
    )
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NavbarLink</span><span class="hljs-params">(path, text <span class="hljs-keyword">string</span>, active <span class="hljs-keyword">bool</span>)</span> <span class="hljs-title">g</span>.<span class="hljs-title">Node</span></span> {
    <span class="hljs-keyword">return</span> A(Href(path), g.Text(text),
        c.Classes{
            <span class="hljs-string">"active"</span>: active,
        },
    )
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Container</span><span class="hljs-params">(children ...g.Node)</span> <span class="hljs-title">g</span>.<span class="hljs-title">Node</span></span> {
    <span class="hljs-keyword">return</span> Div(Class(<span class="hljs-string">"container"</span>), g.Group(children))
}
</code></pre>
<p>Run the code with this command to start the server:</p>
<pre><code class="lang-cmd">go run main.go
</code></pre>
<p>Then enter this URL in your browser to the Go components: http://localhost:8080 as shown here:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/gomp.png" alt="gomp" width="600" height="400" loading="lazy"></p>
<p>And we have the Home, About, and Contact components built with <a target="_blank" href="https://www.gomponents.com/">Gomponents</a>.</p>
<h3 id="heading-5-present-a-go-based-slide-deck-tool">5. Present: A Go-based Slide Deck Tool</h3>
<p><a target="_blank" href="https://pkg.go.dev/golang.org/x/tools/present?utm_source=godoc">Present</a> is a tool in the Go ecosystem used for creating slide decks and presentations. It allows developers to generate technical presentations or documentation directly in Go.</p>
<p>Present is beneficial when you need to create and share technical presentations about frontend development topics, project updates, or any other relevant information.</p>
<p>Here's how you can use Present in your projects:</p>
<p>First, create a file named <code>presentation.slide</code> in your code editor and enter this sample code:</p>
<pre><code class="lang-md"><span class="hljs-section"># My Frontend Tech Talk</span>

---

<span class="hljs-section">## Agenda</span>

<span class="hljs-bullet">1.</span> Introduction
<span class="hljs-bullet">2.</span> Project Scope
<span class="hljs-bullet">3.</span> Live Demo
<span class="hljs-bullet">4.</span> Q&amp;A
</code></pre>
<p>Then write your presentation content using <a target="_blank" href="https://commonmark.org/help/tutorial/">CommonMark</a>, a simple markup language. Use headings (#) for slides and --- to separate them.</p>
<p>Install the <a target="_blank" href="https://pkg.go.dev/golang.org/x/tools/present?utm_source=godoc">Present</a> tool like this:</p>
<pre><code class="lang-shell">go get -u golang.org/x/tools/cmd/present
</code></pre>
<p>Then run the Present tool in the directory containing your presentation file. Your browser then displays a presentation.</p>
<pre><code class="lang-shell">present
</code></pre>
<h2 id="heading-why-use-go-tools-in-frontend-projects">Why Use Go Tools in Frontend Projects?</h2>
<ul>
<li><p><strong>Efficiency:</strong> Go tools are designed to enhance efficiency, automate repetitive tasks, and reduce manual intervention in frontend development workflows.</p>
</li>
<li><p><strong>Performance:</strong> Tools like <a target="_blank" href="https://gofiber.io/">Fiber</a> are optimized for performance, ensuring that your frontend-backend communication is swift and responsive.</p>
</li>
<li><p><strong>Consistency:</strong> <a target="_blank" href="https://gobuffalo.io/">Buffalo's</a> conventions and project structure promote consistency, allowing developers to focus on building features rather than dealing with boilerplate code.</p>
</li>
<li><p><strong>Flexibility:</strong> <a target="_blank" href="https://www.gomponents.com/">Gomponents'</a> ability to generate HTML components dynamically in Go provides flexibility in constructing UIs within the language.</p>
</li>
<li><p><strong>Documentation and Presentation:</strong> <a target="_blank" href="https://pkg.go.dev/golang.org/x/tools/present?utm_source=godoc">Present</a> simplifies the creation of technical presentations and documentation, keeping your project insights and updates organized.</p>
</li>
</ul>
<p>The seamless integration of Go tools not only streamlines development but also aligns with Go's philosophy of simplicity and efficiency.</p>
<p>Experiment with these tools, and you'll see the positive impact they can have on your workflow. </p>
<p>Happy coding! 💜 </p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
