<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Simon Pfeiffer</title>
    <description>The latest articles on DEV Community by Simon Pfeiffer (@simoncodephere).</description>
    <link>https://dev.to/simoncodephere</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F554778%2F191d2cfd-adbf-4c23-ac17-1b5649667f07.png</url>
      <title>DEV Community: Simon Pfeiffer</title>
      <link>https://dev.to/simoncodephere</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/simoncodephere"/>
    <language>en</language>
    <item>
      <title>Build your own VS Code extension</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Mon, 10 Jun 2024 12:19:48 +0000</pubDate>
      <link>https://dev.to/codesphere/build-your-own-vs-code-extension-5gae</link>
      <guid>https://dev.to/codesphere/build-your-own-vs-code-extension-5gae</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2Fvscodetutorial-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2Fvscodetutorial-1.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio Code is a powerful code editor known for its extensibility. Users can install various extensions to fit their needs, and if something isn't available, they can build their own extensions to enhance productivity. To do this, an extension author needs to be familiar with the VS Code API and the development workflow for creating extensions.&lt;/p&gt;

&lt;p&gt;In this blog post, we will show you the framework we used to build an extension using a webview to display anything you like. We will guide you through the development of an extension, where you can modify the project to do specific tasks, and finally, we will show you how to officially publish an extension on the VS Code Marketplace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FVSCodeMarketplace.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FVSCodeMarketplace.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The published VS Code extension&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FVSCodeExtension.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FVSCodeExtension.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Extension listed in VS Code&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;p&gt;The VS Code extension will be a TypeScript project, and for the webview, we will use Svelte as the frontend framework. We have prepared a VS Code extension development template so that you can clone this repository and get started right away:&lt;/p&gt;

&lt;p&gt;GitHub repository: &lt;a href="https://github.com/codesphere-cloud/vscode-extension-template?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://github.com/codesphere-cloud/vscode-extension-template&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/codesphere-cloud/vscode-extension-template
&lt;span class="nb"&gt;cd &lt;/span&gt;vscode-extension-template
npm i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download the VS Code template and install all the dependencies you need for developing your extension.&lt;/p&gt;

&lt;p&gt;That's it. You are ready to test and build your extension. First, we want to make sure that the tutorial extension works. When developing an extension, we want to test every change right away, and testing should be a smooth workflow.&lt;/p&gt;

&lt;p&gt;We can open a debug VS Code window called &lt;code&gt;Extension-Development-Host&lt;/code&gt; to test our extension. First, we run a command to compile our extension while working on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tip: open a new Terminal with this Keyboard combination: Ctrl + Shift + .&lt;/p&gt;

&lt;p&gt;This compiles our extension every time we are changing our project. now we open either &lt;code&gt;src/SidebarProvider.ts&lt;/code&gt; or &lt;code&gt;src/extension.ts&lt;/code&gt; file and push &lt;code&gt;F5&lt;/code&gt; to open a new extension-developing-host. an this is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2Ftutorial-extension.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2Ftutorial-extension.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Activated tutorial extension in an extension-development-host&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The template extension we provide adds a view to the activity bar on the side with a webview a user can interact with. We kept this tutorial template as simple as possible, so you can follow along with this tutorial and get to know the project better while adding new features to it.&lt;/p&gt;

&lt;p&gt;This tutorial focuses on creating a webview in the side panel of VS Code, but you can do much more than that. In the last section of this blog post, we will list a bunch of links to useful resources for further reference.&lt;/p&gt;

&lt;p&gt;Now, we will build a GitHub Copilot-style chat interface together, but with a locally installed Llama.cpp.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project idea
&lt;/h2&gt;

&lt;p&gt;The idea is to replicate the GitHub Co-Pilot chat interface on VS Code but with a locally self-hosted Llama.cpp instance. The use case for this would be if you are working on a project which data should not be exposed to other services. Such an approach of using AI would be data compliant.&lt;/p&gt;

&lt;p&gt;We need to set up our local Llama.cpp instance and use the server example of this repository to host our own OpenAI compatible chat completion, so we have a nice to use API. Now, we guide you through the process of setting up your locally installed Llama.cpp instance.&lt;/p&gt;

&lt;p&gt;Firstly, you need to clone the GitHub repository of llama.cpp and build the project with &lt;code&gt;make&lt;/code&gt; . Make sure that you are in the root directory of the extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp/
make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on your local resources this command might take while. After completion we can continue to set up the OpenAI compatible server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;examples/server
make server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like before this might take a while. After building the server we need to download our model we want to use. The important thing is to use models in the .gguf format. You can browse avalable models on HuggingFace: &lt;a href="https://huggingface.co/models?library=gguf&amp;amp;sort=trending&amp;amp;ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://huggingface.co/models?library=gguf&amp;amp;sort=trending&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We chose the qwen2-7b-instruct-q8_0.gguf model: &lt;a href="https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF/tree/main?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF/tree/main&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is 8 GB and to download it takes a while. You can download it to your models/ directory in your root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd models/
wget https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF/resolve/main/qwen2-7b-instruct-q8_0.gguf?download=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply replace this link with the link to the model you want to use. Now, you need to wait until this model is downloaded.&lt;/p&gt;

&lt;p&gt;When downloading is finished you can use this model right away. Execute this command in the root directory of llama.cpp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod a+x ./server
./server -m models/qwen2-7b-instruct-q8_0.gguf -c 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And again, if you chose an other model replace the path to the model to the right file.&lt;/p&gt;

&lt;p&gt;You can now use the &lt;code&gt;Llama.cpp&lt;/code&gt; interface on &lt;code&gt;localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FLlamacppServer.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FLlamacppServer.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Llama.cpp server on localhost&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can for sure optimize the performance of the model running now on your localhost, but lets stick with this for this tutorial because we want to build a VS Code extension.&lt;/p&gt;

&lt;p&gt;Since we are using the API we wont need that UI. Here is a documentation about the API: &lt;a href="https://github.com/ggerganov/llama.cpp/tree/master/examples/server?ref=codesphere.ghost.io#API-Endpoints" rel="noopener noreferrer"&gt;https://github.com/ggerganov/llama.cpp/tree/master/examples/server#API-Endpoints&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Features to add
&lt;/h2&gt;

&lt;p&gt;Inside our sidepanel extension we want to add these features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start your AI model out of VS Code&lt;/li&gt;
&lt;li&gt;place an input field for your prompts&lt;/li&gt;
&lt;li&gt;use the completion endpoint of our model to generate a response&lt;/li&gt;
&lt;li&gt;display the response to our webview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want to keep this extension simple and if you want to you can take the challenge and improve it as you like! 🤗&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a Button to Start and Stop Llama.cpp
&lt;/h3&gt;

&lt;p&gt;Let's create buttons so that we don't have to type the command in the terminal every time we want to use our Llama instance. Additionally, it would be convenient to have a button to stop the model from running, as we don't want to manually find out the PID in order to kill the process.&lt;/p&gt;

&lt;p&gt;The idea is to add icons to the top-right corner. Perhaps you're familiar with some extensions that have these small icons in the top-right corner (e.g., GitHub Co-Pilot).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FextensionButtons.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FextensionButtons.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Icons in the right top corner&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To add these buttons, we need to modify our package.json file first. The &lt;code&gt;package.json&lt;/code&gt; file in our project is an important file filled with various metadata about your extension. This file serves as one of the main entry points to your extension. Inside the &lt;code&gt;package.json&lt;/code&gt;, there is a field called &lt;code&gt;menus&lt;/code&gt;. We will add two additional icons to our existing icon by appending these objects to that array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"menus": {
      "view/title": [
        {
          "command": "tutorial.tutorial",
          "when": "view == tutorial-sidebar",
          "group": "navigation"
        },
        {
          "command": "tutorial.start",
          "when": "view == tutorial-sidebar &amp;amp;&amp;amp; !tutorial.modelIsRunning",
          "group": "navigation"
        },
        {
          "command": "tutorial.stop",
          "when": "view == tutorial-sidebar &amp;amp;&amp;amp; tutorial.modelIsRunning",
          "group": "navigation"
        }
      ]
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;when&lt;/code&gt; attribute, you can specify conditions for when the icons should be displayed. We can control variables through the VS Code API. We want the start icon to be displayed when the model is not running, and when it is running, we want to display the stop button.&lt;/p&gt;

&lt;p&gt;We aim to use small icons for these commands. There is a variety of internal icons available for use: &lt;a href="https://code.visualstudio.com/api/references/icons-in-labels?ref=codesphere.ghost.io#icon-listing" rel="noopener noreferrer"&gt;Icon Listing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;package.json&lt;/code&gt; file, there is a &lt;code&gt;commands&lt;/code&gt; field where we can add such icons. You can simply copy this snippet and replace it with the existing one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"commands": [
      {
        "command": "tutorial.tutorial",
        "title": "tutorial",
        "icon": "$(log-out)",
        "category": "Tutorial command"
      },
      {
        "command": "tutorial.start",
        "title": "Start",
        "icon": "$(play)",
        "category": "Tutorial command"
      },
      {
        "command": "tutorial.stop",
        "title": "Stop",
        "icon": "$(stop)",
        "category": "Tutorial command"
      }
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we save with Ctrl + S and navigate to our Extension Development Host, then press Ctrl + R to refresh the window. You should now see the icons displayed in the top-right corner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FiconsInExtension.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FiconsInExtension.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Icons in the top corner&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Perhaps you noticed that the stop button is not displayed. This is because of the condition we provided in the &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let's code the commands that are executed when clicking on these icons. We need to open the src/extension.ts file to register our commands with the extension. We register the commands in the activation function of this file. In our &lt;code&gt;package.json&lt;/code&gt;, there is a field named &lt;code&gt;activationEvents&lt;/code&gt; where we can specify when the activation function is executed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"activationEvents": [
    "onStartupFinished"
  ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The activation event is set to &lt;code&gt;onStartupFinished&lt;/code&gt;. This means the activation function is executed when VS Code is fully loaded. You can copy this code snippet and paste it into &lt;code&gt;extension.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context.subscriptions.push(
  vscode.commands.registerCommand('tutorial.start', async () =&amp;gt; {
    vscode.commands.executeCommand('setContext', 'tutorial.modelIsRunning', true);
  })
);

context.subscriptions.push(
  vscode.commands.registerCommand('tutorial.stop', async () =&amp;gt; {
    vscode.commands.executeCommand('setContext', 'tutorial.modelIsRunning', false);
  })
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you reload the Extension Development Host with Ctrl + R, you can test these icons, and you will notice that after clicking, the icon changes. This is the result of using the condition. We can change the context for the modelIsRunning variable during the runtime of your extension.&lt;/p&gt;

&lt;p&gt;Now, we actually want to add functionality to our buttons. We can do that by simply extending the &lt;code&gt;registerCommand()&lt;/code&gt; code block with the logic we want to have.&lt;/p&gt;

&lt;p&gt;We want to start our LLama.cpp instance when clicking &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt; it when clicking &lt;code&gt;stop&lt;/code&gt;. We need to execute these bash commands. You can again simply copy and paste this code snippet into the code you already have:&lt;/p&gt;

&lt;p&gt;a) start Llama.cpp on localhost:8080:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context.subscriptions.push(
  vscode.commands.registerCommand('tutorial.start', async () =&amp;gt; {

    const extension = vscode.extensions.getExtension('Tutorial.tutorial')?.extensionPath;

    // Your Llama.cpp folder has to be inside the extension folder
    const serverPath = path.join(extension, 'llama.cpp', 'server');
    const modelPath = path.join(extension, 'llama.cpp', 'models', 'qwen2-7b-instruct-q8_0.gguf');
    const bashcommand = `${serverPath} -m ${modelPath} -c 2048`;

    const test = 'echo hi';

    exec (bashcommand, (error, stdout, stderr) =&amp;gt; { 
        if (error) {
            console.error(`exec error: ${error}`);
            return;
        }

        if (stderr) {
            console.error(`stderr: ${stderr}`);
            return;
        }

        console.log(`stdout: ${stdout}`);
    });
    vscode.commands.executeCommand('setContext', 'tutorial.modelIsRunning', true);
  })
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: We will save the PID (process ID) of the process to our extension's globalStorage to ensure that we can kill this process with our extension's stop button. The globalStorage can be used for state management in our extension.&lt;/p&gt;

&lt;p&gt;b) stop Llama.cpp server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context.subscriptions.push(
    vscode.commands.registerCommand('tutorial.stop', async () =&amp;gt; {
        const pid = context.globalState.get("codesphere.runningPID");
        console.log(pid)

        const bashcommand = `kill ${pid as number + 1}`;

        const childProcess = exec(bashcommand, (error, stdout, stderr) =&amp;gt; {
            if (error) {
                console.error(`exec error: ${error}`);
                return;
            }

            if (stderr) {
                console.error(`stderr: ${stderr}`);
                return;
            }

            console.log(`stdout: ${stdout}`);
        });

        context.globalState.update("codesphere.runningPID", "");
        vscode.commands.executeCommand('setContext', 'tutorial.modelIsRunning', false);
    })
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We loaded the PID from our &lt;code&gt;globalStorage&lt;/code&gt; and used it to terminate the correct process. And there we have it! Now we can start and stop our local Llama.cpp model as needed.&lt;/p&gt;

&lt;p&gt;Tip: We actually have a webview developer console inside the extension development host to debug our extension. You can open it as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + P&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;select &lt;code&gt;Developer: Open Webview Developer Tools&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FWebviewConsole.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FWebviewConsole.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Developer Console inside VS Code&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Place an input field to our webview
&lt;/h3&gt;

&lt;p&gt;The user needs to place the prompt somewhere. Let's create the UI for that within our Svelte file. For that, we simply need HTML, CSS, and JavaScript for the functionality.&lt;/p&gt;

&lt;p&gt;In our Svelte file, we have a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag for JavaScript code and a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag for CSS. In the rest of the file, you can place the HTML code. Let's keep it simple, and if you want, you can practice your UI/UX design by enhancing this code as you like.&lt;/p&gt;

&lt;p&gt;Add the following two code snippets to &lt;code&gt;src/webviews/components&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;a) CSS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.prompt-container {
        display: flex;
        align-items: center;
        padding: 10px;
        width: 100%;
        max-width: 600px;
        margin: 0 auto;
        position: fixed;
        bottom: 0;
        left: 0;
    }

    .prompt-input {
        flex: 1;
        padding: 10px;
        border: none;
        outline: none;
        font-size: 16px;
    }

    .prompt-button {
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 10px 20px;
        font-size: 16px;
        border: none;
        cursor: pointer;
        border-radius: 4px;
        margin-left: 10px;
        width: 50px;
        height: 50px;
        align-self: end;  
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b) HTML&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="prompt-container"&amp;gt;
        &amp;lt;textarea type="text" placeholder="Ask your model something about your code" wrap="hard" class="prompt-input"&amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;button class="prompt-button"&amp;gt;
            &amp;lt;svg xmlns="http://www.w3.org/2000/svg" width="100px" height="100px" viewBox="0 0 16 16"&amp;gt;
                &amp;lt;path fill="currentColor" fill-rule="evenodd" d="m4.25 3l1.166-.624l8 5.333v1.248l-8 5.334l-1.166-.624zm1.5 1.401v7.864l5.898-3.932z" clip-rule="evenodd"/&amp;gt;
            &amp;lt;/svg&amp;gt;
        &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FinputField.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FinputField.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Added input field&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we didn't add a color to our button, but it's blue. That's because in our project, we have some standard VS Code styles imported. You can find the styles in the media directory of our project.&lt;/p&gt;
&lt;h3&gt;
  
  
  Split the webview to two different sections
&lt;/h3&gt;

&lt;p&gt;It's time to split up our webview since the HelloWorld template has nothing to do with our chat interface. We can have multiple webviews in our sidebar simultaneously, like an accordion, where you can open and close the different webviews as you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"views": {
      "tutorial-sidebar-view": [
        {
          "type": "webview",
          "id": "tutorial-sidebar",
          "name": "Tutoial",
          "icon": "media/tutorial.svg",
          "contextualTitle": "Tutorial"
        },
        {
          "type": "webview",
          "id": "tutorial-chat",
          "name": "Chat",
          "icon": "media/tutorial.svg",
          "contextualTitle": "Chat"
        }
      ]
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that simple trick we created a second webview window inside our sidebar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2F2ndWindowSidebar.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2F2ndWindowSidebar.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Second window in the sidebar&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The content in our new chat window is loading infinitely because we haven't registered content for that window in our extension. Let's change that by adding code in src/extension.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const chatProvider = new ChatProvider(context.extensionUri, context);
context.subscriptions.push(
  vscode.window.registerWebviewViewProvider(
  "tutorial-chat",
  chatProvider
  )
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you refresh the extension development host, you will see that the content is loaded into the second webview. The reason this works without adding extra files is that we provided the necessary files in the template. Here is a list of the files you need to add to register this new webview:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ChatProvider.ts&lt;/code&gt; in &lt;code&gt;/src&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Chat.svelte&lt;/code&gt; in &lt;code&gt;webviews/components&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chat.ts&lt;/code&gt; in &lt;code&gt;webview/pages&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are about to create another webview and you are using our &lt;code&gt;SidebarProvider.ts&lt;/code&gt; as a template for that, you need to change the compiled JavaScript and CSS files to the correct ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const styleResetUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "media", "reset.css")
    );
    const scriptUri = webview.asWebviewUri(
      // change here
      vscode.Uri.joinPath(this._extensionUri, "out", "compiled/chat.js")
    );
    const styleMainUri = webview.asWebviewUri(
      // change here
      vscode.Uri.joinPath(this._extensionUri, "out", "compiled/chat.css")
    );
    const styleVSCodeUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "media", "vscode.css")
    );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add functionality to our chat-interface
&lt;/h3&gt;

&lt;p&gt;Now we have our UI, but we need the logic for handling our prompts and displaying the response. In order to do that, we need to send our prompt to our model via the API and send the response to our webview so that we can display it. It follows the same pattern when implementing logic for our extension:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send data from the webview to our extension via the postMessage method.&lt;/li&gt;
&lt;li&gt;Handle the message within our extension's code (e.g., ChatProvider.ts) inside the switch-case block.&lt;/li&gt;
&lt;li&gt;Send back data to our webview with the postMessage method.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, we define a function to forward our prompt to the extension and add this prompt to our chat interface. You can replace the &lt;code&gt;someMessage()&lt;/code&gt; function with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function someMessage() {
    // Clear the textarea
    document.getElementById('promptInput').value = '';

    // add textblock to chat interface
    const chat = document.createElement('div');
    let randomId = generateRandomId();
    chat.innerHTML = `&amp;lt;div style=" background-color: #00BCFF; 
                                    color: black; 
                                    padding: 10px; 
                                    margin: 10px; 
                                    border-radius: 10px; 
                                    display: inline-block;"&amp;gt;${prompt}&amp;lt;/div&amp;gt;`;
    document.getElementById('chatContainer').appendChild(chat);

    // Create response div
    const responseDiv = document.createElement('div');
    // styling
    responseDiv.style = "background-color: #6F40D3; color: black; padding: 10px; margin: 10px; border-radius: 10px; display: inline-block;";
    responseDiv.id = randomId;
    document.getElementById('chatContainer').appendChild(responseDiv);

    vscode.postMessage({
        type: 'prompt',
        value: {
            prompt: prompt,
            id: randomId
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;postMessage()&lt;/code&gt; method will send a message to our extension, and the &lt;code&gt;type&lt;/code&gt; property of the message body will serve as the identifier for which case in our switch-case code block will be executed within the &lt;code&gt;ChatProvider.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;webviewView.webview.onDidReceiveMessage(async (data) =&amp;gt; {
      switch (data.type) {
        case "prompt": {
          if (!data.value) {
            return;
          }

          let prompt = data.value.prompt;
          let id = data.value.id;

          const Test = async (prompt: string, id: any) =&amp;gt; {
            try {
              let response = await fetch("http://127.0.0.1:8080/completion", {
                method: 'POST',
                headers: {
                  'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                  prompt,
                  n_predict: 30,
                  stream: true,
                }),
              });

              if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
              }

              if (!response.body) {
                throw new Error('Response body is null');
              }

              const reader = response.body.getReader();
              const decoder = new TextDecoder();
              let result = '';

              while (true) {
                const { done, value } = await reader.read();
                if (done) {
                  break;
                }
                result += decoder.decode(value, { stream: true });

                const lines = result.split('\n');
                for (const line of lines) {
                  if (line.startsWith('data:')) {
                    try {
                      const json = JSON.parse(line.substring(5).trim());
                      console.log(json.content);
                      let token = json.content;
                      this._view?.webview.postMessage({
                        type: "response",
                        value: token,
                        id: id
                      });
                    } catch (e) {
                      console.error('Error parsing JSON:', e);
                    }
                  }
                }
                result = lines[lines.length - 1];
              }

              this._view?.webview.postMessage({
                type: "response",
                value: "done",
                id: id
              });
            } catch (error) {
              console.error('Error:', error);
            }
          };

          await Test(prompt, id);

          break;
        }
      }
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will handle our request to our running LLM. We call it in stream mode, which means that every token is sent separately back. Every time we get a token, we send this token back to our extension via the &lt;code&gt;postMessage&lt;/code&gt; method so that we can add it to our chat interface within the webview's code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onMount(() =&amp;gt; {
    window.addEventListener('message', event =&amp;gt; {
        const message = event.data; // The JSON data our extension sent
        console.log(`message received: ${JSON.stringify(message)}`);
        switch (message.type) {
            case 'response':
                if (message.value === 'done') {
                    // Mark the end of the response
                    break;
                }
                let responseDiv = document.getElementById(message.id)
                responseDiv.innerHTML += message.value;

                break;
        }
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can have a chat with our locally hosted LLM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FchatInterface.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F06%2FchatInterface.webp" alt="Build your own VS Code extension"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;VS Code extension LLM chat&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Publish an extension
&lt;/h2&gt;

&lt;p&gt;When we decide that our extension is ready to be published, we can do so using the vsce CLI tool for managing VS Code extensions.&lt;/p&gt;

&lt;p&gt;Fist we need to install the vsce CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @vscode/vsce
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Packaging your project to a .vsix file is the easiest way to share your extension. You need to run this command in the root directory of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vsce pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, since it's not always trustworthy to share files directly, we might want to publish our extension in the official &lt;a href="https://marketplace.visualstudio.com/vscode?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;VS Code Extension Marketplace&lt;/a&gt;. Since Microsoft's official documentation for publishing extensions is clear, we will just reference their documentation here: &lt;a href="https://code.visualstudio.com/api/working-with-extensions/publishing-extension?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;Publishing Extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you've completed everything and your extension is accepted, it will be listed and available for other users to install.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further links
&lt;/h2&gt;

&lt;p&gt;This is a list of links which were useful to us when building our own VS Code extension (will be updated when finding good reference in the ):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Youtube tutorial (Ben Awad): &lt;a href="https://www.youtube.com/watch?v=a5DX5pQ9p5M&amp;amp;list=WL&amp;amp;index=1&amp;amp;ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=a5DX5pQ9p5M&amp;amp;list=WL&amp;amp;index=1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Genral information: &lt;a href="https://code.visualstudio.com/api?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Commands: &lt;a href="https://code.visualstudio.com/api/extension-guides/command?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api/extension-guides/command&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Webviews: &lt;a href="https://code.visualstudio.com/api/extension-guides/webview?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api/extension-guides/webview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Build-in commands: &lt;a href="https://code.visualstudio.com/api/references/commands?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api/references/commands&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Activation events: &lt;a href="https://code.visualstudio.com/api/references/activation-events?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api/references/activation-events&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;about package.json: &lt;a href="https://code.visualstudio.com/api/references/extension-manifest?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api/references/extension-manifest&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;publishing extensions: &lt;a href="https://code.visualstudio.com/api/working-with-extensions/publishing-extension?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://code.visualstudio.com/api/working-with-extensions/publishing-extension&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This tutorial serves as a comprehensive guide for creating a Visual Studio Code extension from scratch. We utilized the template provided to build an extension that interacts with a running Llama.cpp instance on &lt;code&gt;localhost:8080&lt;/code&gt;. While this tutorial covers a lot, there are many more possibilities to explore when creating VS Code extensions, but covering all of them would be too much for one blog post.&lt;/p&gt;

&lt;p&gt;If you're interested in building your own VS Code extension, you can join our Discord Server to connect with fellow software developers, Codesphere users, and the Codesphere team: &lt;a href="https://discord.gg/codesphere?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;Discord Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, if you want to improve this tutorial extension, there are plenty of things you can do! It's a great project to practice your coding skills and your VS Code extension creation skills. Here are some ideas for improvement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Chats&lt;/strong&gt; : Implement a feature to save chat histories or conversations between sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating a HuggingFace LLM Browser as a Separate Webview to Download Models&lt;/strong&gt; : Develop a separate webview interface where users can browse and download models from HuggingFace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Tune Prompt Settings or Create a UI for User Settings&lt;/strong&gt; : Allow users to fine-tune prompt settings or provide a user-friendly UI for configuring settings related to the extension.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve UI/UX&lt;/strong&gt; : Enhance the user interface and experience of the extension to make it more intuitive and visually appealing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-Friendly Installation of Llama.cpp&lt;/strong&gt; : Simplify the installation process of Llama.cpp for users, possibly by automating or providing clear instructions within the extension.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve Model Performance Depending on User's Hardware&lt;/strong&gt; : Implement optimizations to enhance the performance of the model, taking into account the hardware specifications of the user's machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Error Handling&lt;/strong&gt; : Develop mechanisms to handle errors gracefully, providing meaningful error messages and guiding users on how to resolve issues when they occur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform Compatibility&lt;/strong&gt; : Ensure that the extension functions smoothly on the Mac, Linux, and Windows operating systems.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>vscode</category>
      <category>extension</category>
      <category>llamacpp</category>
    </item>
    <item>
      <title>Self-host AI interface builder OpenUI on Codesphere</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Mon, 08 Apr 2024 13:14:04 +0000</pubDate>
      <link>https://dev.to/codesphere/self-host-ai-interface-builder-openui-on-codesphere-3ha5</link>
      <guid>https://dev.to/codesphere/self-host-ai-interface-builder-openui-on-codesphere-3ha5</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1bSXN_qT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/openUI_cover.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1bSXN_qT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/openUI_cover.webp" alt="Self-host AI interface builder OpenUI on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI tools helping with any and all parts of software development have been popping up left and right. The promise replacing coding tasks that are typically both mundane and require a learning curve is very appealing.&lt;/p&gt;

&lt;p&gt;With OpenUI you can create frontends in most common programming languages i.e. HTML, React, Svelte, Vue or web components with simple natural language prompts. The created draft is then refined by the AI by adding comments to elements just like you'd be collaborating with a design team in Figma. And the best? It works both with managed AI endpoints from OpenAI and self hosted models available via Ollama.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create workspace
&lt;/h2&gt;

&lt;p&gt;If you haven't created an account navigate to codesphere.com and sign up. Next you want to create a workspace, if you plan on using a self hosted model via Ollama it needs to be equipped with one of our GPU plans. Otherwise a smaller plan should be sufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Install Ollama
&lt;/h2&gt;

&lt;p&gt;If you want to use OpenAI instead of a self hosted model you can skip this step.&lt;/p&gt;

&lt;p&gt;We will need to follow a slightly altered version of the official installation guide as we need to install Ollama in our local home/user/app directory. Open a terminal an type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo curl -L https://ollama.com/download/ollama-linux-amd64 -o /home/user/app/ollama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then provide execution rights to the downloaded file via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chmod +x /home/user/app/ollama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Clone OpenUI Repository
&lt;/h2&gt;

&lt;p&gt;Next get the source code for the UI interface builder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/wandb/openui.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new folder in your app directory with the frontend and backend code for the UI builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Install required Python version and configure virtual environment env variables
&lt;/h2&gt;

&lt;p&gt;Codesphere pre-installs Python but for this project we will need a newer version. Firstly we need to configure our pyenv - this will make sure our installed Python version is persistent across workspace restarts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to setup/env vars and set &lt;code&gt;PYENV_ROOT&lt;/code&gt; to &lt;code&gt;/home/user/app/.pyenv&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Grab your workspace dev domain via &lt;code&gt;echo $WORKSPACE_DEV_DOMAIN&lt;/code&gt; and copy to clipboard&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;OPENUI_HOST&lt;/code&gt; to the value you just copied&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; to &lt;code&gt;xxx&lt;/code&gt; (for local Ollama models) or to your actual Open AI API key &lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;OPENUI_ENVIRONMENT&lt;/code&gt; to &lt;code&gt;local&lt;/code&gt; (please note for production use you'll want to set this to &lt;code&gt;production&lt;/code&gt; and set up the GitHub SSO app for session handling)&lt;/li&gt;
&lt;li&gt;Open a  &lt;strong&gt;new&lt;/strong&gt;  terminal (existing terminals don't get updated env vars)&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;pipenv install --python 3.10.0&lt;/code&gt; and confirm with &lt;code&gt;Y&lt;/code&gt; - this will take a few minutes&lt;/li&gt;
&lt;li&gt;Activate with &lt;code&gt;pipenv shell&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verify that your Python version is 3.10 via &lt;code&gt;python -V&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6PrkA7SY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/openui_env_vars.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6PrkA7SY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/openui_env_vars.webp" alt="Self-host AI interface builder OpenUI on Codesphere" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Install dependencies for OpenUI
&lt;/h2&gt;

&lt;p&gt;First let's build the frontend artefacts. In your terminal navigate into the frontend directory with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd openui/frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the installation and build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
npm run build -- --mode hosted

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now navigate to the backend directory (assuming your still in the frontend directory) with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ../backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install the Python dependencies via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Adjust port and host
&lt;/h2&gt;

&lt;p&gt;Currently OpenUI does not support changing the port via a command line flag or similar so we will need to do a small change to the source code.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;/openui/backend/openui/ __main__.py&lt;/code&gt; and change line 55 to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            host="0.0.0.0",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and line 57 to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            port=3000,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now save the changes with command+s.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Download model &amp;amp; run Ollama server
&lt;/h2&gt;

&lt;p&gt;Navigate to the root directory via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /home/user/app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And launch the Ollama server via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./ollama serve&amp;amp;./ollama run llava
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download the model (takes a few seconds) and start a prompt shell, to verify it is actually working type a prompt and see if it will produce output. If it works exit the prompt context via &lt;code&gt;control + d&lt;/code&gt; (mac) or &lt;code&gt;strg + c&lt;/code&gt; on windows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ROUTs-P2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/ollama_test.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ROUTs-P2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/ollama_test.webp" alt="Self-host AI interface builder OpenUI on Codesphere" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Run the UI builder and enjoy!
&lt;/h2&gt;

&lt;p&gt;Make sure your still inside your pipenv and then in the same! terminal now type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /home/user/app/openui/backend
python -m openui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NVNmjIM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/ScreenRecording2024-04-08at12.34.06-ezgif.com-video-to-gif-converter.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NVNmjIM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2024/04/ScreenRecording2024-04-08at12.34.06-ezgif.com-video-to-gif-converter.gif" alt="Self-host AI interface builder OpenUI on Codesphere" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>generativeai</category>
      <category>openui</category>
      <category>codesphere</category>
    </item>
    <item>
      <title>How to Get Started with Umami on Codesphere, Your Google Analytics Alternative</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Fri, 23 Feb 2024 14:43:41 +0000</pubDate>
      <link>https://dev.to/codesphere/how-to-get-started-with-umami-on-codesphere-your-google-analytics-alternative-4pjl</link>
      <guid>https://dev.to/codesphere/how-to-get-started-with-umami-on-codesphere-your-google-analytics-alternative-4pjl</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FumamiANDcodesphere.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FumamiANDcodesphere.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In today's digital landscape, data privacy is paramount. With giants like Facebook and Google tracking users across the web using cookies, the need for privacy-centric solutions has never been greater. Umami stands out by prioritizing user privacy and avoiding the use of cookies altogether. By not using cookies, Umami ensures that it doesn't collect personal data, thus safeguarding the privacy of your audience. This commitment not only protects user information but also ensures that you maintain control over your data. By choosing Umami on Codesphere, you're not just opting for powerful analytics, but also actively safeguarding the privacy of your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Umami
&lt;/h2&gt;

&lt;p&gt;Umami is a modern, open-source analytics solution offering advanced features and privacy-centric design. It provides a comprehensive understanding of user interactions with websites or applications, rivaling Google Analytics in functionality. &lt;/p&gt;

&lt;p&gt;Users may prefer Umami for its self-hosted option, ensuring full control over data and compliance with privacy regulations, while still offering robust analytics capabilities. Its simplicity, flexibility, and focus on user privacy make Umami a compelling alternative to Google Analytics for those seeking more control over their data.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll guide you through setting up your own Umami instance on Codesphere and conducting an A/B test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an A/B test?
&lt;/h2&gt;

&lt;p&gt;An A/B test, also known as a split test, is a method used to compare two or more versions of a webpage or app to determine which one performs better. In an A/B test, users are randomly divided into groups and shown different versions of the same webpage or app, with each version containing a specific variation or change. The performance of each version is then measured based on predefined metrics, such as click-through rates, conversion rates, or user engagement.&lt;/p&gt;

&lt;p&gt;By analyzing the results of the test, you can determine which version is more effective in achieving your goals and make data-driven decisions to optimize your product or website.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For following along this tutorial like blog post you need to have these things before diving in with us.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;To deploy Umami&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Codesphere Account&lt;/li&gt;
&lt;li&gt;Database (MySQl, Postgres)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: If you haven't a Database connection yet you can get one in the marketplace tab in your Codesphere teams overview.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;To conduct an A/B test&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;A domain&lt;/li&gt;
&lt;li&gt;Two versions of a website to analyze&lt;/li&gt;
&lt;li&gt;Deployed Umami instance on Codesphere&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Deploy Umami on Codesphere&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To set up Umami on Codesphere, follow these steps:&lt;/p&gt;

&lt;p&gt;Open a new workspace on Codesphere and paste this GitHub link into the text field:&lt;/p&gt;

&lt;p&gt;GitHub link: &lt;a href="https://github.com/codesphere-community/umami?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://github.com/codesphere-community/umami&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FScreenshot-from-2024-02-22-12-40-38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FScreenshot-from-2024-02-22-12-40-38.png" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create a new workspace on Codesphere&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the next step you need a MySQL or Postgres database which we can connect to our Umami instance. If you don't have a database yet, acquire one from the marketplace tab in the teams overview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FcreateDatabase.webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FcreateDatabase.webp.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create a new database in the marketplace&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To initialize a new database may take several minutes. When your database is ready to be used you can click on &lt;code&gt;Show info&lt;/code&gt; on the right side to get all relevant information:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FdatabaseShowInfo.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FdatabaseShowInfo.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Ready to use database on Codesphere&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We need to create a new environmental variable in our Codesphere workspace, so that Umami can use the database. To do that switch to the set up tab on the left side of your workspace and navigate to &lt;code&gt;Env vars&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FEnvvarsOverview.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FEnvvarsOverview.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Environmental variables UI&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By clicking &lt;code&gt;+ Add new variable&lt;/code&gt; you can create a new environment variable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;DATABASE_URL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; Your database URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you created your database on Codesphere, you find the &lt;code&gt;database URL&lt;/code&gt; in the &lt;code&gt;Services&lt;/code&gt; tab in your Codesphere team overview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FserviceTab.webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FserviceTab.webp.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When clicking &lt;code&gt;Show info&lt;/code&gt; the &lt;code&gt;database URL&lt;/code&gt; is listed under &lt;code&gt;Connection string&lt;/code&gt;. Just copy the string into the value field when creating a new environmental variable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Fcreateenvar.webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Fcreateenvar.webp.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Creating a new environmental variable on Codesphere&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now you can start the 'Prepare' stage of the &lt;code&gt;CI-Pipeline&lt;/code&gt;, and upon completion, start the 'Run' stage. You can navigate to the &lt;code&gt;CI-Pipeline&lt;/code&gt; UI by clicking &lt;code&gt;CI-Pipeline&lt;/code&gt; at the bottom of your workspace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Fcipipeline.webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Fcipipeline.webp.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;CI-Pipeline&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Access your Umami instance by clicking the &lt;code&gt;Open deployment&lt;/code&gt; button in the top-right corner of your workspace. When initializing Umami for the first time, you need to sign in with the following credential:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;user:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;password:&lt;/strong&gt; &lt;code&gt;umami&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After signing in, we recommend changing the admin credentials immediately to ensure security. You can do this in the settings on Umami.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FsettingsUmami-.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FsettingsUmami-.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Umami account settings&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Feel free to discover what you can change in the settings on Umami and set up your Umami instance according to your preferences.&lt;/p&gt;

&lt;p&gt;After setting up Umami, you're ready to proceed with setting up an A/B test on Codesphere and utilizing Umami to analyze the results.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;A/B testing with Umami on Codesphere&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To set up an A/B test on Codesphere, you need a domain so that multiple workspaces can connect to it. If you haven't connected a custom domain yet, we have a tutorial on how to do this:&lt;/p&gt;

&lt;p&gt;Link: &lt;a href="https://codesphere.com/articles/custom-domains?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://codesphere.com/articles/custom-domains?ref=codesphere.ghost.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To start setting up this A/B test, you need to create a workspace for each version of your website you want to test.&lt;/p&gt;

&lt;p&gt;For this example I used the &lt;code&gt;static landing page with Express.js and Tailwind.CSS&lt;/code&gt; Template: &lt;a href="https://github.com/codesphere-cloud/landingpage-temp?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://github.com/codesphere-cloud/landingpage-temp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating these workspaces, you need to connect them all to the same custom domain. This allows Codesphere to automatically handle traffic distribution, ensuring that traffic is evenly distributed among the different versions.&lt;/p&gt;

&lt;p&gt;You can do this in the teams overview on Codesphere by navigating to the domains tab. All you need to do is select the workspaces that should be associated with this domain:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FconnectDomainToWorkspaces.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FconnectDomainToWorkspaces.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Connect a custom domain to several workspaces&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to save the changes by clicking the &lt;code&gt;save&lt;/code&gt; button on the right side.&lt;/p&gt;

&lt;p&gt;Now we want to add our website to Umami. To do that, we navigate to the settings once again and click on the blue button in the top right corner. You just need to fill out the modal with the name of your website and your custom domain, which you set up on Codesphere:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FaddWebsitetoUmami.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FaddWebsitetoUmami.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add your Website to Umami&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now we can obtain the tracking code, which needs to be inserted into the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag of each version of our website. You can find it by editing your website settings and navigating to the tracking code section, where you can copy the tracking code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FtrackingCode.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FtrackingCode.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tracking code&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When pasted the tracking code into your html file in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; you need to save the file with &lt;code&gt;Ctrl + S&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FtrackingCodeinHTML.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2FtrackingCodeinHTML.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The tracking code inside a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that being set up we can see the traffic data in our umami dashboard when navigating to &lt;code&gt;Dashboard&lt;/code&gt; and then click on &lt;code&gt;View details&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Fresult.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Fresult.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Umami Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, we want to conduct an A/B test on one of our buttons within our website. For that, we need to write a tracking event code in JavaScript, which sends data to Umami every time this specific button is clicked. This JavaScript code needs to be pasted into each of our version's HTML files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    function trackButtonClick(buttonId) {
        umami.track('Button_Click', { button_id: buttonId });
    }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then we need to give the button on each of our versions a &lt;code&gt;on_click&lt;/code&gt; attribute like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a) Version 1:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button onclick="trackButtonClick('Version_A_Button')" type="submit" class="[...]"&amp;gt;
  Notify me
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;b) Version 2:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button onclick="trackButtonClick('Version_B_Button')" type="submit" class="[...]"&amp;gt;
  Notify me
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that setup, Umami can distinguish between both buttons and count how often each of them is clicked. We can view the results in our Umami dashboard by navigating to &lt;code&gt;Event Data&lt;/code&gt; and then clicking on our event, &lt;code&gt;Button_Click&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Feventdata.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2024%2F02%2Feventdata.webp" alt="How to Get Started with Umami on Codesphere, Your Google Analytics Alternative"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Event data from the button&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that setup, we can track which version receives more clicks. You can view the results on Umami in your website's overview.&lt;/p&gt;

&lt;p&gt;This is a simple example of A/B testing, and there are more use cases for A/B testing with Codesphere and Umami. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Umami is a powerful Google Analytics tool that's worth exploring further. It offers much more than the example we've shown in this blog post. Additional features of Umami include funnel analytics, drop-off rates, traffic sources, real-time data, and much more. Moreover, you have full control over your data.&lt;/p&gt;

&lt;p&gt;If you're interested in trying Umami and need assistance, you can join our Discord community. There, you can connect with the Codesphere Team and fellow Codesphere users.&lt;/p&gt;

&lt;p&gt;Discord: &lt;a href="https://discord.gg/codesphere?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://discord.gg/codesphere&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>umami</category>
      <category>abtest</category>
      <category>abtesting</category>
    </item>
    <item>
      <title>AI-Enhanced Knowledge Hub: Improving Access to our Blog and Docs</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Fri, 26 Jan 2024 11:08:14 +0000</pubDate>
      <link>https://dev.to/codesphere/ai-enhanced-knowledge-hub-improving-access-to-our-blog-and-docs-4edj</link>
      <guid>https://dev.to/codesphere/ai-enhanced-knowledge-hub-improving-access-to-our-blog-and-docs-4edj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2sTfZ9sQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/01/now-leaving-gitboook.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2sTfZ9sQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/01/now-leaving-gitboook.jpg" alt="AI-Enhanced Knowledge Hub: Improving Access to our Blog and Docs" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over the years, we have amassed a significant amount of content about Codesphere and what is possible to do with it.&lt;br&gt;&lt;br&gt;
One issue with how our content has been structured so far has been the distinction between our blog and documentation as separate platforms.&lt;/p&gt;

&lt;p&gt;In this article I will talk in retrospective about how we consolidated our documentation and our existing blog and combined it with an LLM-powered search using Qdrant (as described in &lt;a href="https://codesphere.com/articles/using-qdrant-to-create-an-ai-powered-search-for-your-static-blog?ref=codesphere.ghost.io"&gt;my last article&lt;/a&gt;), to have a consolidated, natural language-enabled search across all our information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the scene
&lt;/h2&gt;

&lt;p&gt;At Codesphere, we not only had rather traditional documentation, we also publish multiple blog articles per week which include tutorials that reach from simple deployment guides to more complex topics like &lt;a href="https://learn.codesphere.com/articles/building-email-marketing-engine-expressjs-sendgrid-part-1?ref=codesphere.ghost.io"&gt;building out an email marketing engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This left us with the problem that informational content about Codesphere, including our most sophisticated tutorials, was hosted outside of our documentation. Therefore it was not searchable from our docs page which would be where users go when they are looking for information on what to do with and how to use Codesphere.&lt;/p&gt;

&lt;p&gt;That is why we set out to find a way to consolidate our docs and blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our existing setup
&lt;/h2&gt;

&lt;p&gt;Our existing setup mainly consisted of three different pillars to host our information:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gitbook: We stored all out docs in Gitbook, a standard docs application used by many tech startups around the world.&lt;/li&gt;
&lt;li&gt;Ghost.io: As we_'_ve mentioned many times before, we use Ghost.io as the content management system for our blog.&lt;/li&gt;
&lt;li&gt;Custom Blog Frontend: Our blog is a &lt;a href="https://codesphere.com/articles/how-to-create-a-static-site-generator-with-github-repo-2023?ref=codesphere.ghost.io"&gt;fully custom static site generator&lt;/a&gt; which gives us full flexibility on how to set it up which is what we profited from for this project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While Gitbook comes with some quality of life improvement like an automatic search and a clean UI, it wasn_'_t really possible to connect it with our blog to create a consolidated search which is why we set out to create our own.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we made it happen
&lt;/h2&gt;

&lt;p&gt;Before we get into the different steps, let me quickly mention what should already be a given: Everything we did was implemented in Codesphere or through services available through the Codesphere marketplace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data migration
&lt;/h3&gt;

&lt;p&gt;It became clear to us pretty early on that we'd need to say goodbye to Gitbook to realize what we had in mind.&lt;br&gt;&lt;br&gt;
That is why we implemented all our existing docs to Ghost.io and retrofitted them with fitting tags that we could use for filtering in our frontend.&lt;/p&gt;

&lt;p&gt;This way, we were now able to pull our docs and blog posts separately from Ghost and save them in the Node environment where our consolidated hub would run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Search Capabilities
&lt;/h3&gt;

&lt;p&gt;We used the same technique I explained in my last blog article to create an &lt;a href="https://codesphere.com/articles/using-qdrant-to-create-an-ai-powered-search-for-your-static-blog?ref=codesphere.ghost.io"&gt;AI-powered search using Qdrant&lt;/a&gt;. Using Xenova/all-MiniLM-L6-v2 as the transformer for our embeddings. We are hosting the Qdrant instance in a custom Docker image, set up through the Codesphere Marketplace.&lt;/p&gt;

&lt;p&gt;This now allows for us to search both our blog and docs simultaneously, using natural language or more traditional queries alike, enabling high quality semantic search that goes beyond just simple keywords.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjusting the frontend
&lt;/h3&gt;

&lt;p&gt;Of course, all those changes would need to be reflected in our frontend. I already had some design improvements for the blog article page brewing in the background and this was the perfect opportunity to let them come to light.&lt;/p&gt;

&lt;p&gt;One big addition we made was implementing a similar navigation to Gitbook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bvyaYLSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/01/codesphere-new-docs-nav.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bvyaYLSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2024/01/codesphere-new-docs-nav.webp" alt="AI-Enhanced Knowledge Hub: Improving Access to our Blog and Docs" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We were able to also implement this in a static way, keeping our blog fast and snappy.&lt;/p&gt;

&lt;p&gt;The last thing to add was the search for our frontend. For that, we simply added a prominent search bar to the top or our page. We might add this to the navbar to make it even more prominent in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The transition came with its own challenges and tedious steps but at the end of the day, it was a lot simpler than we (or at least I) initially expected.&lt;br&gt;&lt;br&gt;
We now have a new and state of the art way for our users to search all of the content we have ever published to get them to their goal quicker.&lt;/p&gt;

&lt;p&gt;Now that we have set up this database, this opens up new possibilities for us as we could use it to power an LLM-powered Codesphere assistant or provide an even more tailored content experience.&lt;/p&gt;

&lt;p&gt;The possibilities are endless and we can't wait to keep on exploring what's possible.&lt;/p&gt;

&lt;p&gt;If you would like to set up something similar and don't know where to start, feel free to join our &lt;a href="https://discord.gg/codesphere?ref=codesphere.ghost.io"&gt;Discord community&lt;/a&gt; and ask for further assistance.&lt;/p&gt;

</description>
      <category>vectordatabase</category>
      <category>knowledge</category>
      <category>ai</category>
      <category>gitbook</category>
    </item>
    <item>
      <title>Cloud Deployments &amp; Live Code Sync from Visual Studio Code</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Fri, 22 Dec 2023 14:22:44 +0000</pubDate>
      <link>https://dev.to/codesphere/cloud-deployments-live-code-sync-from-visual-studio-code-31ha</link>
      <guid>https://dev.to/codesphere/cloud-deployments-live-code-sync-from-visual-studio-code-31ha</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9EuU0HKz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/visual_studio_cover.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9EuU0HKz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/visual_studio_cover.webp" alt="Cloud Deployments &amp;amp; Live Code Sync from Visual Studio Code" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Codesphere has its own cloud based code editor. It is optimized for speed and works as a simple collaborative text editor for coding. While we are constantly expanding its functionality it is of course not as powerful as the leading local IDEs out there. However in this blog post we are going to show you a way to connect your local Visual Studio Code (or the browser version thereof) to any Codesphere workspace in 5 minutes.&lt;/p&gt;

&lt;p&gt;Picture this: using your local Visual Studio Code to seamlessly sync with Codesphere Workspaces bidirectionally, giving you the full power of Visual Studio Code and the deployment &amp;amp; compute in Codesphere. You can leverage the full compute power of your workspace even for local development. The days where local development requires expensive hardware will soon be in the past, what we are showing today works on any device, as long as you have internet and a browser. It even works from an iPad or your phone.&lt;/p&gt;

&lt;p&gt;You can fully utilise the collaborative possibilities, co-create with other developers or share sessions with project managers and other stakeholders. 'Works on my machine' is no longer a valid excuse. Ready to dive in?&lt;/p&gt;

&lt;h2&gt;
  
  
  How to set it up (takes 5 min or less)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open any existing Codesphere workspace&lt;/li&gt;
&lt;li&gt;Open a terminal and enter the following commands
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&amp;amp;os=cli-alpine-x64' --output vscode_cli.tar.gz

tar -xf vscode_cli.tar.gz

./code tunnel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Follow the instructions in the terminal, you can login via GitHub or a Microsoft account&lt;/li&gt;
&lt;li&gt;We select GitHub and navigate to &lt;a href="https://github.com/login/device?ref=codesphere.ghost.io"&gt;https://github.com/login/device&lt;/a&gt;, enter the code provided in the terminal and submit&lt;/li&gt;
&lt;li&gt;Back in the terminal, provide a name for this tunnel and open the link provided in any browser &lt;a href="https://vscode.dev/tunnel/%3Cmachine_name%3E/%3Cfolder_name%3E?ref=codesphere.ghost.io"&gt;&lt;code&gt;https://vscode.dev/tunnel/&amp;lt;machine_name&amp;gt;/&amp;lt;folder_name&amp;gt;&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Alternatively you can open your local VS Code, install the &lt;code&gt;Remote - Tunnels&lt;/code&gt; extension from Microsoft and follow the instructions to connect your tunnel&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's it. If you now open a terminal in your Visual Studio instance you can run all commands like you would from Codesphere. Files changed either on Codesphere or in VS Code are bidirectionally synced in almost real-time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JAux3ujJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/vs_code_demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JAux3ujJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/vs_code_demo.gif" alt="Cloud Deployments &amp;amp; Live Code Sync from Visual Studio Code" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is that amazing?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Access to higher performance e.g. GPU's from a limited machine&lt;/li&gt;
&lt;li&gt;Share it with PMs, Stakeholders, and Tech Leads immediately (it's live and deployed)&lt;/li&gt;
&lt;li&gt;Access the whole landscape from your laptop (e.g. test databases, other services)&lt;/li&gt;
&lt;li&gt;Use your local extensions to modify a preview deployment&lt;/li&gt;
&lt;li&gt;Use this in meetings as a replacement for screen sharing, both can edit live, run, and test the code&lt;/li&gt;
&lt;li&gt;'Works on my machine' is a thing of the past&lt;/li&gt;
&lt;li&gt;No code needs to be stored on your laptop -&amp;gt; more secure&lt;/li&gt;
&lt;li&gt;No more installing of the development environment, it is already set up and is the same as previews, staging, and production&lt;/li&gt;
&lt;li&gt;Just connect to any deployment from GitHub and fix it if it did not work 😄&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also different to other CDE platforms like Codesandbox and Gitpod, because Codesphere covers the whole software lifecycle from preview to production, with CI Pipelines, Replicas, Autoscaling, Microservice Support, Managed Services, GPU's, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesphere.com/"&gt;Try it yourself&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>vscode</category>
      <category>sync</category>
      <category>codesphere</category>
    </item>
    <item>
      <title>GrapesJS - Self Hosted Low-Code Webpage Builder on Codesphere</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Wed, 20 Dec 2023 17:15:41 +0000</pubDate>
      <link>https://dev.to/codesphere/grapesjs-self-hosted-low-code-webpage-builder-on-codesphere-10a</link>
      <guid>https://dev.to/codesphere/grapesjs-self-hosted-low-code-webpage-builder-on-codesphere-10a</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qQU_T51Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/Group-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qQU_T51Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/Group-1.png" alt="GrapesJS - Your Low-Code Webpage Builder on Codesphere" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Working with Low Code tools can save a lot of development time, allowing you to allocate resources more efficiently elsewhere. GrapesJS is one such Low-Code tool that enables you to easily build your webpage by dragging and dropping elements.&lt;/p&gt;

&lt;p&gt;Hosting GrapesJS on Codesphere is straightforward and can be done within a few minutes. In this blog article, we will collaboratively create a GrapesJS instance. Additionally, we will demonstrate how to deploy your generated page effortlessly on Codesphere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy GrapesJS on Codesphere
&lt;/h2&gt;

&lt;p&gt;We've created a GitHub repository that serves as the foundation for deploying GrapesJS on Codesphere.&lt;/p&gt;

&lt;p&gt;To get started, simply create a new workspace within one of our Codesphere teams. A free plan workspace is sufficient. When setting up the workspace, paste this GitHub link into the designated field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/codesphere-cloud/deploy_grapesjs?ref=codesphere.ghost.io"&gt;https://github.com/codesphere-cloud/deploy_grapesjs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The GitHub repository includes a CI pipeline that contains everything necessary to deploy GrapesJS.&lt;/p&gt;

&lt;p&gt;To begin, execute the 'Prepare' stage of the CI pipeline to install all dependencies. This process may take up to two minutes. Once the 'Prepare' stage is complete, you can directly execute the 'Run' stage, and within a second, our GrapesJS will be deployed.&lt;/p&gt;

&lt;p&gt;In this project, GrapesJS is deployed using an Express server. Additionally, we have set up the option within that Express server to deploy the HTML code created in GrapesJS directly from this Codesphere workspace.&lt;/p&gt;

&lt;p&gt;Here is our documentation about creating a workspace with a GitHub repository:&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Your Webpage with GrapesJS
&lt;/h2&gt;

&lt;p&gt;Once GrapesJS is deployed, you'll see an overview on the main route. This overview has been provisionally set up by us and is not an integral part of GrapesJS.&lt;/p&gt;

&lt;p&gt;To use the GrapesJS Webpage builder, press the respective 'GrapesJS - Low Code Webpage builder' button on this homepage. From here, you enter the GrapesJS tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e6NPw58S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e6NPw58S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-4.png" alt="GrapesJS - Your Low-Code Webpage Builder on Codesphere" width="800" height="435"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;GrapesJS UI&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Initially, a demo webpage from GrapesJS is displayed, illustrating what can be achieved with the 'webpage preset.' To clear everything, use the 'clear canvas' button in the top-right corner of the user interface.&lt;/p&gt;

&lt;p&gt;On the right side, there's a section where you can choose from various 'blocks' to use on your webpage. As an example, let's drag and drop the 2 Columns block into the white canvas, and you'll see an element with two columns added at the respective position.&lt;/p&gt;

&lt;p&gt;Clicking on the element brings up a menu on the right side, allowing you to make various settings. For instance, you can set the padding of the box here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EdK2bIrZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EdK2bIrZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-5.png" alt="GrapesJS - Your Low-Code Webpage Builder on Codesphere" width="277" height="472"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Example in the menu for setting the padding&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, let's fill the boxes with text by inserting the text block into one of the two columns. Next, we'll integrate a Youtube video into our webpage using the 'video block'. Here you need to paste this Video-ID in the respective input field in the settings: &lt;code&gt;KJwYBJMSbPI&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kxAgtjqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kxAgtjqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-6.png" alt="GrapesJS - Your Low-Code Webpage Builder on Codesphere" width="800" height="253"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Example page in GrapesJS&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, we've created a admittedly very simple webpage. We can retrieve the HTML code by using the import button. From the pop-up window, we can copy our HTML code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fn9tQDPn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fn9tQDPn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-7.png" alt="GrapesJS - Your Low-Code Webpage Builder on Codesphere" width="800" height="614"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Generated HTML&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The HTML code can be employed in various ways. In this sample project, we've configured the Express server to deploy the HTML code when it's inserted as an .html file into the &lt;code&gt;my_webpages&lt;/code&gt; folder. It becomes accessible through the main route in the overview.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy Your Webpage on Codesphere
&lt;/h2&gt;

&lt;p&gt;Now, let's deploy the webpage we just created. Navigate to the 'grapesjs' folder in your IDE, then go to the 'my_webpages' folder. Right-click, choose 'New File,' and give the file any name with the extension .html (e.g., Example.html).&lt;/p&gt;

&lt;p&gt;Next, copy the HTML code and paste it into the empty .html file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My-Webpage&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then we copy the generated HTML code out of GrapesJS and paste it below the head tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"iynq"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"gjs-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"i21h"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"gjs-cell"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"inhh"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello World!
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"i6qj"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"gjs-cell"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;allowfullscreen=&lt;/span&gt;&lt;span class="s"&gt;"allowfullscreen"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"il92"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.youtube-nocookie.com/embed/KJwYBJMSbPI?"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.gjs-row&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;flex-start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;stretch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.gjs-cell&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;75px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex-grow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex-basis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#iynq&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;100px&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#inhh&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;25px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Comic&lt;/span&gt; &lt;span class="n"&gt;Sans&lt;/span&gt; &lt;span class="n"&gt;MS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;cursive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;109px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#il92&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;350px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;615px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;.gjs-row&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, save the file by pressing Ctrl + S. Next, revisit your workspace URL by, for example, clicking the 'open deployment' button in the Codesphere IDE's top right corner.&lt;/p&gt;

&lt;p&gt;Your created HTML file will now appear in the overview, and you can preview it by clicking the respective button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IRfkxIxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IRfkxIxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/12/image-8.png" alt="GrapesJS - Your Low-Code Webpage Builder on Codesphere" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Finished webpage&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization and Beyond
&lt;/h2&gt;

&lt;p&gt;In this blog article, we utilized GrapesJS's &lt;code&gt;Webpage preset.&lt;/code&gt; However, what makes GrapesJS interesting is its high level of customization. Everything, including the user interface and the individual blocks you can use, is adjustable in the source code. Moreover, there are numerous plugins that you can integrate into GrapesJS, allowing you to tailor it to your specific needs. So actually GrapesJS is not only a low code webpage builder, it is also a framework to build your own low code webpage builder.&lt;/p&gt;

&lt;p&gt;Here is the link to the official documentation: &lt;a href="https://grapesjs.com/docs/?ref=codesphere.ghost.io"&gt;https://grapesjs.com/docs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this documentation, you can also find information on how to integrate plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discord Community Server
&lt;/h2&gt;

&lt;p&gt;If you interested in connecting with fellow Codesphere users and developers you can join our Discord Community Server: &lt;a href="https://discord.gg/codesphere?ref=codesphere.ghost.io"&gt;https://discord.gg/codesphere&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>grapesjs</category>
      <category>lowcode</category>
      <category>webpagebuilder</category>
    </item>
    <item>
      <title>n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Tue, 21 Nov 2023 17:57:02 +0000</pubDate>
      <link>https://dev.to/simoncodephere/n8n-a-cost-saving-zapier-alternative-hosted-on-codesphere-4jf8</link>
      <guid>https://dev.to/simoncodephere/n8n-a-cost-saving-zapier-alternative-hosted-on-codesphere-4jf8</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_tkZ0C4J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/zapier-vs-n8n-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_tkZ0C4J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/zapier-vs-n8n-1.jpg" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the world of automating tasks, Zapier has been a go-to for many. But here's the thing – if you're looking for more control and a wallet-friendly option, consider n8n the open-source workflow automation tool.&lt;/p&gt;

&lt;p&gt;The cool thing is that you can host it yourself, and if you do it on Codesphere, you'll save money compared to use Zapier. Plus, you get full control over your data and how your workflows play out.&lt;/p&gt;

&lt;p&gt;In this article, we're going to show you just how simple it is to set up your own n8n instance on Codesphere. We will guide you through the process of getting started effortlessly. Let's make the most of n8n's potential with the ease of self hosting on Codesphere – it's a win-win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the n8n workspace
&lt;/h2&gt;

&lt;p&gt;Let's get your n8n workspace up and running on Codesphere. You just need to follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose Your Plan:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick either "Always On" or "Off When Unused" for your workspace. If you choose "Off When Unused," remember it shuts down after an hour of inactivity. Don't worry, though—your workspace will reboot the next time you use it, although it might take a bit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add this GitHub Repository:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;link: &lt;a href="https://github.com/codesphere-cloud/n8n-Template?ref=codesphere.ghost.io"&gt;https://github.com/codesphere-cloud/n8n-Template&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run "Prepare" and "Run" stages in the CI-Pipeline:&lt;/li&gt;
&lt;li&gt;Open Deployment and Set Up the user account for this n8n instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ieM-uq3r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/createn8ninstance.webp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ieM-uq3r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/createn8ninstance.webp.gif" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create a workspace for hosting n8n on Codesphere&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your n8n workspace is set! Now, dive in and start creating your workflows. The n8n interface is user-friendly, making it easy to build workflows tailored to your tasks.&lt;/p&gt;

&lt;p&gt;Next up, we'll guide you through setting up the workflow integrations: Discord, Todoist and Rowbase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading the predefined workflow
&lt;/h2&gt;

&lt;p&gt;Now that your n8n workspace is up and running on Codesphere, let's dive into effortlessly loading a predefined workflow. Follow these simple steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download the Workflow JSON:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the Codesphere IDE, right-click on the provided JSON file for the predefined workflow. Select "Download" to save it directly to your device.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Import the Workflow:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside your n8n workspace, navigate to the workflows section. Look for the "Import" option and upload the downloaded JSON file. This action will seamlessly import the predefined workflow into your n8n instance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save Changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2dhK2t7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/data-src-image-3a354249-3c52-4869-9ab7-dd826d80a40f.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2dhK2t7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/data-src-image-3a354249-3c52-4869-9ab7-dd826d80a40f.gif" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Import the predefined workflow into n8n&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next up, we'll guide you through creating a workflow. This one uses RSS feeds to share the latest news on Discord, organizes article data in a table, and sets up a task in a to-do list. This workflow is a hands-on example to demonstrate n8n's flexibility and power.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once you've completed setting up the n8n instance on Codesphere, ensure you have these accounts ready before diving into the showcase workflow:&lt;/p&gt;

&lt;p&gt;Prerequisites for the Showcase:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://baserow.io/?ref=codesphere.ghost.io"&gt;Baserow Account&lt;/a&gt;: Create an account on Baserow and create a new database.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://todoist.com/?ref=codesphere.ghost.io"&gt;Todoist Account&lt;/a&gt;: Make sure you have a Todoist account, as it's part of our workflow.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://discord.com/?ref=codesphere.ghost.io"&gt;Discord Account&lt;/a&gt;: If you don't have one yet, sign up for a Discord account and create a new server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's get started with setting up the workflow integrations and see what n8n has to offer!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Workflow Integrations: Discord
&lt;/h2&gt;

&lt;p&gt;Navigate to your Discord server settings and select "Integrations" from the menu. Create a new webhook and copy its URL. Paste this URL into the n8n webhook URL field. In the n8n content field, you can define the message the webhook should generate based on the RSS-feed data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JJx1AZ01--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/Discord.webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JJx1AZ01--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/Discord.webp.webp" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create a Discord webhook&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Workflow Integrations: Todoist
&lt;/h2&gt;

&lt;p&gt;In the n8n UI, create a new credential for Todoist. Copy the API token from Todoist's settings in the "Integrations" tap and select "Developers" to obtain the API token. Paste the API token into the password field of the n8n credential.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--klop6YzZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/todoist.webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--klop6YzZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/todoist.webp.webp" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Setting up Todoist integration&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Workflow Integrations: Rowbase
&lt;/h2&gt;

&lt;p&gt;For this integration, you need to set up a Rowbase credential. Simply click on 'Create New Credential,' and enter the email address associated with your Rowbase account in the username field, along with your Rowbase account password in the respective fields.&lt;/p&gt;

&lt;p&gt;Once this step is completed, proceed to configure the additional required fields in the Rowbase node to populate the data in the desired table. To achieve this, you'll need to extract the Database ID from the API Documentation of your Rowbase Database. Copy and paste the Database ID into n8n.&lt;/p&gt;

&lt;p&gt;To load the database into the node, exit the node menu and re-enter it. Now you can choose the table where you want to input the data. Once again, exit the node menu and re-enter it to select the specific columns for your article data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CbdRMhdU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/rowbase.webp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CbdRMhdU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/rowbase.webp.gif" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Setting up Rowbase integration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once completing these steps, you can save your workflow and activate it. This ensures seamless communication between n8n, Discord, Todoist, and Rowbase, allowing you to automate tasks effortlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Custom RSS-Feed Trigger
&lt;/h2&gt;

&lt;p&gt;To integrate any RSS feed of your choice into the workflow, follow these steps to create a new RSS-feed trigger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a New RSS-Feed Trigger:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within the n8n workspace, add a new RSS-feed trigger node to your workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy RSS-Feed URL:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obtain the URL of the desired RSS feed that you want to integrate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure the RSS-Feed Trigger:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paste the copied RSS-feed URL into the URL field of the RSS-feed trigger node. This ensures that the trigger is connected to the specific RSS feed you want to monitor.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect Trigger to Workflow Nodes:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Link the RSS-feed trigger to each subsequent node in your workflow. This connection ensures that the workflow responds to updates from the specified RSS feed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AKhuuifH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/new-trigger.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AKhuuifH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/new-trigger.gif" alt="n8n - a Cost-Saving Zapier Alternative Hosted on Codesphere" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create a new RSS-Feed trigger for any RSS-Feed you desire&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Link: &lt;a href="https://rss.feedspot.com/it_rss_feeds/?ref=codesphere.ghost.io"&gt;Top 100 RSS-Feeds about IT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With these simple steps, you've now extended the flexibility of your workflow to incorporate updates from any RSS feed of your choice. The dynamic nature of n8n allows you to adapt and customize your automation seamlessly. Your workflow is now ready to efficiently process and act upon the latest information from the "subscribed" RSS feed.&lt;/p&gt;

&lt;p&gt;With n8n's incredible flexibility, the options for automation are limitless. This powerful tool offers a variety of features, leaving a wide array of automation possibilities ready for you to explore.&lt;/p&gt;

&lt;p&gt;Are you interested to learn more about Codesphere and n8n? Connect with fellow Codesphere users and reach out to our support team by joining our Discord server!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesphere.com/discord?ref=codesphere.ghost.io"&gt;Join the Codesphere Community Server on Discord&lt;/a&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>workflow</category>
      <category>lowcode</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Getting started with Stable Diffusion Text2Img AI</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Mon, 13 Nov 2023 15:11:54 +0000</pubDate>
      <link>https://dev.to/codesphere/getting-started-with-stable-diffusion-text2img-ai-930</link>
      <guid>https://dev.to/codesphere/getting-started-with-stable-diffusion-text2img-ai-930</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jiErVmfu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-cover--1-.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jiErVmfu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-cover--1-.webp" alt="Getting started with Stable Diffusion Text2Img AI" width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stable diffusion has become the staple of open source image generation AI. Similar to Llama anyone can use and work with the stable diffusion code. Tons of other open source projects build on top of it.&lt;/p&gt;

&lt;p&gt;Generally you can use stable diffusion &amp;amp; related models to either generate images from prompts or edit images with prompts (text2img or img2img).&lt;/p&gt;

&lt;h2&gt;
  
  
  Running stable diffusion
&lt;/h2&gt;

&lt;p&gt;The most comfortable way to work with stable diffusion is via the open source webUI &lt;a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui?ref=codesphere.ghost.io"&gt;https://github.com/AUTOMATIC1111/stable-diffusion-webui&lt;/a&gt;. Unlike other alternatives that require deeper understanding of the underlying code and working with command line prompts - this webUI comes with a 1-click installer and a web view with all the configuration options you might need. You can run it locally (if you have a strong graphics card &amp;amp; CPU) or run it via a cloud provider that offers GPU environments. If you’re looking for free cloud GPU's check out our comparison article here &lt;a href="https://codesphere.com/articles/5-best-free-cloud-gpu-providers-for-hobbyists?ref=codesphere.ghost.io"&gt;https://codesphere.com/articles/5-best-free-cloud-gpu-providers-for-hobbyists&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are using Codesphere to deploy your stable diffusion webUI you can use our template.&lt;/p&gt;

&lt;p&gt;After running the installation (which takes a few minutes) and your run pipeline you should see a WebUI similar to this one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oxEU-IwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable_diff1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oxEU-IwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable_diff1.webp" alt="Getting started with Stable Diffusion Text2Img AI" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write good text to image prompts
&lt;/h2&gt;

&lt;p&gt;This is the typical way of working with generative image AI. You provide a prompt describing visually what you expect to see in the image and the AI takes the input, tries to find semantically similar images from its training data and merges them into an image.&lt;/p&gt;

&lt;p&gt;As you can see there is a wide array of settings available in the webUI and after playing around with it for a bit you will see that results differ dramatically.&lt;/p&gt;

&lt;p&gt;Generating usable images from this requires mainly two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A good prompt with relevant context&lt;/li&gt;
&lt;li&gt;Settings appropriate for your use case (there is a trade-off between generation speed &amp;amp; quality - more below)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's look at some example prompts first:&lt;/p&gt;

&lt;p&gt;" &lt;strong&gt;&lt;em&gt;Romantic painting of a ship sailing in a stormy sea, with dramatic lighting and powerful waves.&lt;/em&gt;&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q14gfC7Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-no-details.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q14gfC7Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-no-details.webp" alt="Getting started with Stable Diffusion Text2Img AI" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This prompt is a good start because it visually describes key features, i.e. what style of image is expected (romantic painting), the main object (ship) and the surrounding with enough detail. Finding the right amount of details can be a bit tricky and might take some practice - in general if you describe clearly and unambiguously what you'd like to see as concise as possible you'll get decent results.&lt;/p&gt;

&lt;p&gt;Unfortunately the picture output doesn't have much details yet:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"best quality, masterpiece, (photorealistic:1.4), ship in stormy seas, (powerful waves:1.2), dramatic lighting, hyper quality, intricate detail, ultra realistic, maximum detail, foreground focus, instagram, 8k, volumetric light, cinematic, octane render, uplight, no blur, 8k"&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ybt30Z7D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-details.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ybt30Z7D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-details.webp" alt="Getting started with Stable Diffusion Text2Img AI" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Can we still improve the output? Yes! In order to understand what changed between the first prompt and the second prompt it is helpful to think about how stable diffusion and similar models are trained:&lt;/p&gt;

&lt;p&gt;They get millions of labeled images of differing quality, assign patterns &amp;amp; similarity through arranging vector representations of labels and images closer in proximity of each other if similar. Now we can trick the model to only look on a subset of the pictures it was trained on, namely those we associate with beauty. By adding comma separated attributes associated with high quality input (i.e. masterpiece or cinematic) we can improve the output significantly. You can also add weights (i.e. photorealistic:1.4) if you want to prioritize between different attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"best quality, masterpiece, (photorealistic:1.4), ship in stormy seas, (powerful waves:1.2), dramatic lighting, hyper quality, intricate detail, ultra realistic, maximum detail, foreground focus, instagram, 8k, volumetric light, cinematic, octane render, uplight, no blur, 8k"&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&amp;amp; negative prompt:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"nsfw, ng_deepnegative_v1_75t,badhandv4, (worst quality:2), (low quality:2), (normal quality:2), lowres,watermark, monochrome"&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hsRMCapm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-details-neg2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hsRMCapm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/11/stable-diff-details-neg2.webp" alt="Getting started with Stable Diffusion Text2Img AI" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another great way to improve an already excellent output image is by specifying attributes and output visual you want to avoid. Your frontend provides a second box for this. By adding quality descriptions like low and normal to the negative prompt we can enforce the ultra high quality even stronger. By adding thing like monochrome &amp;amp; watermark we can enforce more variation in our output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text 2 image settings
&lt;/h2&gt;

&lt;p&gt;As mentioned there is a ton of different settings so we will focus on the important ones here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sampling method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The default is DPM++ 2M Karras which is a great mix between speed &amp;amp; quality. If you have time and want a bit more detail try switching to DPM++ SDE Karras&lt;/p&gt;

&lt;p&gt;Other choices include some that are a lot faster like UniPC at the expense of detail&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hires. fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This handles the upscaling of your output images. Per default images are 512x512 pixels which is the size of the training images used. You can upscale your images to higher resolution at the cost of speed. I recommend a value between 1.5 and 1.7 anything higher will get critically slow and might even crash the GPU with a out of memory error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refiner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This allows you to add a second model that takes the output of the first model and refines it.&lt;/p&gt;

&lt;p&gt;Alright that's a wrap for today! - Have fun creating stunning visuals with this fascinating new technology&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>stablediffusion</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>Getting started with Jupyter Notebooks in AI</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Thu, 26 Oct 2023 13:13:45 +0000</pubDate>
      <link>https://dev.to/codesphere/getting-started-with-jupyter-notebooks-in-codesphere-1ki1</link>
      <guid>https://dev.to/codesphere/getting-started-with-jupyter-notebooks-in-codesphere-1ki1</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2FCodesphere_jupyter.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2FCodesphere_jupyter.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jupyter notebooks are important for machine learning as they allow testing and experimenting with data and models. This blog post will teach you how to create your first Jupyter notebook in Codesphere. You will also get to try out some cool AI examples. One example is using a stable diffusion AI image generator in a notebook. Our GPU notebooks offer a similar but more customizable experience as Google Colab spaces.&lt;/p&gt;

&lt;p&gt;Jupyter notebooks are great because they have a user interface where you can add cells to run code. You can use different programming languages like Python, R, and Julia. There are many other options available thanks to the open source community. It's about time that we added support for it to Codesphere and explored some use cases, so let's dive right in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up your first Jupyter Notebook in Codesphere
&lt;/h3&gt;

&lt;p&gt;First you'll want to create a new empty workspace (or clone a repo with your existing notebooks of course). Since we will be using Python inside the notebook (which requires Python 3.9 or higher) we will need to update Python first.&lt;/p&gt;

&lt;p&gt;[Workaround] If you want your python version to persist across workspace restarts currently you are required to set the following environment variable (via setup/env vars)&lt;/p&gt;

&lt;p&gt;Key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PYENV_ROOT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/user/app/.pyenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure the python version selection gets stored in the persistent app directory. Afterwards open a new terminal (Do not use existing terminals as they are missing the environment variable you just set) - in this new terminal type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipenv install jupyter --python 3.9.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted to confirm whether you want to install the new Python version confirm with &lt;code&gt;Y&lt;/code&gt;. Wait till all the dependencies were added successfully - this might take a minute.&lt;/p&gt;

&lt;p&gt;Afterwards either activate your pipenv via &lt;code&gt;pipenv shell&lt;/code&gt; followed by a &lt;code&gt;jupyter notebook&lt;/code&gt; or run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipenv run juptyer notebook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can open the deployment with the icon in the top right corner. Per default Notebooks are secured by a password / token mechanism - this ensure's that only you and your team have access to the notebook server.&lt;/p&gt;

&lt;p&gt;That means also you will need to authenticate yourself as the owner of this notebook server with a token - your specific token will be printed in the terminal output of the last command you ran. Copy the token and insert it into the webUI of your notebook server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fjupyter1-Large.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fjupyter1-Large.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After providing your token you should be greeted by the Notebook Server UI that contains all the files in your directory. If you started with an empty workspace you should only find the pipenv files here. Right click anywhere in the UI and select new notebook. This will open a new tab in your browser with an untitled and empty jupyter notebook. You still need to select the kernel, for this simple example we'll go with the default iPython kernel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fjupter2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fjupter2.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Notebook basics (skip ahead to use cases if you are familiar with notebooks already)
&lt;/h3&gt;

&lt;p&gt;Notebooks are organized in cells and there are 3 types of cells:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code cells&lt;/li&gt;
&lt;li&gt;Markdown cells&lt;/li&gt;
&lt;li&gt;Raw text cells&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Code cells contain your python code or terminal commands like &lt;code&gt;pip install pandas&lt;/code&gt; and can be executed one by one. You can run a single cell multiple times and if the code you ran contains terminal output the notebook frontend will display the output below the cell. Likewise if your code doesn't run through because of unhandled errors it will display these below the respective cell.&lt;/p&gt;

&lt;p&gt;Markdown cells on the other hand contain markdown formatted text. This is great for creating interactive documents that allow others (or yourself in the future) to follow along and understand why you built the code blocks the way you did.&lt;/p&gt;

&lt;p&gt;Try adding some Python code to the first cell and click the play icon in the menu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print("Hello this code was excecuted in a jupyter cell") 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see whatever text you put inside the "" of the print statement below the cell.&lt;/p&gt;

&lt;p&gt;If you want to use a markdown cell instead, select the cell and switch the from code to markdown. If you enter valid markdown notion and run the cell you will get formatted text in return.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use case - Running your own open-source DALL-E alternative
&lt;/h3&gt;

&lt;p&gt;For this example we will run a Text to image AI. It requires a notebook on one of our GPU plans - free BETA available through special signup link: &lt;a href="https://ai.codesphere.com/?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://ai.codesphere.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will be using this performance optimized version of stable diffusion &lt;a href="https://huggingface.co/segmind/SSD-1B?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://huggingface.co/segmind/SSD-1B&lt;/a&gt; because that is small enough to be run on our free tier GPU. Deploying and running inference of the Segmin SSD 1B model is actually really easy in Codesphere.&lt;/p&gt;

&lt;p&gt;In your notebook you need to add &amp;amp; execute the following code cells:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install git+https://github.com/huggingface/diffusers

pip install transformers accelerate safetensors

from diffusers import StableDiffusionXLPipeline
import torch
pipe = StableDiffusionXLPipeline.from_pretrained("segmind/SSD-1B", torch_dtype=torch.float16, use_safetensors=True, variant="fp16")
pipe.to("cuda")
# if using torch &amp;lt; 2.0
# pipe.enable_xformers_memory_efficient_attention()
prompt = "A glamorous ski town in the Swiss alps in the 80s. A retro fotograph style picture with street lights, fancy cars and snowy mountains in the background and fancy dressed people enjoying a hot drink outside." # Your prompt here
neg_prompt = "ugly, blurry, poor quality" # Negative prompt here
image = pipe(prompt=prompt, negative_prompt=neg_prompt).images[0]

image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prepare to be amazed! The output is a beautiful and stunning scene it feels as if we where there in 1980s Zermatt 💜 You can hold shift+right click on the image to copy or save it from the notebook to your laptop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fstable-diffusion-Large.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fstable-diffusion-Large.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just for the fun of it, you don't even have to come up with the detailed prompts yourself - ask chatGPT or your trusted self hosted Llama to to help with the prompts:&lt;/p&gt;

&lt;p&gt;"I need your help to write prompts for a stable diffusion text to image generator. The goal is to generate a series of images that are detail rich, have a cosy atmosphere and a retro look.&lt;br&gt;&lt;br&gt;
Can you describe this "A glamorous ski town in the Swiss alps in the 80s. A retro fotograph style picture with street lights, fancy cars and snowy mountains in the background and fancy dressed people enjoying a hot drink outside." from multiple angles and perspectives so that I can plug each prompt into the image generator?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F56cd9e35-404a-49a3-941d-23b66711ade9-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F56cd9e35-404a-49a3-941d-23b66711ade9-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fdf8d569c-477a-4b80-b884-6aeb9c5ccd0e-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fdf8d569c-477a-4b80-b884-6aeb9c5ccd0e-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F43168b1f-6f45-456f-b27e-88ad86216386-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F43168b1f-6f45-456f-b27e-88ad86216386-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F1ab84cba-a695-4e1e-a908-09e280554224-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F1ab84cba-a695-4e1e-a908-09e280554224-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F4415fe54-1b99-4437-903c-e18a3eab0d15-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F4415fe54-1b99-4437-903c-e18a3eab0d15-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F5d803b84-4dde-457b-81dc-a9cdf862fd6c-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F5d803b84-4dde-457b-81dc-a9cdf862fd6c-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F13926d6f-847e-4964-989b-866d25a28aeb-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F13926d6f-847e-4964-989b-866d25a28aeb-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fcc7ab202-23eb-4d83-ae10-3221e928e5c4-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2Fcc7ab202-23eb-4d83-ae10-3221e928e5c4-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F68dadac7-b88f-4fa3-b283-8741a2202707-Small-1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F10%2F68dadac7-b88f-4fa3-b283-8741a2202707-Small-1.webp" alt="Getting started with Jupyter Notebooks in Codesphere"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jupyter</category>
      <category>notebooks</category>
      <category>ai</category>
      <category>codesphere</category>
    </item>
    <item>
      <title>Off when unused deployment mode now available</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Thu, 24 Aug 2023 08:16:28 +0000</pubDate>
      <link>https://dev.to/codesphere/off-when-unused-deployment-mode-now-available-2lo9</link>
      <guid>https://dev.to/codesphere/off-when-unused-deployment-mode-now-available-2lo9</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F08%2Fdeployment_mode_cover.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F08%2Fdeployment_mode_cover.webp" alt="Off when unused deployment mode now available"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week we released another very exciting milestone. When creating a new workspace in Codesphere, you now have the option to chose between two different deployment modes. The default for paid plans is &lt;code&gt;always on&lt;/code&gt; - until now all our paid plans are deployed like this, with dedicated 24/7 resources from our own datacenter.&lt;/p&gt;

&lt;p&gt;The new deployment mode is called &lt;code&gt;off when unused&lt;/code&gt; or sometimes referred to as standby or on demand workspaces. Workspaces deployed with this setting receive dedicated resources only when used and go into standby after ~60 minutes without usage. Upon receiving a request at the connected domain the server resources will boot up and restart your application (as long as there is a valid CI pipeline defined). Through under-provisioning, intelligent scheduling, and different deployment algorithms our servers have much faster cold starts (~1s) than those of competitors. Since these deployments only consume resources as they are used we can offer them at 10% of the cost of an &lt;code&gt;always on&lt;/code&gt; deployment.&lt;/p&gt;

&lt;p&gt;This allows teams to spam deployments more freely as cloud spend is only a fraction of what it would otherwise be. Imagine having a larger dev team, like at Codesphere for example, we have  ~600 branches and ~250 pull requests open in parallel on a given day. Each of them get's deployed automatically as preview deployments - this has made us roughly 6x faster but the cost can get considerable fast. We estimate that all things considered, our on demand deployments can make preview deployments 70% cheaper than with conventional setups.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F08%2Fstandby_plans.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesphere.ghost.io%2Fcontent%2Fimages%2F2023%2F08%2Fstandby_plans.webp" alt="Off when unused deployment mode now available"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can read more technical details in the corresponding &lt;a href="https://docs.codesphere.com/getting-started/deployment-mode?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;doc page&lt;/a&gt;. In this article we want to explore some of the exciting use cases and advantages this opens up.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Use cases
&lt;/h3&gt;

&lt;p&gt;Obviously you don't want a multi second delay for high traffic production setups but there are plenty of cases where this is totally acceptable.&lt;/p&gt;

&lt;p&gt;The obvious example are preview deployments (read more about these here: &lt;a href="https://docs.codesphere.com/getting-started/preview-deployments?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://docs.codesphere.com/getting-started/preview-deployments&lt;/a&gt;). These are used during the software development process to automatically deploy every committed code change and making a working version available to all team members. Developers can test things that require infrastructure not available locally easier, code reviews can be done much faster and you can even share pre-release versions of your software in client demos. Codesphere allows automating preview deployments from GitHub, Bitbucket &amp;amp; Gitlab (coming soon!) via our CI integrations.&lt;/p&gt;

&lt;p&gt;Another use case is spinning up sandbox environments where you might still need ample computing power. For example we have been experimenting with self-hosted Llama2 LLMs. These are very resource intense applications but you might not be willing to spend big time just for trying things out. For example a smaller 7b param Llama2 runs fine on our Pro plan as we showed in another recent article (find it here: &lt;a href="https://codesphere.com/articles/self-hosted-chatgpt-in-codesphere?ref=codesphere.ghost.io" rel="noopener noreferrer"&gt;https://codesphere.com/articles/self-hosted-chatgpt-in-codesphere&lt;/a&gt;). With the new &lt;code&gt;off when unused&lt;/code&gt; deployment mode you can try this yourself for as little as $8/m.&lt;/p&gt;

&lt;p&gt;Slightly less obvious but equally neat is the benefit this update provides to all of our free tier users. The free plan workspaces were always deployed with an &lt;code&gt;off when unused&lt;/code&gt; logic but now they benefit from the same wakeup on domain access logic as all paid workspaces deployed in this mode. Imagine you are hosting a portfolio site where you showcase your amazing dev projects - typically these sites will not be high traffic (unless you're rockstar in your field which justifies an always on plan I'd say 😎). You can now host sites like this free forever - whenever someone wants to take a look at it, it will take a few seconds to boot up but will be available momentarily.&lt;/p&gt;

&lt;p&gt;Happy Coding from the entire Codesphere team.&lt;/p&gt;

</description>
      <category>codesphere</category>
      <category>previewdeployments</category>
      <category>ondemand</category>
      <category>hosting</category>
    </item>
    <item>
      <title>From first click to prompt output in 1m38s - Running Llama2 in Codesphere</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Fri, 18 Aug 2023 09:18:49 +0000</pubDate>
      <link>https://dev.to/codesphere/from-first-click-to-prompt-output-in-1m38s-running-llama2-in-codesphere-3glp</link>
      <guid>https://dev.to/codesphere/from-first-click-to-prompt-output-in-1m38s-running-llama2-in-codesphere-3glp</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wKh8hif7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/llama.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wKh8hif7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/llama.webp" alt="From first click to prompt output in 1m38s - Running Llama2 in Codesphere" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What started out of pure curiosity: how difficult can it be to run your own LLM inside a Codesphere workspace, will it even run and if so will it be performant enough, running inference on our CPUs? Turned into one of the most exciting projects I've worked on in recent weeks.&lt;/p&gt;

&lt;p&gt;Unlike OpenAi and their GPT models, Meta has open sourced their entire suite of Large Language Models called Llama1 &amp;amp; Llama2 alongside the pre-trained chat versions. In independent performance tests these land somewhere between GPT3.5 and GPT4 and they are actually a bit faster than GPT4 in their responses.&lt;/p&gt;

&lt;p&gt;These models come in different sizes, based on how many parameters they were trained on. Typically inference for LLM's is run on GPU instead CPU processors because these computation are very memory intense and GPU's have a clear edge there. GPU servers are still very expensive and not as widely accessible as CPU servers - &lt;a href="https://welcome.codesphere.com/?utm_source=blog&amp;amp;utm_medium=link&amp;amp;utm_campaign=devto"&gt;Codesphere&lt;/a&gt; is planning to offer shared (cheaper) &amp;amp; dedicated GPU plans in the near future but as of today we only offer them after receiving a pre-order requesting early access.  &lt;/p&gt;

&lt;p&gt;Therefore today we are going to test if we can still run the smaller model (trained on 7 Billion parameters) on a CPU based server inside of Codesphere. Since we know it will challenging to get a smooth response, we are going with our pro plan, providing 8 state of the art vCPUs, 16GB RAM and a 100GB of storage.&lt;/p&gt;

&lt;p&gt;Getting Llama2 running on Codesphere is actually very easy thanks to the amazing open source community, providing C++ based wrappers (llama.cpp) and huggingface offering pre-compiled and compressed model versions for download.&lt;/p&gt;

&lt;p&gt;It is actually so easy that I decided to do a timed run. From the first click to the first chat response took 1 minute 38 seconds. It really blows my mind.&lt;/p&gt;

&lt;p&gt;Let's take a look how it's done inside of Codesphere. If you still need to create a Codesphere account now is as good a time as any. If your machine is strong enough this tutorial will also work locally (at least for Linux &amp;amp; MacOS with small adjustments)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a workspace from the Llama.cpp repository
&lt;/h3&gt;

&lt;p&gt;Sign in to your Codesphere account, click the create new workspace button at the top right and paste this into the repo search bar at the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://github.com/ggerganov/llama.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you'll want to provide a workspace name, select the pro plan and hit the start coding button. This plan is 80$/m for a production always on plan and 8$/m for a standby when unused deployment mode. We know 80$/m seems like a lot but consider that renting a GPU is typically more than 1000$/m.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Compile the code
&lt;/h3&gt;

&lt;p&gt;Open up a terminal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will compile the c++ code to be readable for Linux. The Llama.cpp repository contains a &lt;code&gt;Makerfile&lt;/code&gt; that tells the compiler what to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Download the model
&lt;/h3&gt;

&lt;p&gt;First type &lt;code&gt;cd models&lt;/code&gt; in the terminal to navigate to the folder where Llama.cpp expects to find the model binaries. There are a wide variety of versions available via hugging face, as mentioned we are picking the 7b params size and opt for the pre-trained chat version of that.&lt;/p&gt;

&lt;p&gt;Even for this specification there are ~10 different flavours available, pick the one that suits your use case best - we found this repo to contain good explanations alongside models: &lt;a href="https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML?ref=codesphere.ghost.io"&gt;https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the models directory run this command and replace the model name with the flavour that suits you best:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/resolve/main/llama-2-7b-chat.ggmlv3.q4_K_M.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Run your first query
&lt;/h3&gt;

&lt;p&gt;Now we can ask our very own chatbot the first question. Navigate back to the main directory with &lt;code&gt;cd ..&lt;/code&gt; and then run this command from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./main &lt;span class="nt"&gt;-m&lt;/span&gt; ./models/llama-2-7b-chat.ggmlv3.q4_K_M.bin &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Why are GPUs faster for running inference of LLMs"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 512
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's going to take a few seconds to load the 4GB model into memory but then you should start to see your Chatbot typing an answer to your query into the terminal.&lt;/p&gt;

&lt;p&gt;Once completed it will also print the timings, the initial load can take up to 30s but subsequent runs take less than 1s to start providing a response - also the speed is not quite as fast as interacting with chatGPT in the browser but it still returns around 4 words per second which is pretty good.&lt;/p&gt;

&lt;p&gt;The images show the timing of the initial run vs. subsequent runs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mgUO6Xsc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/inital_llm_run.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mgUO6Xsc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/inital_llm_run.webp" alt="From first click to prompt output in 1m38s - Running Llama2 in Codesphere" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MmA9hW5M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/subsequent_llm_run.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MmA9hW5M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/subsequent_llm_run.webp" alt="From first click to prompt output in 1m38s - Running Llama2 in Codesphere" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  [Optional] Step 5: Getting the example chatbot web interface running on Codesphere
&lt;/h3&gt;

&lt;p&gt;The llama.cpp repository comes with simple web interface example. This provides an experience closer to what you might be used to from ChatGPT.&lt;/p&gt;

&lt;p&gt;Navigate to CI pipeline and click the Define CI Pipeline button. For the prepare stage enter &lt;code&gt;make&lt;/code&gt; as command.&lt;/p&gt;

&lt;p&gt;And for the run stage enter this command, making sure the model name point to the binary of the version you downloaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./server &lt;span class="nt"&gt;-m&lt;/span&gt; ./models/llama-2-7b-chat.ggmlv3.q4_K_M.bin &lt;span class="nt"&gt;-c&lt;/span&gt; 2048 &lt;span class="nt"&gt;--port&lt;/span&gt; 3000 &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to set the port to 3000 and the host to 0.0.0.0 in order to expose the frontend in Codesphere.&lt;/p&gt;

&lt;p&gt;Now you can run your prepare stage (which won't do anything if you previously ran make already via the terminal) but might be needed after workspace restarts.&lt;/p&gt;

&lt;p&gt;Next run your run stage and click the open deployment icon in the top right corner. Now you and anyone you share the URL with can have chats with your self-hosted ChatGPT clone 😎&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UBBjA1_2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/llama_chat.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UBBjA1_2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/llama_chat.webp" alt="From first click to prompt output in 1m38s - Running Llama2 in Codesphere" width="630" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us know what you think about this! Also feel free to reach out to us if you are interested in getting early access to our GPU plans.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>llama</category>
      <category>largelanguagemodels</category>
      <category>inference</category>
      <category>codesphere</category>
    </item>
    <item>
      <title>Back to the roots: advantages to static web development</title>
      <dc:creator>Simon Pfeiffer</dc:creator>
      <pubDate>Tue, 08 Aug 2023 13:34:08 +0000</pubDate>
      <link>https://dev.to/codesphere/back-to-the-roots-advantages-to-static-web-development-50ed</link>
      <guid>https://dev.to/codesphere/back-to-the-roots-advantages-to-static-web-development-50ed</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iqHGh5ZW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/roots--3-.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iqHGh5ZW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/roots--3-.webp" alt="Back to the roots: advantages to static web development" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As web developers page speed and performance considerations are at the top of mind. They affect all important KPIs, starting with user experience and ultimately affecting how well your content performs on search platforms.&lt;/p&gt;

&lt;p&gt;Today we are going to be talking about advantages of static websites, consisting of simple HTML, CSS and a Javascript server that serves this static content. That doesn't sound very sexy at first but bear with us for a second.&lt;/p&gt;

&lt;p&gt;The fastest growing web development framework in recent years is arguably Next.js. The popularity is caused by a combination of easy to use (heads off to the people at Vercel!) and the fact that Next.js managed to solve some key concerns of serverside rendering frameworks like React. Next.js achieves this by pairing react with an intricate logic for serving static props -  and on top it runs well on &lt;a href="https://codesphere.com/articles/serverless-vs-containers?ref=dev.to"&gt;Serverless&lt;/a&gt; infrastructure, making infrastructure setup a no brainer (at least for simple use cases).&lt;/p&gt;

&lt;p&gt;Now under the hood Next.js is still a react based framework and as such it’s the server and setup are not exactly lightweight. Don't get me wrong, it is very impressive how much faster Next.js can be compared to a standard React in many cases - but we want to argue today that for smaller / mostly static use cases, Next.js might be overkill and can easily be outperformed by a more simple setup: Static HTML, CSS &amp;amp; Express.js server. We call this back to the roots, because all mentioned web frameworks rely on these and build on top of them. At &lt;a href="https://welcome.codesphere.com/?utm_source=static-web-development-article&amp;amp;utm_medium=blog&amp;amp;utm_campaign=blog"&gt;Codesphere&lt;/a&gt; we have recently been turning to this simple setup for a lot of use cases - originally not because of speed considerations but because stripping away all unneeded complexity it made us much faster in the marketing team.&lt;/p&gt;

&lt;p&gt;When concerned with speed few things can beat a simple express.js server serving static html content. The main issue here is that most use cases are not simply serving static content. If your use case is purely static i.e. a landing page you should probably look no further. We have recently switched all our landing pages to this approach and can highly recommend it! It even makes things like &lt;a href="https://docs.codesphere.com/tutorials/a-b-testing-posthog?ref=codesphere.ghost.io"&gt;a/b testing&lt;/a&gt; soo much easier 😎&lt;/p&gt;

&lt;p&gt;If your use case is not as simple as serving a static landing page it might still be worth considering if it can't be turned into a static website with a few tricks.&lt;/p&gt;

&lt;p&gt;Let’s take a look at an obvious example - a blog frontend is not static per se as you will keep adding or updating content regularly. Upon closer inspection you might concede though, that updates are not that frequent, maybe a few times a week, depending how active you are. In such a case it’s actually rather easy to turn to fully static pages, you will just need a way to automate replacing the static content regularly - more on that example in a bit.&lt;/p&gt;

&lt;p&gt;Even for other cases it might be worth asking what portion of your content actually changes frequently and as a consequence if it might make sense to serve the static parts as such, simple html files, served via express.js and only adding dynamic pieces on top. You will be rewarded with much faster pages in return.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case Study - how we transformed our blog to a static frontend
&lt;/h3&gt;

&lt;p&gt;So far our codesphere blog is part of our monolithic website, which uses quite a few frameworks and databases to ultimately pull and serve the content from our ghost.io backend.&lt;/p&gt;

&lt;p&gt;It includes middleware for caching, webpack for compiling the files and typescript for all the server operations. As part of our move towards more simple frameworks,  that the marketing team can handle and adjust without Dev team support we are also redoing our website, and therefore building a standalone version of our blog hosted inside of codesphere.&lt;/p&gt;

&lt;p&gt;When turning a non static use case into a purely static website you basically have two potential approaches to handling updates to the underlying content. Either you periodically run code that pulls updates and replaces the static files with updated versions or you can attach a trigger to the event that caused the update in the first place. Which option you prefer might depend on how frequent you expect updates, how computationally expensive (in terms of resources) the static file generation is and more.&lt;/p&gt;

&lt;p&gt;For our blog we expect updates a few times a week at most, currently we do not have comments, if we did we would probably add these dynamically on top. Also our CMS ghost.io provides built in triggers (webhooks) whenever content changes.&lt;/p&gt;

&lt;p&gt;Now the webhook offers somewhat limited functionality - therefore we did need to implement a small server for handling the requests and forwarding them to GitHub with proper authentication. In GitHub we have repository which get's the updates as a commit and the configured GitHub action then takes the updates and triggers a deployment of the updated static html, css and javascript. The functionality we exploit here is &lt;a href="https://docs.codesphere.com/getting-started/preview-deployments?ref=dev.to"&gt;Codesphere's preview deployment via GitHub actions&lt;/a&gt; - just that we (mis^)use it for the actual production deployment of our headless static blog frontend.  We are already working on building a public Codesphere API, this will make it even more flexible.&lt;/p&gt;

&lt;p&gt;If running (and paying) two separate workspaces is a concern for you, you could also configure the webhook listening &amp;amp; committing the changes to the repo into one application, we decided against that because we don't want to occupy our static server with anything but serving static content (for maximum performance) - but you could also counter this by restricting updates to run nightly only, when traffic is low anyways.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XxdJndNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/codesphere_blog_flow.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XxdJndNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/codesphere_blog_flow.webp" alt="Back to the roots: advantages to static web development" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright this leaves us to finish adding some additional functionality to our headless blog frontend but we managed to fit a non static case into a lightweight static infrastructure. The whole thing took around one day to setup, it would be less if you were not building an entire headless frontend from scratch and instead go with an existing html template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance tests
&lt;/h3&gt;

&lt;p&gt;First of all I'd like to point out that the static regeneration only runs a couple of seconds for our full set of 160+ blog posts, if the number increases greatly it would be somewhat easy to restrict regenerated files to those that changed. Also you might be concerned that the workflow with multiple workspaces and GitHub action in the middle takes long, it doesn't! From triggering a change to the update being live on the deployed frontend takes 1-2 minutes only.&lt;/p&gt;

&lt;p&gt;Now to the actual static website, performs like a champ! We are not quite ready to share the URL yet as we are still tweaking the design but stay tuned, revisiting our blog in the future might just blow your mind 😎&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Yp09x36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/performance_static_blog.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Yp09x36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesphere.ghost.io/content/images/2023/08/performance_static_blog.webp" alt="Back to the roots: advantages to static web development" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>express</category>
      <category>static</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
