<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Katerina Skroumpelou on Medium]]></title>
        <description><![CDATA[Stories by Katerina Skroumpelou on Medium]]></description>
        <link>https://medium.com/@pakotinia?source=rss-d83b4a2663b5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*L_QOKzV680Rx_Ydt3yEheQ.jpeg</url>
            <title>Stories by Katerina Skroumpelou on Medium</title>
            <link>https://medium.com/@pakotinia?source=rss-d83b4a2663b5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 14 Jul 2026 03:49:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@pakotinia/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How I gamified your next event]]></title>
            <link>https://pakotinia.medium.com/how-i-gamified-your-next-event-a976dc194971?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/a976dc194971</guid>
            <category><![CDATA[supabase]]></category>
            <category><![CDATA[developer-relations]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[events]]></category>
            <category><![CDATA[boltnew]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Wed, 25 Jun 2025 12:57:42 GMT</pubDate>
            <atom:updated>2025-06-25T13:28:53.020Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iHmv01UKbYPhpzlGRb9bdg.jpeg" /><figcaption>Katerina, looking a bit too excited from all the gaming, at the Nx EU Summit. (photo: Heidi Grütter)</figcaption></figure><p><em>— — — In this post I talk about the app I built. 🔗 </em><a href="https://github.com/mandarini/nxsummit-game"><em>Here is the GitHub repo</em></a></p><p>Back in April, I organized the first ever Nx Summit in the EU. It took place in Amsterdam, and it involved quite a lot of planning.</p><p>Here are the main things I handled:</p><ul><li><strong>Venue selection</strong></li><li><strong>Date and format planning</strong></li><li><strong>Budget management</strong>: I had to keep everything under a specific budget</li><li><strong>Attendee targeting</strong>: We wanted 100 European customers and prospects, so I reached out individually to invite them</li><li><strong>Hotel arrangements</strong>: I picked a conference hotel right next to the venue and secured a group price and discount codes (which, to be honest, didn’t work great)</li><li><strong>Registration system</strong>: Just a Google Form</li><li><strong>Schedule development</strong>: A full agenda with meals, presentations, roundtables, and space for networking</li><li><strong>Speaker coordination</strong>: Worked with our team and invited external speakers</li><li><strong>Swag</strong>: Actually useful stuff : rain ponchos (luckily unused), inkless pens (aka pencils?), and scrunchies</li><li><strong>Gift bags</strong>: Paper bags for each attendee with goodies and a handwritten thank-you postcard</li><li><strong>Badges</strong>: Amsterdam-themed name tags for attendees and the Nx team</li><li><strong>Team travel coordination</strong>: Took note of arrivals, booked rooms, and planned social activities</li><li><strong>Social activities</strong>: Planned a group outing to Keukenhof in full bloom the day after the event</li><li><strong>Attendee tracking</strong></li><li><strong>Cost tracking</strong>: We wrapped things up using just 80% of the budget</li><li><strong>Market research</strong>: Avoided date conflicts with other conferences and arranged for us to sponsor one that took place the day before</li><li><strong>Partner coordination</strong>: Talked to partners about inviting their customers</li></ul><p>All that wasn’t enough for me.</p><p>I decided we needed a virtual ticketing and check-in system. There are a million solutions for this, but none were good enough for me. So naturally, I reinvented the wheel and built an app.</p><p>At first, I just wanted a ticket and check-in app. But with a blank slate, I thought why not make it fun?</p><p>I asked myself: <em>What’s the point of this event?</em> The answer: for our team to talk to as many attendees as possible, gather feedback, and collect stories.</p><p>So I gamified it.</p><h3>The Idea: Collect Points</h3><ul><li><strong>Scan someone’s QR code, earn points</strong></li><li><strong>Trade points for a prize</strong></li></ul><p>But I didn’t want just one winner who talked to the most people — not everyone has the same time or comfort levels. So I made it a <strong>weighted raffle</strong>:</p><p>The more points you have, the more chances you get.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/768/1*Bq9IKvl86K0cixlZZ-Uj8g.png" /><figcaption>The ticket page, with the points I have collected.</figcaption></figure><h3>How It Worked</h3><ul><li>Every attendee had a personal QR code (UUID-based, no emails)</li><li>Scan another attendee: +1 point</li><li>Scan an Nx team member: +4 points</li><li>Check in at the event: +1 point</li></ul><p>Bonus points:</p><ul><li>Participate in a round table: +5 points</li><li>Ask a question during Q&amp;A: +5 points</li><li>Get interviewed: +8 points</li><li>Show me a picture of your cat: +3 points (UUID-based, no emails)</li></ul><h3>Admin Mode</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0Ib5-oS0jfwZls7eGmHU8A.png" /><figcaption>The admin dashboard. List of attendees and action buttons.</figcaption></figure><p>Admins could:</p><ul><li>View and manage the attendee list</li><li>Scan attendees to check them in</li><li>Sign people in/out manually</li><li>Track points and participation</li><li>Run the raffle (with a spinning wheel animation, naturally)</li><li>View schedule and game rules</li></ul><p>I spec’d everything out with GPT, then pasted the prompt into <a href="https://bolt.new">bolt.new</a>, and tada! Website.</p><p>Of course, I made it harder for myself by overengineering it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jmw8W_mzxs8Er1K1Q_hGUw.png" /><figcaption>The raffle page!</figcaption></figure><h3>Challenges</h3><ul><li>Getting the QR scanner to behave and not go into infinite scan mode</li><li>Deciding what controls admins should have</li><li>Setting up role-based access (attendee, staff, super_admin)</li><li>Ensuring anonymity (UUIDs instead of emails)</li><li>Printing QR code stickers for team members — bonus points if they had their Calendly links too</li></ul><p>Authentication was handled by an Edge Function. All staff shared a password. Was it Fort Knox? No. Was it enough? Yes.</p><p>RLS was enforced for access control, while game-related reads (like scanning) were available under <strong>public-read</strong> constraints that respected RLS.</p><h3>Using GPT</h3><p>Before I even opened a code editor, I pitched my idea to GPT. We talked it through a bit — what I wanted the app to do, how it might work, what the edge cases were. Then I asked GPT to spec it out in detail, so I could feed it directly into bolt.</p><p>We went back and forth tweaking the spec until it left no room for ambiguity. I wanted something clean and executable — no guessing. Once we nailed it, I dropped it into bolt and the app was ready.</p><p>I like GPT, it’s good for pitching and discussing ideas.</p><h3>Using Bolt.new</h3><p>I used <a href="https://bolt.new/">bolt.new</a> to build the entire app, and it made everything faster and smoother. The UI that bolt created was clean, mobile-friendly, and, honestly, fresh and fun.</p><p>What really stood out was the <strong>Supabase integration</strong>: I could connect my project, run migrations, deploy Edge Functions, and query data — all from the same place. It worked like a charm.</p><p>Bolt let me take an idea from a GPT-generated spec to a live working app in no time. And then I had plenty of time left over to overengineer it, of course.</p><p>And once everything was ready, I 1-click-deployed it to Netlify straight from bolt. It just worked.</p><h3>Using Supabase</h3><p>Supabase made things surprisingly easy. I used it for the entire backend — the database, serverless functions, role-based access, and real-time updates were all handled in one place.</p><p>Before anything could work, I uploaded the list of registered attendees to the Supabase attendees table. Each record had a name and email — this allowed the system to verify identities when someone “signed in” with their email, and to generate their unique QR code.</p><p>Here’s how it worked:</p><ul><li><strong>Attendees didn’t need to sign in.</strong> They simply entered their email. If their email existed in the attendees table, they were in. No account creation, no password, no magic link — just a soft lookup. If the email didn’t exist in the database, the Edge Function returned a failure response, and the frontend blocked them from proceeding. So while the app felt lightweight with no login, identity verification was still enforced securely on the backend.</li><li><strong>QR codes were anonymized.</strong> Each attendee’s QR code used a UUID from the database — not their email or any personal info. That way, if someone scanned you, they couldn’t see who you were or get access to your data — they just got points.</li><li><strong>Game functionality (like scanning or checking a leaderboard) was available without authentication.</strong> Public reads were allowed via Supabase’s anon key, thanks to carefully scoped RLS (Row-Level Security) policies. This meant the app could run smoothly client-side, with no login required — but still respect access controls.</li><li><strong>Mutations (like awarding points, checking in attendees, or running raffles) were locked behind Supabase Edge Functions.</strong> These functions used the service_role key, which gave them full privileges. I wrote RLS policies so that only the service_role could perform updates. So even if someone tried to call the same APIs from their browser, nothing would work unless it was coming from the server.</li><li><strong>Role-based logic was layered in.</strong> Attendees had the default attendee role. Staff (and me, the super_admin) had elevated roles stored in the same attendees table. Edge Functions validated roles before running sensitive actions, like toggling the game state or drawing raffle winners. Staff authenticated with a shared password (via an Edge Function), and once verified, they had access to the admin panel.</li></ul><p>In short, Supabase let me build something fast, fun, and secure — without needing a separate auth system, backend framework, or deployment flow. It was all right there. Just enough abstraction to move quickly, and just enough control to make it feel solid.</p><h3>Measuring Engagement</h3><p>At most conferences, you talk to a dozen people, have great conversations and then later realize you don’t remember their name, or never got their email. That’s a problem.</p><p>For our team, it was critical to track who we interacted with, who had questions, who shared feedback . Not just for follow-up, but to understand our impact.</p><p>Instead of asking the team to manually jot down names or hand out stickers, I gamified it.</p><p>Every time someone scanned a badge, it left a digital footprint of that interaction. Attendees were incentivized to scan team members (worth more points), which in turn meant we could easily tell who had talked to whom.</p><p>It made it easier for attendees to start conversations, and for us to track real, valuable engagement automatically.</p><p>The game wasn’t just fun. It helped us track real engagement in a lightweight and meaningful way.</p><p>Because each interaction (like scanning someone or participating in a session) generated a point and left a record, we could:</p><ul><li>See who connected with whom — every scan was a touchpoint</li><li>Understand which attendees were most active and involved</li><li>Identify the most talked-to team members</li><li>Spot high-interest moments, like Q&amp;A spikes or interview participation</li></ul><p>This gave us a unique way to measure the impact of the event beyond just attendance. We didn’t need forms or follow-up surveys — the game <em>was</em> the engagement tracker.</p><h3>Data Insights</h3><p>After the conference, we downloaded all the interaction data as a CSV. This gave us a clean record of who showed up, who scanned whom, and how points were distributed.</p><p>We could import everything into a Google Sheet, track metrics, visualize participation, and segment attendees based on engagement — without needing extra tooling.</p><ul><li>Downloaded attendee list to see who showed up, who scanned who</li><li>Retroactive conversation tracking: if someone scanned your badge, you spoke to them</li><li>Vanity stats! We knew who was most engaged, who participated in Q&amp;A, who got interviewed</li></ul><h3>Raffle Time</h3><p>I customized the raffle logic. Bolt generated the page, but I wanted the chances to be more fair. I weighted heavily based on points and added a spinning animation. It was slightly off visually, but still fun.</p><h3>Why I Loved Building This</h3><p>I love building things, especially games. (Come to one of my birthday parties. My last one had a murder mystery game for 75 people.)</p><p>Using tools like GPT and Bolt made the process so much smoother. I wasn’t stuck Googling how to make QR scanners work. I could just build, tweak, and keep going.</p><p>Did I spend 5 days perfecting it? Yes. Am I picky? Yes. Did I enjoy every second? Absolutely.</p><p>We live in a time where it’s easier than ever to build something cool, useful, and delightful. You just have to start.</p><p>🔗 <a href="https://github.com/mandarini/nxsummit-game">Here is the GitHub repo</a></p><p>Fork it, clone it, and use it at your next event.</p><h3>🎮 Nx Summit Game Rules</h3><p><strong>Overview</strong> This is a gamified check-in and networking app for the Nx Summit that encourages attendee interaction through QR code scanning and point collection.</p><p><strong>How to Play</strong></p><ol><li><strong>Getting Started</strong></li></ol><ul><li>Check-in is required before playing</li><li>Each attendee gets a unique, private QR code (UUID-based)</li><li>Everyone starts with 0 points</li></ul><p><strong>2. Earning Points</strong></p><p><strong>QR Code Scanning</strong>:</p><ul><li>Regular Attendees: +1 point</li><li>Nx Team Members: +4 points</li><li>You can only scan each person once</li></ul><p><strong>Engagement Rewards</strong>:</p><ul><li>Participate in roundtables or Q&amp;A: bonus points</li><li>Staff can manually award points</li></ul><p><strong>Hidden Bonuses</strong>:</p><ul><li>Show Katerina a cat photo: +3 points</li></ul><p><strong>3. Privacy &amp; Security</strong></p><ul><li>Only name and invite email are stored</li><li>No emails exposed during scanning</li><li>No tracking or third-party data sharing</li></ul><p><strong>4. Winning Prizes</strong></p><p><strong>Weighted Raffle</strong>:</p><ul><li>Your chances = your points / total points</li><li>Everyone has a shot, but activity helps!</li></ul><p><strong>Game Strategy Tips</strong></p><ul><li>Prioritize scanning Nx team (they’re worth more!)</li><li>Engage in sessions and Q&amp;A</li><li>Scan lots of unique attendees</li><li>Stay involved and earn those bonus points</li></ul><p><strong>Technical Highlights</strong></p><ul><li>Real-time updates and leaderboard</li><li>Admin panel with controls and raffle logic</li><li>Mobile-friendly</li><li>Resilient to network hiccups</li></ul><p>The game made networking more fun, interactive, and rewarding — without sacrificing privacy or creating friction.</p><p><strong>Make it yours and add it to your next event!</strong></p><p>I’d love to hear what you ended up doing. Feel free to tag me or reach out!</p><h3>Reach out</h3><p>GitHub repo: <a href="https://github.com/mandarini/nxsummit-game">https://github.com/mandarini/nxsummit-game</a></p><p>Find me: <a href="https://psyber.city">https://psyber.city</a></p><p>Follow me:</p><ul><li><a href="https://x.com/psybercity">https://x.com/psybercity</a></li><li><a href="https://bsky.app/profile/psyber.city">https://bsky.app/profile/psyber.city</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a976dc194971" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Monorepos — Why Speed Matters]]></title>
            <link>https://medium.com/nrwl/monorepos-why-speed-matters-e74293ae697b?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/e74293ae697b</guid>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[speed]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[ci]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Wed, 20 Mar 2024 20:44:57 GMT</pubDate>
            <atom:updated>2024-03-20T20:44:57.755Z</atom:updated>
            <content:encoded><![CDATA[<h3>Monorepos — Why Speed Matters</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-ouxLjANaikDwx-kaCOjuw.jpeg" /></figure><p>In the ever-evolving landscape of software development, efficiency and speed are vital. As projects grow in complexity, developers and teams need tools that can keep up without sacrificing quality or performance.</p><p>Nx is a suite of powerful tools designed to optimize your development workflow, which sets the <a href="https://nx.dev/ci/concepts/building-blocks-fast-ci">building blocks for a fast CI</a>. Nx is always innovating in many ways to make developers’ lives easier, but this post is exclusively focused on the things Nx has done in the past year to make development faster and faster.</p><h3>Why speed matters</h3><p>The ability to iterate quickly and efficiently is vital for any software project. Speed in the development process offers several critical advantages:</p><ul><li><strong>Faster feedback loops:</strong> Quick iterations mean immediate feedback, allowing teams to adapt, learn, and improve their work on the fly.</li><li><strong>Reduced time to market:</strong> Accelerating the development process can significantly cut down the overall time to market, providing a competitive edge which reclaims revenue that would have otherwise been lost.</li><li><strong>Decreased developer frustration:</strong> <a href="https://nx.dev/ci/concepts/reduce-waste">No more waiting for builds and tests to complete</a>. A streamlined workflow keeps morale high and productivity higher.</li></ul><p>If you’re using Nx already, you’re already familiar with</p><ul><li><a href="https://nx.dev/ci/features/affected"><strong>Affected</strong></a> - identifying and running tasks only on projects impacted by code changes,</li><li><a href="https://nx.dev/ci/features/remote-cache"><strong>Nx Replay</strong></a> -our powerful cache and</li><li><a href="https://nx.dev/ci/features/distribute-task-execution"><strong>Nx Agents</strong></a><strong> </strong>— the concept of <a href="https://nx.dev/ci/concepts/parallelization-distribution">Parallelization and Distribution</a>.</li></ul><p>But let’s see all the extra things we did this past year to make everything faster.</p><h3>Speed at the core</h3><h3>Rustifying Nx</h3><p>The Nx daemon has seen significant enhancements, notably through the use of Rust to calculate file hashes behind the scenes. This improvement not only speeds up the start-up times but also optimizes performance even without the daemon, especially on CI environments where the daemon isn’t used. The benchmark results at <a href="https://github.com/vsavkin/large-monorepo">this repo</a> showcase the remarkable speed improvements, making Nx competitive with native code solutions while maintaining the accessibility and flexibility of Node.js. Nx is still Node-first, so contributions are easier and only the most performance-critical parts of Nx are native code.</p><h3>Task Hasher and archive file innovations</h3><p>The introduction of a task hasher written in Rust, alongside the use of an archive file to store workspace file hashes (.nx/cache), has significantly reduced the need for repetitive file system accesses. This innovation means that running multiple Nx commands in CI is much faster, as file hashing becomes unnecessary after the initial run.</p><p><strong>The Archive file</strong></p><p>The archive file is a binary file that contains the workspace file hashes with their last modified time. Every time Nx starts (ie, running nx run project:target) it gets all the files with their last modified time, and compares it to the archive. If the file exists in the archive, then Nx does not access the file system to read the file to hash (reading individual files is slower than just getting a list of files from a directory). So running multiple nx commands in CI is quick to start because Nx does not need to constantly hash files.</p><h3>Nx Replay</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Pc60JAuCXqBBVaEzVsF4JQ.png" /></figure><p>Nx Replay enables caching and reusing of task results. It’s our well known Nx remote cache! It allows developers to avoid re-running expensive tasks by retrieving the cached results from a remote cache. This significantly improves build and test performance, as well as developer productivity. Nx Replay is also critical to the functioning of Nx Agents, which rely on the remote cache to ensure that the results of a task will be shared with every agent that needs them. By using Nx Replay, developers can optimize their workflows and reduce the time spent waiting for tasks to complete.</p><p>With Nx Replay, you can see significant speed improvements in your CI pipelines for modified PRs. What’s also important is that if a task has been executed in CI, a developer running that same task locally can reuse the task result instead of actually running the task. So you will also see improvements locally.</p><h3>Nx Agents</h3><p><a href="https://nx.dev/ci/features/distribute-task-execution">Nx Agents</a> represent the pinnacle of task distribution optimization, ensuring that tasks are executed as efficiently as possible based on the specific requirements of each change. Some features that make up this effort are:</p><ul><li><a href="https://nx.dev/ci/features/distribute-task-execution#cicd-guides"><strong>Easy integration with existing providers</strong></a><strong>:</strong> Distribution is handled on the Nx Cloud infrastructure and all you need is a single line. What’s more, all results are played back to your original CI provider script which triggers the Nx Cloud distribution, so that you can make use of the resulting artifacts</li><li><a href="https://nx.dev/ci/features/dynamic-agents"><strong>Efficient task distribution</strong></a><strong>: </strong>Save compute resources and reduce costs, minimizing idle time and compute waste. Also, Nx offers dynamic sizing based on PR size</li><li><a href="https://nx.app/products/tusky"><strong>Tusky</strong></a> — our AI solution (coming soon): You set your desired cost/speed ratio, and you forget about any more configuration. We ensure maximum speed up to limits you set yourself.</li></ul><p>You can read more about Nx Agents <a href="https://nx.app/products/agents#content">here</a>.</p><h3>TaskAtomizer</h3><p>The <a href="https://nx.dev/ci/features/split-e2e-tasks">TaskAtomizer</a> splits your Cypress or Playwright e2e tests by file. This significantly enhances granularity for caching, parallel execution, and flaky test identification. This granular approach ensures that individual test results can be cached and only the necessary tests rerun, greatly reducing CI pipeline times and facilitating more accurate flaky test detection.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F0YxcxIR7QU0%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D0YxcxIR7QU0&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F0YxcxIR7QU0%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a7df29e8f478a082a09a1ff31d6b22a0/href">https://medium.com/media/a7df29e8f478a082a09a1ff31d6b22a0/href</a></iframe><h3>Addressing flaky tests with test deflaking</h3><p>Flaky tests can be a significant bottleneck in the CI process. Nx tackles this issue head-on by intelligently <a href="https://nx.dev/ci/features/flaky-tasks">re-running only the flaky tasks</a>, rather than the entire pipeline. This approach not only saves time but also provides developers with more confidence in their CI pipeline’s reliability.</p><p>Nx creates a hash of all the inputs for a task whenever it is run. If it encounters a task that fails with a particular set of inputs and then succeeds with those same inputs, Nx knows for a fact that the task is flaky.</p><h3>New Nx features that tie in with our core speed improvements</h3><h4>Module Federation</h4><p>With the help of Nx and the Module Federation setup that Nx offers, you can split up large Angular apps into smaller “vertical slices”. This can significantly speed up your builds and app startup time. Nx has revolutionized the use of Module Federation, especially in how static remotes are built and served. We make use of Nx’s task orchestration, allowing users to fine tune the number of builds happening in parallel to improve local startup time, manage machine resources better, allow for scaling.</p><h4>First-Class Playwright support</h4><p>With first-class support for Playwright through <strong>@nx/playwright</strong>, Nx offers out-of-the-box generators to run Playwright tests efficiently. This integration is especially powerful with features like TaskAtomizer, enhancing the testing process&#39;s speed and reliability.</p><h3>Conclusion</h3><p>Nx provides an unparalleled toolkit for developers and teams looking to optimize their development workflows, and we keep making it faster. By intelligently leveraging modern technologies and innovative optimizations, Nx delivers speed, efficiency, and reliability, allowing teams to focus on what matters most: building great software.</p><h3>Learn more</h3><p>Check out the <a href="https://github.com/mandarini/my-nx-nuxt-workspace">example repo</a> used in this blog post or one of the links below to learn more:</p><p>- 🧠 <a href="https://nx.dev/">Nx Docs</a><br>- 👩‍💻 <a href="https://github.com/nrwl/nx">Nx GitHub</a><br>- 💬 <a href="https://go.nx.dev/community">Nx Community</a><br>- 📹 <a href="https://www.youtube.com/@nxdevtools">Nx Youtube Channel</a><br>- 🚀 <a href="https://nx.app/">Speed up your CI</a></p><p>Also, if you liked this, click the 👏and make sure to follow <a href="https://twitter.com/psybercity">Katerina</a> and <a href="https://twitter.com/nxdevtools">Nx</a> on Twitter for more!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e74293ae697b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nrwl/monorepos-why-speed-matters-e74293ae697b">Monorepos — Why Speed Matters</a> was originally published in <a href="https://medium.com/nrwl">Nx Devtools</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing @nx/nuxt: Enhanced Nuxt.js Support in Nx]]></title>
            <link>https://medium.com/nrwl/introducing-nx-nuxt-enhanced-nuxt-js-support-in-nx-01eac78034fc?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/01eac78034fc</guid>
            <category><![CDATA[nuxtjs]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[nuxt]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Tue, 06 Feb 2024 14:38:28 GMT</pubDate>
            <atom:updated>2024-02-19T15:20:38.347Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qlUpGQE9o0umNFIDwrsV-g.png" /><figcaption>@nx/nuxt with 💎Project Crystal💎</figcaption></figure><p>We’re excited to introduce a new way to enhance your <a href="https://nuxt.com/">Nuxt</a> development workflow! After the Vue plugin, we’re introducing our new Nx plugin for Nuxt, <a href="https://nx.dev/nx-api/nuxt">@nx/nuxt</a>. Designed for Nuxt developers and existing Nx users alike, this integration brings the best of both worlds into your development ecosystem, enabling you to leverage Nx’s powerful capabilities seamlessly within your Nuxt projects.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F1L-bDvEemoc%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D1L-bDvEemoc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F1L-bDvEemoc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7f960b3d1135fe1bcd7402f7fb4a2d89/href">https://medium.com/media/7f960b3d1135fe1bcd7402f7fb4a2d89/href</a></iframe><h3>Why Consider Nx for Your Nuxt Projects?</h3><p>Using Nx with your Nuxt.js projects presents the following advantages:</p><ul><li><strong>Monorepo Management</strong>: Simplify the management of multiple projects within a single repository, facilitating code sharing and reducing overhead.</li><li><strong>Modular Development</strong>: Break down your Nuxt app into manageable, independent modules that can be developed, tested, and deployed in isolation.</li><li><strong>Enhanced Caching</strong>: Accelerate your development with Nx’s intelligent caching, automatically configured for your Nuxt projects.</li><li><strong>Nx generators</strong>: Nx provides generators for scaffolding new Nuxt applications, with support for Jest, Storybook, and e2e test generation with Cypress or Playwright.</li><li><strong>Automated upgrades</strong>: Nx offers a set of migrators that help you upgrade your projects.</li></ul><h3>Getting Started with Nx and Nuxt.js</h3><p>Whether you’re initiating a new project or integrating into an existing one, <a href="https://nx.dev/nx-api/nuxt">@nx/nuxt</a> offers a straightforward setup process:</p><h4>Starting a New Nx Workspace with Nuxt</h4><p>Creating a new Nx workspace optimized for Nuxt is as simple as running:</p><pre>npx create-nx-workspace@latest --preset=nuxt</pre><p>Our setup wizard will guide you through the initial configuration, ensuring your workspace is tailored to your needs:</p><pre>npx create-nx-workspace@latest<br>&gt; NX Let&#39;s create a new workspace [https://nx.dev/getting-started/intro]<br>✔ Where would you like to create your workspace? · my-org<br>✔ Which stack do you want to use? · vue<br>✔ What framework would you like to use? · nuxt<br>✔ Integrated monorepo, or standalone project? · integrated<br>✔ Application name · my-app<br>✔ Test runner to use for end to end (E2E) tests · playwright (also cypress)<br>✔ Default stylesheet format · scss (also css, less)<br>✔ Set up CI with caching, distribution and test deflaking · github</pre><p>This command will create a new Nx workspace with a single Nuxt application, complete with essential features and ready for development.</p><h4>Enhancing an Existing Nuxt Project with Nx</h4><p>Integrating Nx into an existing Nuxt.js project has never been easier, with the help of the `nx init` command. This command will add Nx to your project without the need to disrupt your current setup.</p><p><strong>How It Works</strong></p><p>When you run `<em>nx init</em>` in your existing Nuxt.js project, Nx does the following:</p><ul><li><strong>Installs @nx/nuxt</strong>: Adds the necessary Nx and <a href="http://twitter.com/nx/nuxt">@nx/nuxt</a> dependencies to your project, enabling Nx’s features while keeping your existing setup intact.</li><li><strong>Understands Existing Configurations</strong>: Nx automatically recognizes your nuxt.config.js or nuxt.config.ts file, ensuring that all your custom configurations, scripts, and commands are preserved and utilized.</li><li><strong>Minimal Configuration</strong>: Only a minimal `nx.json` file is added to your project. This file is used to configure the `@nx/nuxt` plugin if needed, but in most cases, your existing Nuxt.js configurations will suffice.</li></ul><p>To begin the integration process, simply navigate to the root of your existing Nuxt.js project and run:</p><pre>npx nx init</pre><p>This approach offers several key benefits for teams looking to adopt Nx:</p><ul><li><strong>Zero Disruption</strong>: Your project will continue to use its existing configurations, and the existing configuration entrypoint files. There’s no need to learn new configuration syntaxes or reconfigure your project to start using Nx.</li><li><strong>Immediate Value</strong>: Instantly gain access to Nx’s powerful developer tools and build system, without significant changes to your project.</li><li><strong>Future Flexibility</strong>: As your project grows, Nx is ready to scale with you. You can gradually adopt more Nx features and plugins over time, at a pace that suits your team.</li></ul><h3>Using Nx to run your Nuxt app</h3><p>Nx scans your workspace to look for Nuxt configuration files (eg. `<em>nuxt.config.ts</em>`). It uses these files to understand where your Nuxt projects live, and uses them to set up tasks that can be invoked through Nx, like `serve` and `build`. So, in your Nx workspace, you can then run:</p><pre>nx serve my-nuxt-app</pre><pre>nx build my-nuxt-app</pre><p>and these commands will call the `nuxt` CLI under the hood, enhanced with Nx’s features.</p><p>You can see a visual representation of your task dependencies by running</p><pre>nx graph</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Pe4q3lz0kPobBu5PT5dJWw.png" /><figcaption>The dependency graph for the `build` task.</figcaption></figure><p>You can also see how Nx configures your tasks, by running:</p><pre>nx show project my-nuxt-app — web</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ca1knO9IgSBnxra240BlMQ.png" /><figcaption>Project details.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0aLEwprK99XUCwzCt4HLsA.png" /><figcaption>Project details — individual task details</figcaption></figure><h4>Using Nx Console</h4><p>You get access to all these features through our VSCode and WebStorm <a href="https://nx.dev/features/integrate-with-editors">Nx Console extension</a>.</p><p>You can use Nx Console to visualise tasks, and understand where each inferred task (like `build` and `serve` in Nuxt’s case) is coming from, with our codelens-like feature, as an alternative to the ` — web` flag on the `nx show project` command.</p><p>Nx Console is also very convenient for generating code and running tasks, since it offers a graphical user interface for all the amazing features of the Nx CLI.</p><h3>What Does Nx Bring to Your Nuxt Development?</h3><p>With `@nx/nuxt`, your Nuxt projects gain automatic recognition of `build` and `serve` processes. There’s no need to deal with unfamiliar configurations; Nx intuitively understands your Nuxt project structure and optimizes accordingly.</p><h4>Modularize and Scale with Ease</h4><p>One of the most compelling aspects of using Nx with Nuxt.js is the ability to modularize large applications into manageable libraries or components. This not only makes your codebase more organized and maintainable but also significantly enhances your development workflow and CI processes.</p><h4>Breaking Down a Monolithic Nuxt App</h4><p>Large Nuxt applications can become challenging to maintain and scale over time. By adopting Nx, you can structure your Nuxt app as a collection of smaller, focused libraries. Each library can encapsulate specific functionalities or features, such as UI components, utilities, or business logic.</p><h4>Independent Development and Testing</h4><p>This modular structure allows teams to work on different aspects of the application in parallel, reducing bottlenecks and improving collaboration. Furthermore, you can run tests, linters, and other checks independently for each library, making your development process more efficient and targeted.</p><p>For instance, if you want to create a new Vue UI library, you can use the following command:</p><pre>nx generate @nx/vue:lib my-shared-ui</pre><p>This command creates a <em>my-shared-ui</em> library within your workspace, which can then be used across your Nuxt app and potentially other applications within the same workspace.</p><h4>Enhancing CI with Modular Builds</h4><p>On the CI front, Nx’s modular approach makes things much faster. You can configure your CI pipeline to build, test, and deploy only the affected libraries and applications, thanks to Nx’s advanced dependency graph analysis. This results in faster CI runs and more efficient resource utilization.</p><h4>Sharing Code Between Applications</h4><p>Nx’s workspace model facilitates code sharing between projects, which is particularly useful in monorepos containing multiple front-end projects. With Nx, sharing UI components, utilities, or services between these applications becomes straightforward.<br>To share code, simply import the library into your Nuxt and Vue applications as needed. Nx takes care of the rest, ensuring that dependencies are correctly managed and that your applications remain buildable and testable.</p><p>Imagine a scenario where your workspace contains a Nuxt application for your public-facing website and a Vue application for an internal tool. You can create a shared library for common UI components, such as buttons, inputs, and modals, and use these components in both applications. This not only reduces duplication but also ensures consistency across your projects.</p><pre>// Importing a shared UI component in your Nuxt app<br>import { MyButton } from ‘@my-org/my-shared-ui’;</pre><pre>// Importing the same component in your Vue app<br>import { MyButton } from ‘@my-org/my-shared-ui’;</pre><h4>Visualizing Your Project Structure</h4><p>Nx provides a clear overview of your project’s structure and dependencies, making it easier to manage complex applications. The Nx Console extension for VSCode, for instance, offers a graphical interface to visualize and run tasks, enhancing your development experience.</p><p>In your workspace, you can run</p><pre>nx graph</pre><p>and see the structure of your projects:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NJXDbrlm4SXEjcCS52Z9GA.png" /><figcaption>A project graph — showing project dependencies</figcaption></figure><h3>Embracing Nx in Your Nuxt Journey</h3><p>Whether you’re starting a new Nuxt project or looking to enhance an existing one, Nx offers a compelling set of tools and features to streamline your development process. From modularization to caching, the integration of Nx into your Nuxt projects promises a more efficient, scalable, and enjoyable development experience. By embracing Nx’s capabilities in your Nuxt development, you’re not just optimizing your current workflow; you’re future-proofing your development process. As your projects grow and evolve, Nx’s modular architecture and powerful tooling will continue to provide value, making your development experience more enjoyable and productive.</p><h3>Nx Live With Nuxt Maintainer Daniel Roe</h3><p>Don’t miss Nx team members Zack and Katerina with Nuxt’s maintainer, Daniel Roe — live!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FuHwUxFYX2DY%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DuHwUxFYX2DY&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FuHwUxFYX2DY%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/2d76ffdc057d88bc26774d1aaa2feb9d/href">https://medium.com/media/2d76ffdc057d88bc26774d1aaa2feb9d/href</a></iframe><h3>Learn more</h3><p>Check out the <a href="https://github.com/mandarini/my-nx-nuxt-workspace">example repo</a> used in this blog post or one of the links below to learn more:</p><p>- 🧠 <a href="https://nx.dev/">Nx Docs</a><br>- 👩‍💻 <a href="https://github.com/nrwl/nx">Nx GitHub</a><br>- 💬 <a href="https://go.nx.dev/community">Nx Community</a><br>- 📹 <a href="https://www.youtube.com/@nxdevtools">Nx Youtube Channel</a><br>- 🚀 <a href="https://nx.app/">Speed up your CI</a></p><p>Also, if you liked this, click the 👏and make sure to follow <a href="https://twitter.com/psybercity">Katerina</a> and <a href="https://twitter.com/nxdevtools">Nx</a> on Twitter for more!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=01eac78034fc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nrwl/introducing-nx-nuxt-enhanced-nuxt-js-support-in-nx-01eac78034fc">Introducing @nx/nuxt: Enhanced Nuxt.js Support in Nx</a> was originally published in <a href="https://medium.com/nrwl">Nx Devtools</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nx Docs AI Assistant]]></title>
            <link>https://medium.com/nrwl/nx-docs-ai-assistant-433d238e45d4?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/433d238e45d4</guid>
            <category><![CDATA[nx-ai-assitant]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[documentation]]></category>
            <category><![CDATA[openai]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Tue, 21 Nov 2023 16:30:22 GMT</pubDate>
            <atom:updated>2023-11-21T16:37:46.431Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8IiEWOqfsvqXWD5E" /></figure><h3>Introduction</h3><p>The <a href="https://nx.dev/ai-chat">Nx Docs AI Assistant</a> is a tool designed to provide users with answers straight from the Nx documentation. In this article I will explain how it is built, and how we ensure accuracy and relevance.</p><p>In the end of this document I have added a “<a href="#d485">glossary</a>” of terms that are used throughout this document.</p><h3>Why have an AI assistant for documentation?</h3><p>First of all, let’s answer this simple question: why do you need an AI assistant for a documentation site in the first place? Using an AI assistant for documentation search and retrieval can offer a number of benefits for both users and authors. For users, the challenges of navigating through a large volume and density of documentation are alleviated. Unlike static keyword matching, AI enables more personalized and contextual search, allowing for more complex or sophisticated queries beyond simple keywords. This creates a dynamic feedback loop where users can ask follow-up questions, mix and combine documents, and ultimately enjoy an enhanced user experience that goes beyond basic documentation retrieval.</p><p>For authors, a docs AI assistant provides valuable insights into user behavior. It can identify the questions users are frequently asking, pointing to areas where more documentation may be needed. Additionally, if the AI consistently provides unsatisfactory or incorrect responses to certain queries, it could highlight unclear or lacking portions of the documentation. This not only allows for targeted improvements but also makes more parts of the documentation easily accessible to users through intelligent linking. Overall, it can enrich user interaction and help with future content strategy.</p><h3>The Nx Docs AI Assistant Workflow</h3><h3>Overview</h3><p>In a nutshell, the Nx Docs AI Assistant works in the following way:</p><ol><li>Split our docs into smaller chunks</li><li>Create an <a href="#ab7f">embedding</a> for each chunk</li><li>Save all these embeddings in <a href="https://supabase.com/docs/guides/database/extensions/pgvector">Postgres using pgvector (Supabase!</a>)</li><li>Get question from the user</li><li>Create embedding for user’s question</li><li>Perform a vector similarity search on your database — bring back all the chunks of your documentation that are similar to the user’s question</li><li>Use the <a href="https://platform.openai.com/docs/guides/text-generation/chat-completions-api">GPT chat completion</a> function. Pass a prompt, the user’s question and the retrieved chunks from the docs. GPT will then try to extract the relevant facts from these chunks, in order to formulate a coherent answer.</li></ol><p>This is based on the Web Q&amp;A Tutorial from OpenAI (<a href="https://platform.openai.com/docs/tutorials/web-qa-embeddings">https://platform.openai.com/docs/tutorials/web-qa-embeddings</a>) and Supabase’s Vector Search example (<a href="https://supabase.com/docs/guides/ai/examples/nextjs-vector-search">https://supabase.com/docs/guides/ai/examples/nextjs-vector-search</a>).</p><p>It’s important to note here that we are not “training the model on our docs”. The model is pretrained. We are just giving the model parts of our docs which are relevant to the user’s question, and the model creates a coherent answer to the question. It’s basically like pasting in ChatGPT a docs page and asking it “how do I do that?”. Except in this case, we’re first searching our documentation and giving GPT only the relevant parts (more about how we do that later in this article), which it can “read” and extract information from.</p><h3>Step 1: Preprocessing our docs</h3><p>Every few days, <a href="https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/.github/workflows/generate-embeddings.yml">we run an automated script</a> that will generate <a href="#ab7f">embeddings</a> (numeric/vector representations of words and phrases) for our documentation, and store these embeddings in Supabase. As mentioned above, this step has 3 parts:</p><h4>Split our docs into smaller chunks</h4><p>Most of this code follows the example from <a href="https://github.com/supabase-community/nextjs-openai-doc-search">Supabase’s Clippy</a>. It breaks the markdown tree into chunks, it keeps the heading and it also creates a checksum, to keep track of changes.</p><p>Ref in the code: <a href="https://github.com/nrwl/nx/blob/0197444df5ea906f38f06913b2bc366e04b0acc2/tools/documentation/create-embeddings/src/main.mts#L66">https://github.com/nrwl/nx/blob/0197444df5ea906f38f06913b2bc366e04b0acc2/tools/documentation/create-embeddings/src/main.mts#L66</a></p><p>This part is copied from: <a href="https://github.com/supabase-community/nextjs-openai-doc-search/blob/main/lib/generate-embeddings.ts">https://github.com/supabase-community/nextjs-openai-doc-search/blob/main/lib/generate-embeddings.ts</a></p><pre>export function processMdxForSearch(content: string) {<br>  // …<br>  const mdTree = fromMarkdown(content, {});<br>  const sectionTrees = splitTreeBy(mdTree, (node) =&gt; node.type === &#39;heading&#39;);<br>  // …<br>  const sections = sectionTrees.map((tree: any) =&gt; {<br>    const [firstNode] = tree.children;<br>    const heading =<br>      firstNode.type === &#39;heading&#39; ? toString(firstNode) : undefined;<br>    return {<br>      content: toMarkdown(tree),<br>      heading,<br>      slug,<br>    };<br>  });<br>  return {<br>    checksum,<br>    sections,<br>  };<br>}</pre><h4>Create an embedding for each chunk</h4><p>Using `openai.embeddings.create` function with the model “text-embedding-ada-002” we are creating an embedding for each chunk.</p><p>Ref in the code:</p><p><a href="https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/tools/documentation/create-embeddings/src/main.mts#L314">https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/tools/documentation/create-embeddings/src/main.mts#L314</a></p><pre>const embeddingResponse = await openai.embeddings.create({<br>  model: &#39;text-embedding-ada-002&#39;,<br>  input,<br>});</pre><h4>Save all these embeddings in Postgres using pgvector, on Supabase.</h4><p>Store this embedding in Supabase, in a database that has already been created, following the steps mentioned here:</p><p><a href="https://supabase.com/docs/guides/ai/examples/nextjs-vector-search?database-method=dashboard#prepare-the-database">https://supabase.com/docs/guides/ai/examples/nextjs-vector-search?database-method=dashboard#prepare-the-database</a></p><p>Essentially, we are setting up two PostgreSQL tables on Supabase. Then, we are inserting the embeddings into these tables.</p><p>Ref in code: <a href="https://github.com/nrwl/nx/blob/master/tools/documentation/create-embeddings/src/main.mts#L327">https://github.com/nrwl/nx/blob/master/tools/documentation/create-embeddings/src/main.mts#L327</a></p><pre>const { data: pageSection } =<br>  await supabaseClient<br>    .from(&#39;nods_page_section&#39;).insert({<br>      page_id: page.id,<br>      slug,<br>      heading,<br>      longer_heading,<br>      content,<br>      url_partial,<br>      token_count,<br>      embedding<br>    }) // …</pre><h3>Step 2: User query analysis and search</h3><p>When a user poses a question to the assistant, we calculate the embedding for the user’s question. The way we do that is, again, using openai.embeddings.create function with the model text-embedding-ada-002.</p><p>Ref in code: <a href="https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L58">https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L58</a></p><pre>const embeddingResponse: OpenAI.Embeddings.CreateEmbeddingResponse =<br>  await openai.embeddings.create({<br>    model: &#39;text-embedding-ada-002&#39;,<br>    input: sanitizedQuery + getLastAssistantMessageContent(messages),<br>  });</pre><p>The assistant compares the query embedding with these documentation embeddings to identify relevant sections. This comparison is essentially measuring how close the query’s vector is to the documentation vectors. The closer they are, the more related the content. The way this works is that it sends the user’s question embedding to Supabase, to a PostgreSQL function, which runs a vector comparison between the user’s question embedding and the stored embeddings in the table. The PostgreSQL function returns all the similar documentation chunks.</p><p>The function that is used uses the dot product between vectors to calculate similarity. For normalized vectors, the dot product is equivalent to cosine similarity. Specifically, when two vectors A and B are normalized (i.e., their magnitudes are each 1), the cosine similarity between them is the same as their dot product. The OpenAI embeddings are normalized to length 1, so cosine similarity and dot product will produce the same results.</p><p>Ref in code:</p><p><a href="https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L70">https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L70</a></p><pre>const { data: pageSections } = await supabaseClient.rpc(<br>  &#39;match_page_sections&#39;,<br>  {<br>    embedding,<br>    // …<br>  }<br>);</pre><h3>Step 3: Generating a Response</h3><p>With the relevant sections (documentation chunks) identified and retrieved, GPT (the generative AI) steps in. Using the relevant sections as <strong>context</strong> and following a systematic approach, GPT crafts a response.</p><p>This approach the AI is instructed to use (in the <strong>prompt</strong>) is the following:</p><ul><li>Identify CLUES from the query and documentation.</li><li>Deduce REASONING based solely on the provided Nx Documentation.</li><li>EVALUATE its reasoning, ensuring alignment with Nx Documentation.</li><li>Rely on previous messages for contextual continuity.</li></ul><h4>Ensuring Quality</h4><p>If there’s no matching section in the documentation for a query, the script throws a “no_results” error. So, after the initial search in the docs (PostgreSQL function), if the search returns no results (no vectors found that are similar enough to the user’s question vector), the process stops, and our Assistant replies that it does not know the answer.</p><h4>The use of useChat function</h4><p>It’s necessary here to clarify that we use the useChat (<a href="https://sdk.vercel.ai/docs/api-reference/use-chat">https://sdk.vercel.ai/docs/api-reference/use-chat</a>) function of the <a href="https://sdk.vercel.ai/docs">Vercel AI SDK</a>. This function, as mentioned in the docs, does the following:</p><blockquote><em>It enables the streaming of chat messages from your AI provider, manages the state for chat input, and updates the UI automatically as new messages are received.</em></blockquote><p>It, essentially, takes care of the following things:</p><ol><li>You don’t have to worry about manually creating a “messages” array to store your “conversation” (messages you exchange) with the GPT endpoint</li><li>You don’t have to manually implement the streaming functionality in your UI</li></ol><p>Then, in your React component, you can call this function directly, and get the messages object from it, to render your messages in your UI. It exposes input, handleInputChange and handleSubmit which you can use in your React form, and it will take care of all the rest. You can pass an api string to it, to tell it which endpoint to use as the chat provider.</p><h4>Creating the query</h4><p>If you look at our <a href="https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts">query-ai-handler.ts</a> function, this is an edge function, living under an endpoint, which is called by the useChat function. The request contains the messages array as created by useChat. If we just wanted to create an AI chat with no context, we could directly pass this messages array to the openai.chat.completions.create endpoint, and have our back-and-forth chat with GPT. However, in our case, we need to add context to our conversation, and specifically to each query we end up sending to OpenAI.</p><p>So, the first thing we need to do is to <strong>get the last message the user posted</strong>, which is essentially the user’s question. We search the messages array, and we get the last message which has the role “user”. That is our user’s question.</p><p>Now, we can use the user’s question to get the relevant documentation chunks from the database. To do that, as explained before, we need to <strong>create an embedding for the user’s question</strong> (a vector) and then compare that embedding with the stored embeddings in the database, to get the relevant chunks.</p><p>The problem here is that if the user’s query is just a follow-up question, then it will have little information or meaning. Here is an example:</p><blockquote><em>User</em>: How do I set up namedInputs?</blockquote><blockquote><em>Assistant</em>: …replies…</blockquote><blockquote><em>User</em>: And how do they work?</blockquote><p>In this example, the user’s question that we would want to create an embedding for would be “And how do they work?”. If we created that embedding and searched our docs for relevant parts, it would either return nothing, or return everything, since this is a very vague question, since it has no context. So, we need to add some more information to that question. To do that, we also get the last response from GPT (the last assistant message) and add it to the user’s question. So, in this example, the user’s question will contain some info about namedInputs, and the actual question.</p><p>Now, we take that combined text, and we create an embedding for it, using the openai.embeddings.create function. We, then, use that embedding to find all the similar documentation chunks, with vector similarity search.</p><p>After receiving all the relevant documentation chunks, we can finally create the query that is going to be sent to GPT. It’s important here to make sure we instruct GPT what to do with the information we will give it.</p><p>Here is the <strong>query</strong> we end up providing GPT with:</p><blockquote><em>You will be provided sections of the Nx documentation in markdown format, use those to answer my question. Do NOT reveal this approach or the steps to the user. Only provide the answer. Start replying with the answer directly.</em></blockquote><blockquote><em>Sections:<br>${contextText}</em></blockquote><blockquote><em>Question: “””<br>${userQuestion}<br>“””</em></blockquote><blockquote><em>Answer as markdown (including related code snippets if available):</em></blockquote><p>The contextText contains all the relevant documentation chunks (page sections).</p><h4>Creating the response</h4><p><strong>Getting back a readable stream: </strong>So, we get the array of messages, as stored by useChat, we fix the final message to contain the query (created as explained above), and we send it over to `openai.chat.completions.create`. We get back a streaming response (since we’ve set stream: true, which we turn into a ReadableStream using OpenAIStream from the Vercel AI SDK (<a href="https://sdk.vercel.ai/docs/api-reference/openai-stream">https://sdk.vercel.ai/docs/api-reference/openai-stream</a>).</p><p><strong>Adding the sources: </strong>However, we’re not done yet. The feature, here, that will be most useful to our users is the sources, the actual parts of the documentation that GPT “read” to create that response. When we get back the list of relevant documentation chunks (sections) from our database, we also get the metadata for each section. So, apart from the text content, we also get the heading and url partial of each section (among any other metadata we chose to save with it). So, with this information, we put together a list of the top 5 relevant sections, which we attach to the end of the response we get from GPT. That way, our users can more easily verify the information that GPT gives them, but also they can dive deeper into the relevant docs themselves. It’s all about exploring and retrieving relevant information, after all.</p><p><strong>Sending the final response to the UI: </strong>With the sources appended to the response, we return a StreamingTextResponse from our edge function, which the useChat function receives, and appends to the messages array automatically.</p><h3>Allow user to reset the chat</h3><p>As explained, each question and answer relies on the previous questions and answers of the current chat. If a user needs to ask something completely irrelevant or different, we are giving the user the ability to do so by providing a “Clear chat” button, which will reset the chat history, and start clean.</p><h3>Gathering feedback and evaluating the results</h3><p>It’s very important to gather feedback from the users and evaluate the results. Any AI assistant is going to give wrong answers, because it does not have the ability to critically evaluate the responses it creates. It relies on things it has read, but not in the way a human relies on them. It generates the next most probable word (see glossary for generative AI below). For that reason, it’s important to do the following things:</p><ol><li>Inform users that they should always double-check the answers and do not rely 100% on the AI responses</li><li>Provide users with feedback buttons and/or a feedback form, where they can evaluate whether a response was good or bad. At Nx we do that, and we also associate each button click with the question the user asked, which will give us an idea around which questions the AI gets right or wrong.</li><li>Have a list of questions that you ask the AI assistant, and evaluate its responses internally. Use these questions as a standard for any changes made in the assistant.</li></ol><h3>Wrapping up</h3><p>In this guide, we’ve explored the intricacies of the Nx Docs AI Assistant, an innovative tool that enhances the experience of both users and authors of Nx documentation. From understanding the need for an AI assistant in navigating complex documentation to the detailed workflow of the Nx Docs AI Assistant, we have covered the journey from preprocessing documentation to generating coherent and context-aware responses.</p><p>Let’s see at some key takeaways:</p><p><strong>Enhanced User Experience:</strong> The AI assistant significantly improves user interaction with documentation by offering personalized, context-aware responses to queries. This not only makes information retrieval more efficient but also elevates the overall user experience.</p><p><strong>Insights for Authors:</strong> By analyzing frequently asked questions and areas where the AI struggles, authors can pinpoint documentation gaps and areas for improvement, ensuring that the Nx documentation is as clear and comprehensive as possible.</p><p><strong>OpenAI API utilization</strong>: The use of embeddings, vector similarity search, and GPT’s generative AI capabilities demonstrate a sophisticated approach to AI-driven documentation assistance. This blend of technologies ensures that users receive accurate and relevant responses.</p><p><strong>Continuous Learning and Improvement:</strong> The system’s design includes mechanisms for gathering user feedback and evaluating AI responses, which are crucial for ongoing refinement and enhancement of the assistant’s capabilities.</p><p><strong>Transparency and User Trust:</strong> By openly communicating the limitations of the AI and encouraging users to verify the information, the system fosters trust and promotes responsible use of AI technology.</p><p><strong>Accessibility and Efficiency:</strong> The AI assistant makes Nx documentation more accessible and navigable, especially for complex or nuanced queries, thereby saving time and enhancing productivity and developer experience.</p><h3>Future steps</h3><p>OpenAI released the Assistants API, which takes the burden of chunking the docs, creating embeddings, storing the docs in a vector database, and querying that database off the shoulders of the developers. This new API offers all these features out of the box, removing the need to create a customized solution, as the one explained above. It’s still in beta, and it remains to be seen how it’s going to evolve, and if it’s going to overcome some burdens it poses at the moment. You can <a href="https://medium.com/@pakotinia/openais-assistants-api-a-hands-on-demo-110a861cf2d0">read more about the new Assistants API in this blog post</a>, which contains a detailed demo on how to use it for documentation q&amp;a.</p><h3>Glossary</h3><h3>Core concepts</h3><p>I find it useful to start by explaining what some terms — which are going to be used quite a lot throughout this blog post — mean.</p><h3>Embeddings</h3><h4>What they are</h4><p>In the context of machine learning, embeddings are a type of representation for text data. Instead of treating words as mere strings of characters, embeddings transform them into <strong>vectors</strong> (lists of numbers) in a way that captures their meanings. In embeddings, vectors are like digital fingerprints for words or phrases, converting their essence into a series of numbers that can be easily analyzed and compared.</p><h4>Why they matter</h4><p>With embeddings, words or phrases with similar meanings end up having <strong>vectors</strong> that are close to each other, making it easier to compare and identify related content.</p><h3>Generative AI</h3><h4>What it is</h4><p>Generative AI, the technology driving the Nx Docs AI Assistant, is a subset of AI that’s trained, not just to classify input data, but to generate new content.</p><p>How it works</p><p>Generative AI operates like a sophisticated software compiler. Just as a compiler takes in high-level code and translates it into machine instructions, generative AI takes in textual prompts and processes them through layers of neural network operations, resulting in detailed and coherent text outputs. It’s like providing a programmer with a high-level task description, and they write the necessary code to achieve it, except here the ‘programmer’ is the AI, and the ‘code’ is the generated text response.</p><h4>What Does “Generation” Mean in AI Context?</h4><p>In AI, especially with natural language processing models, “generation” refers to the process of producing sequences of data, in our case, text. It’s about creating content that wasn’t explicitly in the training data but follows the same patterns and structures.</p><h4>How Does GPT Predict the Next Word?</h4><p>For our Nx Docs AI assistant we use GPT. GPT, which stands for “Generative Pre-trained Transformer”, works using a predictive mechanism. At its core, it’s trained to predict the next word in a sentence. When you provide GPT with a prompt, it uses that as a starting point and keeps predicting the next word until it completes the response or reaches a set limit.</p><p>It’s like reading a sentence and trying to guess the next word based on what you’ve read so far. GPT does this but by using a massive amount of textual data it has seen during training, enabling it to make highly informed predictions.</p><h3>Context and Prompting — their role in AI models</h3><h4>Context</h4><p>In the context of AI, “context” refers to the surrounding information, data, or conditions that provide a framework or background for understanding and interpreting a specific input, ensuring that the AI’s responses or actions are relevant, coherent, and meaningful in a given situation</p><h4>Prompts</h4><p>The prompt acts as an initial “seed” that guides the AI’s output. While the AI is trained on vast amounts of text, it relies on the prompt for context. For example, a prompt like “tell me about cats” might result in a broad answer, but “summarize the history of domesticated cats” narrows the model’s focus.</p><p>By refining prompts, users can better direct the AI’s response, ensuring the output matches their intent. In essence, the prompt is a tool to direct the AI’s vast capabilities to a desired outcome.</p><h3>The GPT Chat Completion Roles</h3><h3>System</h3><p>The “System” role typically sets the “persona” or the “character” of the AI. It gives high-level instructions on how the model should behave during the conversation. We start the instructions with “You are a knowledgeable Nx representative.” We also instruct the model about the format of its answer: “Your answer should be in the form of a Markdown article”. You can read the full instructions on GitHub.</p><h3>User</h3><p>The “User” role is straightforward. This is the input from the end-user, which the AI responds to. The user’s query becomes the User role message. This role guides what the AI should be talking about in its response. It’s a direct prompt to the AI to generate a specific answer. In our case, we take the user’s query, and we add it in a longer prompt, which specific steps the model must follow (as explained above). That way, the model focuses on the specific steps we’ve laid out, making it the immediate context for generating the answer. This is one more step towards more accurate answers based on our documentation only. Inside the prompt, which has the instructions, and the user’s query, we always add the context text as well, which are the relevant parts that are retrieved from the Nx Documentation.</p><h3>Assistant</h3><p>This role, in the context of OpenAI’s chat models, is the response of the AI. Previous Assistant responses can be included in the chat history to provide context, especially if a conversation has back-and-forth elements. This helps the model generate coherent and contextually relevant responses in a multi-turn conversation.</p><h3>Learn more</h3><p>- 🧠 <a href="https://nx.dev/">Nx Docs</a><br>- 👩‍💻 <a href="https://github.com/nrwl/nx">Nx GitHub</a><br>- 💬 <a href="https://go.nrwl.io/join-slack">Nx Community Slack</a><br>- 📹 <a href="https://www.youtube.com/@nxdevtools">Nx Youtube Channel</a><br>- 🚀 <a href="https://nx.app/">Speed up your CI</a></p><p>Also, if you liked this, click the 👏and make sure to follow <a href="https://twitter.com/psybercity">Katerina</a> and <a href="https://twitter.com/nxdevtools">Nx</a> on Twitter for more!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=433d238e45d4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nrwl/nx-docs-ai-assistant-433d238e45d4">Nx Docs AI Assistant</a> was originally published in <a href="https://medium.com/nrwl">Nx Devtools</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[OpenAI’s Assistants API — A hands-on demo]]></title>
            <link>https://pakotinia.medium.com/openais-assistants-api-a-hands-on-demo-110a861cf2d0?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/110a861cf2d0</guid>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[openai]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[assistants-api]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Wed, 15 Nov 2023 20:00:54 GMT</pubDate>
            <atom:updated>2023-11-15T20:11:51.688Z</atom:updated>
            <content:encoded><![CDATA[<h3>OpenAI’s Assistants API — A hands-on demo</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SBUqlGIWUZiPDP1fVclcPA.png" /><figcaption>An AI assistant with a cat for its knowledge base.</figcaption></figure><h3>Introduction</h3><p>Let me start by saying that I am not an AI expert and some of the things I say here may sound simplistic, but it’s the way that helps me understand them. So, let’s begin.</p><p>OpenAI just released the Assistants API, which pretty much simplifies the process of setting up a Q&amp;A system based on a knowledge base for GPT.</p><p>To quote the <a href="https://platform.openai.com/docs/assistants/tools/knowledge-retrieval">OpenAI docs</a>:</p><blockquote><em>Retrieval augments the Assistant with knowledge from outside its model, such as proprietary product information or documents provided by your users. Once a file is uploaded and passed to the Assistant, OpenAI will automatically chunk your documents, index and store the embeddings, and implement vector search to retrieve relevant content to answer user queries.</em></blockquote><p>This is pretty amazing, if you think that the process of chunking, indexing, storing and vector-searching the embeddings was done separately, with customized solutions.</p><p>I have a short section at the end of this document, explaining what embeddings and vectors are. And also a brief explanation on how you would implement a q&amp;A based on a knowledge base without the Assistants API.</p><h3>The demo</h3><p>You can check out my Github repo which contains the example which I am going to explain here in detail:</p><h4><a href="https://github.com/mandarini/openai-assistant-demo">https://github.com/mandarini/openai-assistant-demo</a></h4><p>Please spend some time to also go through the README of that repo.</p><h3>Motivation</h3><p>Building the <a href="https://nx.dev/ai">Nx AI Assistant</a> was a journey that taught us quite a lot about how to use the OpenAI APIs for chat completions, and embeddings, and gave us a taste on how to interact with LLMs. So it was only natural that I wanted to try out the brand new API that would render some of that work obsolete (at least I think so, I’m no expert).</p><p>So, in the demo, which I am going to present in this blog post, I am using the Nx docs, I am uploading them on OpenAI, and then I am adding them in a new assistant. Then, I use this assistant to ask questions about these docs.</p><h3>Project Overview</h3><p>I have created a super simple chat interface (ChatGPT helped a lot) that accepts a question from the user, and replies based on the knowledge base of the Assistant.</p><p>The logic is the following:</p><ol><li><a href="https://platform.openai.com/docs/assistants/how-it-works/creating-assistants">Create an assistant</a>: a. Upload your files to OpenAI, and b. Create an Assistant and pass it all the files id’s of the files you uploaded</li><li><a href="https://platform.openai.com/docs/assistants/how-it-works/managing-threads-and-messages">Create a new message thread</a></li><li>Create a new message in the message thread with the user’s query</li><li><a href="https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps">Run</a> the thread using the Assistant that was just created</li><li>Retrieve the run’s result once run is complete</li><li>Return the messages to the UI</li></ol><p>Let’s see these steps in more detail.</p><h3>Step 1: Creating the assistant</h3><p>The <a href="https://platform.openai.com/docs/assistants/how-it-works/creating-assistants">assistant</a> creation step happens once, and “offline”. You only need to run it again if you want to update your knowledge base. I am not going to cover this in the present blog post.</p><h4>Uploading the files</h4><p>The first thing that you need to do is upload all the files that you want to use. In this example, I am uploading the files programmatically, from a local directory, like this:</p><pre>for (const file of allFileNames) {<br>…<br>const oneFile = await openai.files.create({<br>purpose: &#39;assistants&#39;,<br>file: fs.createReadStream(filePath),<br>});<br>files.push(oneFile);<br>}</pre><p>The result of the openai.files.create gives us back, among other metadata, the file’s id. You can see the full list of your uploaded files under <a href="https://platform.openai.com/files">https://platform.openai.com/files</a> this URL. Each file can be deleted, you can copy it’s ID or you can manually upload more files. All of these actions can also be achieved programmatically with the <a href="https://platform.openai.com/docs/api-reference/files">Files</a> object and methods.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*PcI4IWu3ZMCUBW8f" /><figcaption>The uploaded files on the OpenAI platform.</figcaption></figure><h4>Creating the assistant</h4><p>Now, once you have all your files uploaded, and you also have their id’s handy, you can proceed and create your assistant, either programmatically, or through the GUI. Here is how it can be achieved programmatically:</p><pre>const assistant: OpenAI.Beta.Assistants.Assistant =<br>await openai.beta.assistants.create({<br>instructions:<br>&#39;You are Nx Assistant, a helpful assistant for Nx Dev Tools. Your primary role is to provide accurate and sourced information about Nx Dev Tools. Rely solely on the information in the files you have; do not use external knowledge. If the information is not in the files, respond with &quot;Sorry I cannot help with that&quot;.&#39;,<br>model: &#39;gpt-4–1106-preview&#39;,<br>tools: [{ type: &#39;retrieval&#39; }],<br>file_ids: […files.map((file) =&gt; file.id)],<br>});</pre><p>Of course you can customize the instructions, but make sure to add that you only want your model to rely on the existing knowledge base. The openai.beta.assistants.create function will give us back some metadata about our assistant, including its ID. We can see the list of our assistants and their information (id, etc) under this URL: <a href="https://platform.openai.com/assistants">https://platform.openai.com/assistants</a></p><h3>Step 2: Creating a thread</h3><h4>Initialize the thread</h4><p>The first thing that needs to happen when the chat page is initialized, is for our client to create a message <a href="https://platform.openai.com/docs/assistants/how-it-works/managing-threads-and-messages">Thread</a>. The message thread represents the conversation between the AI and the user. The command to create a new thread is simple:</p><pre>await openai.beta.threads.create()</pre><p>This function will return, among other metadata, also the thread’s id, which we are going to use to run it. This should ideally happen once every session (or once every time the user “resets” the chat).</p><h4>Add the user’s message to the thread</h4><p>Once we have the id of our thread, we can pass in that thread the message that the user sends from the chat interface. The user’s question, that is. Let’s take a look at the code:</p><pre>await openai.beta.threads.messages.create(thread.id, {<br>role: &#39;user&#39;,<br>content: userQuery,<br>});</pre><p>The role: user represents the (surprise!) user. The role: assistant represents the OpenAI API GPT assistant. The role names are defined in the API.</p><h3>Step 3: Running the thread</h3><h4>Create a new “run” instance</h4><p>Now that our thread is ready we can run it. First we need to create a “run” instance for our thread, and pass the id of our assistant to it:</p><pre>const run = await openai.beta.threads.runs.create(thread.id, {<br>assistant_id: assistantId as string,<br>});</pre><p>This function will return (among other metadata) a run id for this particular run.</p><h4>Wait for run to complete</h4><p>Now, this is where it gets tricky. You can read about the run lifecycle in the OpenAI docs: <a href="https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps">https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps</a></p><p>Reading from the above page:</p><blockquote><em>In order to keep the status of your run up to date, you will have to periodically retrieve the </em><em>Run object. You can check the status of the run each time you retrieve the object to determine what your application should do next. We plan to add support for streaming to make this simpler in the near future.</em></blockquote><p>We need to manually poll our run to see if it has completed. The openai.beta.threads.runs.retrieve(threadId, runId) function returns a status and once the status is completed we can finally call the openai.beta.threads.messages.list(threadId) function to get back a full list of all our messages in the thread.</p><p>Here is a simple implementation of the polling logic:</p><pre>while (timeElapsed &lt; timeout) {<br>const run = await openai.beta.threads.runs.retrieve(threadId, runId);<br>if (run.status === &#39;completed&#39;) {<br>const messagesFromThread: OpenAI.Beta.Threads.Messages.ThreadMessagesPage =<br>await openai.beta.threads.messages.list(threadId);<br>resolve({ runResult: run, messages: messagesFromThread });<br>return;<br>}<br>await new Promise((resolve) =&gt; setTimeout(resolve, interval));<br>timeElapsed += interval;<br>}</pre><p>Once we get back our result, we can return the array of messages to the front end.</p><h3>Future Enhancements</h3><p>Part of the immediate improvement plans is to add a list of sources at the end of each message. This is easy to implement, since each message object contains a list of file id’s that were used for that message to be created. Then, it will be a matter of calling the openai.beta.threads.messages.retrieve function</p><p>(<a href="https://platform.openai.com/docs/api-reference">https://platform.openai.com/docs/api-reference</a>) and getting the title of that file.</p><p>Of course, once streaming is implemented by the API, it should be used instead of polling, and it would make the response appear much faster.</p><p>Lots of other improvements could be added, and it’s still to be defined if implementing the assistant programmatically provides benefits over creating a custom GPT (ref: <a href="https://www.builder.io/blog/custom-gpt).">https://www.builder.io/blog/custom-gpt).</a></p><h3>Conclusion</h3><p><a href="https://platform.openai.com/docs/assistants/overview">OpenAI’s Assistants API</a> represents a significant step forward in making complex AI functionalities more accessible and practical for developers. By integrating a knowledge base directly into an AI assistant, we’re able to create a dynamic, responsive, and highly intelligent system that can provide specific, sourced information on demand. This was possible before, but with custom solutions, by “manually” chunking text and creating embeddings. “Manually” storing them in a vector db and implementing the vector search to return relevant sections according to a user query’s embeddings, to add as context.</p><p>The new approach is much simpler and more straightforward, as it comes out-of-the-box with the new API. Questions remain as to whether this approach can leverage issues such as potential downtime, or extra configurations that the other APIs support, but the feature is still in beta, so let’s see what will come next!</p><h3>Glossary</h3><h3>Core concepts</h3><p>I find it useful to explain what some terms mean.</p><h3>Embeddings</h3><h4>What they are</h4><p>In the context of machine learning, embeddings are a type of representation for text data. Instead of treating words as mere strings of characters, embeddings transform them into <strong>vectors</strong> (lists of numbers) in a way that captures their meanings. In embeddings, vectors are like digital fingerprints for words or phrases, converting their essence into a series of numbers that can be easily analyzed and compared.</p><h4>Why they matter</h4><p>With embeddings, words or phrases with similar meanings end up having <strong>vectors</strong> that are close to each other, making it easier to compare and identify related content.</p><h3>Generative AI</h3><h4>What it is</h4><p>Generative AI, the technology driving the Nx Docs AI Assistant, is a subset of AI that’s trained, not just to classify input data, but to generate new content.</p><p>How it works</p><p>Generative AI operates like a sophisticated software compiler. Just as a compiler takes in high-level code and translates it into machine instructions, generative AI takes in textual prompts and processes them through layers of neural network operations, resulting in detailed and coherent text outputs. It’s like providing a programmer with a high-level task description, and they write the necessary code to achieve it, except here the ‘programmer’ is the AI, and the ‘code’ is the generated text response.</p><h4>What Does “Generation” Mean in AI Context?</h4><p>In AI, especially with natural language processing models, “generation” refers to the process of producing sequences of data, in our case, text. It’s about creating content that wasn’t explicitly in the training data but follows the same patterns and structures.</p><h4>How Does GPT Predict the Next Word?</h4><p>GPT, which stands for “Generative Pre-trained Transformer”, works using a predictive mechanism. At its core, it’s trained to predict the next word in a sentence. When you provide GPT with a prompt, it uses that as a starting point and keeps predicting the next word until it completes the response or reaches a set limit.</p><p>It’s like reading a sentence and trying to guess the next word based on what you’ve read so far. GPT does this but by using a massive amount of textual data it has seen during training, enabling it to make highly informed predictions.</p><h3>Links</h3><p>Github: <a href="https://github.com/mandarini/openai-assistant-demo">https://github.com/mandarini/openai-assistant-demo</a></p><p>The live demo: <a href="https://openai-assistant.vercel.app/">https://openai-assistant.vercel.app/</a> (which may or may not work because of API limits etc — it’s just a live demo)</p><p>Follow me on X: <a href="https://twitter.com/psybercity">https://twitter.com/psybercity</a></p><p>Follow Nx: <a href="https://twitter.com/NxDevTools">https://twitter.com/NxDevTools</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=110a861cf2d0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Storybook Interaction Tests in Nx]]></title>
            <link>https://medium.com/nrwl/storybook-interaction-tests-in-nx-135fdaabc944?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/135fdaabc944</guid>
            <category><![CDATA[storybook]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[testing-tools]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Thu, 03 Aug 2023 15:02:04 GMT</pubDate>
            <atom:updated>2023-08-18T15:06:54.088Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1005/1*NfJA7VBZvDwyyZHmV8qsiw.png" /></figure><p>In Nx 16.6 we are introducing our new generators for <a href="https://storybook.js.org/docs/react/writing-tests/interaction-testing">Storybook interaction tests</a>! These new generators replace the default Cypress tests we used to generate along with a project’s Storybook configuration, particularly for those already using Storybook. The intention is that if a user chooses to use Storybook and generate Storybook configuration, to integrate in that experience Storybook Interaction testing, and skip generating Cypress tests, to keep everything in one place, in an integrated experience.</p><p><strong>Prefer a video walkthrough? We’ve got you covered</strong></p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FSaHoUx-TUs8&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DSaHoUx-TUs8&amp;image=http%3A%2F%2Fi.ytimg.com%2Fvi%2FSaHoUx-TUs8%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/3b49f3b3874f5b062e178341cb5731c1/href">https://medium.com/media/3b49f3b3874f5b062e178341cb5731c1/href</a></iframe><h3>Understanding Storybook Interaction Tests</h3><p>Interaction tests allow users to verify the functional aspects of UIs. This is done by supplying the initial state of a component, simulating user behavior such as clicks and form entries, and finally checking if the UI and component state update correctly​. Very much like e2e tests are doing.</p><p>In Storybook, this workflow occurs in your browser, which makes it easier to debug failures since you’re running tests in the same environment you develop components.</p><h3>How it works</h3><p>You write a story to set up the component’s initial state, simulate user behavior using the <a href="https://storybook.js.org/docs/react/writing-stories/play-function">play function</a>, and then use the <a href="https://storybook.js.org/docs/react/writing-tests/test-runner">test runner</a> to confirm that the component renders correctly and that your interaction tests with the play function pass​. <a href="https://storybook.js.org/docs/react/writing-tests/test-runner">Storybook’s Test runner</a> is a standalone utility — powered by Jest and Playwright — that executes all of your interaction tests, and runs parallel to your Storybook.</p><h3>Setting Up Storybook Interaction Tests on Nx</h3><p>You can read our detailed guide on how to set up Storybook interaction tests on Nx, here: <a href="https://nx.dev/packages/storybook/documents/storybook-interaction-tests">https://nx.dev/packages/storybook/documents/storybook-interaction-tests</a>.</p><h3>Writing Interaction Tests in Storybook</h3><p>An interaction test is defined inside a play function connected to a story. The story simulates the user’s behavior once it loads in the UI and verifies the underlying logic​.</p><p>Under the hood, Storybook’s <a href="https://storybook.js.org/addons/@storybook/addon-interactions">@storybook/addon-interactions</a> mirrors <a href="https://testing-library.com/">Testing Library</a>’s user-events API. So, you can use the same queries and assertions that you would use for Testing Library, like we already do with our unit tests.</p><p>For complex flows, it can be worthwhile to group sets of related interactions using the step function. This allows you to provide a custom label that describes a set of interactions.</p><h3>Debugging and Reproducing Errors</h3><p>Storybook provides an interactive debugger that displays the step-by-step flow of your interactions, and provides UI controls to pause, resume, rewind, and step through each interaction​.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ZhrFxCwtYkO3gLaU" /><figcaption>Interaction test for the click of a button.</figcaption></figure><p>If an error occurs during a story’s play function, it’ll be shown in the interaction addon panel to help with debugging. And since Storybook is a web app, anyone with the URL can reproduce the error with the same detailed information without any additional environment configuration or tooling required​.</p><h3>Executing and Automating Tests</h3><p>Storybook only runs the interaction test when you’re viewing a story. Therefore, as a Storybook grows, it becomes unrealistic to review each change manually. The Storybook test-runner automates the process by running all tests for you. This can be executed via the command line or on CI environment​.</p><h3>What should I choose? Interaction tests or E2E tests?</h3><p>Setting up interaction tests with Nx and Storybook provides an extra layer of confidence in the functionality of your components. It ensures that they not only look right but also behave correctly in response to user interactions.</p><p>Storybook interaction tests provide a unique advantage over traditional e2e tests, especially when considering the development setup. With Storybook already in place, you essentially have a controlled environment set up for each of your components. This allows you to write interaction tests almost immediately, without the overhead of setting up and navigating through a full application environment, as is the case with e2e tests.</p><p>Moreover, since Storybook isolates each component, you can ensure that the tests are solely focused on individual component behavior rather than application-level concerns. This results in faster test execution, easier debugging, and more granular feedback during the development process. In essence, with Storybook’s interaction tests, you get many of the benefits of e2e tests but with a setup that’s quicker, more focused, and integrated right into your component development workflow.</p><h3>Screenshare</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FQvD3hJDa_1Q%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DQvD3hJDa_1Q&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/edc2a7e071c1903ce4041fac057c65ab/href">https://medium.com/media/edc2a7e071c1903ce4041fac057c65ab/href</a></iframe><h3>Useful Links</h3><ul><li><a href="https://storybook.js.org/docs/react/writing-tests/interaction-testing">https://storybook.js.org/docs/react/writing-tests/interaction-testing</a></li><li><a href="https://nx.dev/packages/storybook/documents/storybook-interaction-tests">https://nx.dev/packages/storybook/documents/storybook-interaction-tests</a></li></ul><h3>Learn more</h3><p>- 🧠 <a href="https://nx.dev/">Nx Docs</a><br>- 👩‍💻 <a href="https://github.com/nrwl/nx">Nx GitHub</a><br>- 💬 <a href="https://go.nrwl.io/join-slack">Nx Community Slack</a><br>- 📹 <a href="https://www.youtube.com/@nxdevtools">Nx Youtube Channel</a><br>- 🚀 <a href="https://nx.app/">Speed up your CI</a></p><p>Also, if you liked this, click the 👏and make sure to follow <a href="https://twitter.com/psybercity">Katerina</a> and <a href="https://twitter.com/nxdevtools">Nx</a> on Twitter for more!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=135fdaabc944" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nrwl/storybook-interaction-tests-in-nx-135fdaabc944">Storybook Interaction Tests in Nx</a> was originally published in <a href="https://medium.com/nrwl">Nx Devtools</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing Nx Ecosystem CI]]></title>
            <link>https://medium.com/nrwl/introducing-nx-ecosystem-ci-ad0526d37f83?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/ad0526d37f83</guid>
            <category><![CDATA[ecosystem-ci]]></category>
            <category><![CDATA[nx]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Tue, 20 Jun 2023 13:31:51 GMT</pubDate>
            <atom:updated>2023-06-20T14:51:20.028Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EffyLKcVe5gE_x3MT8PJUQ.jpeg" /></figure><p>The JavaScript ecosystem evolves at a rapid pace, frequently introducing new tools and packages. At Nx, we provide out-of-the-box integrations with the most popular among them so you don’t have to worry when stitching them together. That, however…yes you guessed it… can be a challenging task. There’s just one way to keep up: automation.</p><p>We already run a ton of automated testing on our repository to ensure we don’t break anything. But given Nx’s popularity and vast usage across open source and enterprise projects, we want to go a step further: introducing the <a href="https://github.com/nrwl/nx-ecosystem-ci">Nx Ecosystem CI</a>. Inspired by the work done by our friends on the <a href="https://vitejs.dev/">Vite</a> team, the <a href="https://github.com/nrwl/nx-ecosystem-ci">Nx Ecosystem CI</a> is designed to enhance the stability of Nx by testing pre-release versions with projects in the Nx ecosystem.</p><h4>Inspired by the Vite Ecosystem CI</h4><p>The <a href="https://github.com/vitejs/vite-ecosystem-ci">Vite Ecosystem CI</a> is an innovative tool that has significantly enhanced the use of <a href="https://vitejs.dev/">Vite</a>. It monitors the compatibility of Vite with various other packages and projects in the ecosystem by running tests against the latest changes in the Vite codebase and the projects it integrates with. This allows the Vite team to catch issues early and maintain a high level of stability, ensuring that developers using Vite can trust that new contributions to either Vite or their project will not result in breaking changes.</p><p>This robust testing system is essential because it gives users confidence in Vite’s reliability, encouraging more developers to adopt Vite. It’s a great example of proactive testing in a fast-paced ecosystem and an inspiration for other projects, including Nx. The concept of the Ecosystem CI introduces a framework-agnostic way of testing integrations of one tool with other tools in the ecosystem. It puts together a “syntax” with which tools can easily find the way to test their latest versions with one another.</p><h4>Nx Ecosystem CI</h4><p>The <a href="https://github.com/nrwl/nx-ecosystem-ci">Nx Ecosystem CI</a> is a fork of the <a href="https://github.com/vitejs/vite-ecosystem-ci">Vite Ecosystem CI</a> but is tailored specifically for the Nx ecosystem. It’s designed to ensure that Nx maintains its high standards of reliability and compatibility with all our users.</p><h4>How Nx Ecosystem CI Works</h4><p>The Nx Ecosystem CI works in the following way:</p><ol><li>It clones the provided repo which uses Nx</li><li>It installs the project’s dependencies</li><li>It runs a number of scripts specified by the project’s author (eg. test, build, e2e)</li><li>It migrates the repository to the next version of Nx (using nx migrate next)</li><li>It runs the scripts again</li><li>It reports the results of the runs to the <a href="https://join.slack.com/t/nrwlcommunity/shared_invite/zt-1wbp4do0g-3czhwijFnRzsilGI7eJuag">Nrwl Community Slack</a> in the #nx-ecosystem-ci channel.</li></ol><p>The main difference between the Nx Ecosystem CI and the Vite Ecosystem CI is that Nx Ecosystem CI uses the `next` version of Nx as published on npm, rather than cloning and building Nx locally, like Vite does in the Vite Ecosystem CI. This approach ensures that the tests run against the same code that developers are most likely to use in their projects. It also makes it easier for the script to migrate to that version.</p><p>At its core, the Nx Ecosystem CI is a set of command-line tools that run tests for a specific or all available suites. Each test suite corresponds to a specific configuration and consists of a set of commands executed in a given repository. The test suite checks for the correct execution of Nx commands, such as build, test, and e2e tests, ensuring that Nx functions as expected in different environments and projects.</p><h4>Adding a new test suite</h4><p>To add a new test suite for your project in the Nx Ecosystem CI, you would need to create a new file under the tests directory. The name of this file should reflect the suite it represents, for example, <a href="https://github.com/nrwl/nx-ecosystem-ci/blob/main/tests/nx-rspack.ts">nx-rspack.ts</a> .</p><p>The first step is to import the necessary modules and types from utils.ts and types.ts at the top of your file:</p><pre>import { runInRepo } from &#39;../utils&#39;<br>import { RunOptions } from &#39;../types&#39;</pre><p>RunOptions is a type that represents the options for running a test suite. It includes properties such as the repository to test, the branch to use, and the commands to run for building, testing, and performing e2e tests (all optional).</p><p>Next, you need to define the test function that accepts the RunOptions. Within this function, you’ll call the runInRepo function, passing in the options as well as any specific properties required for your suite:</p><p>Again, using the example of nx-rspack:</p><pre>export async function test(options: RunOptions) {<br>    await runInRepo({<br>        …options,<br>        repo: &#39;nrwl/nx-labs&#39;,<br>        branch: &#39;main&#39;,<br>        build: [&#39;build rspack&#39;],<br>        test: [&#39;test rspack&#39;],<br>        e2e: [&#39;e2e rspack-e2e&#39;],<br>    })<br>}</pre><p>In this example, the suite is set up to run on the ‘nrwl/nx-labs’ repository on the main branch. It will run build rspack, test rspack, and e2e rspack-e2e as its build, test, and e2e tests respectively. These commands will be invoked using the package manager used by your repository. So, in the nx-labs case, it will run yarn build rspack in the nrwl/nx-labs repo.</p><p>For this reason, adding a new test suite to the Nx Ecosystem CI also requires setting up appropriate scripts in your repository’s package.json file. These scripts provide the commands that will be invoked by your package manager to carry out the build, test, and e2e steps.</p><p>Here’s an example of how scripts might be configured in a package.json file for a repository using Nx:</p><pre>&quot;scripts&quot;: {<br>…<br>    &quot;build&quot;: &quot;nx build&quot;,<br>    &quot;test&quot;: &quot;nx test&quot;,<br>    &quot;e2e&quot;: &quot;nx e2e&quot;<br>…<br>},</pre><p>These scripts should be set up in such a way that they can be invoked directly by your package manager. For example, in a repository using pnpm, you could run the build script with the command pnpm run build.</p><p>When you create your test suite file, you’ll specify these script names in the build, test, and e2e properties of the options object passed to runInRepo.</p><pre>export async function test(options: RunOptions) {<br>    await runInRepo({<br>        …options,<br>        repo: &#39;nrwl/nx-labs&#39;,<br>        branch: &#39;main&#39;,<br>        build: [&#39;build&#39;],<br>        test: [&#39;test&#39;],<br>        e2e: [&#39;e2e&#39;],<br>        })<br>}</pre><p>With this setup, the Nx Ecosystem CI will run these scripts in your repository as part of its CI process, or just when you run pnpm test &lt;name-of-suite&gt; locally.</p><p>In addition to creating the test suite and setting up the package.json scripts, you will also need to add the name of the new suite to the workflow configuration files in the .github/workflows directory of the Nx Ecosystem CI repository. This suite name should match the filename of your test suite script.</p><p>There are two workflow files you’ll need to update:</p><ul><li>.github/workflows/ecosystem-ci-selected.yml</li><li>.github/workflows/ecosystem-ci.yml</li></ul><p>In .github/workflows/ecosystem-ci.yml you’ll find a strategy section with a matrix property. This matrix property specifies an array of suite names for the workflow to run. You’ll need to add your new suite name to this array.</p><p>Here’s what the strategy section might look like after adding a new suite named my-new-suite:</p><pre>strategy:<br>  matrix:<br>   suite:<br>     - ….<br>     - nx-remix<br>     - nx-rspack<br>     - …<br>     - my-new-suite # your new suite</pre><p>By adding your suite name to this file, you’re instructing the Nx Ecosystem CI to include your suite in its test runs.</p><p>In addition to the .github/workflows/ecosystem-ci.yml file, you also need to include your suite in the .github/workflows/ecosystem-ci-selected.yml file.</p><p>The ecosystem-ci-selected.yml workflow is designed to allow manual selection of a test suite to run. To add a suite to this workflow, you add it to the options array under workflow_dispatch &gt; inputs &gt; suite. Here’s what it might look like with a new suite named my-new-suite:</p><pre>on:<br>  workflow_dispatch:<br>    inputs:<br>      suite:<br>        description: &quot;testsuite to run&quot;<br>        required: true<br>        type: choice<br>        options:<br>          - ….<br>          - nx-remix<br>          - nx-rspack<br>          - …<br>          - my-new-suite # your new suite</pre><p>Adding your suite name to this file allows it to be manually selected for a test run via the GitHub Actions interface. This manual selection process provides additional flexibility and control over the testing process, allowing you to run individual suites as needed.</p><h4>Reporting the results</h4><p>The Nx Ecosystem CI is integrated with GitHub Actions, which helps with its automation process. The CI pipeline is scheduled to run three times a week (on Mondays, Wednesdays, and Fridays) and can also be triggered manually. The workflow uses a matrix strategy to run the suites in parallel. Each suite is given a big amount of memory, and the pipeline is configured with a long timeout, meaning that even if one suite encounters an error, the rest will continue to run. This ensures that we get comprehensive feedback on the health of all the test suites, regardless of individual failures. Once the test suites run, Github sends a message to the <a href="https://join.slack.com/t/nrwlcommunity/shared_invite/zt-1wbp4do0g-3czhwijFnRzsilGI7eJuag">Nrwl Community Slack</a> #nx-ecosystem-ci channel with the status of each suite, enabling the team and the community to view the results. Each result points to the Nx tag that was used, and also the job logs on GitHub.</p><p>Here is an example of a test run:</p><p><a href="https://github.com/nrwl/nx-ecosystem-ci/actions/runs/5144215568/jobs/9260227337">https://github.com/nrwl/nx-ecosystem-ci/actions/runs/5144215568/jobs/9260227337</a></p><h4>Benefits for the Nx Community</h4><p>The introduction of the Nx Ecosystem CI is a significant win for both the Nx team and the Nx developer community. For us, it enables us to catch issues early, often before they affect most end-users. By running tests against the `next` version of Nx, we can ensure that any changes we make are compatible with the various configurations that our users maintain.</p><p>For developers using Nx, the Nx Ecosystem CI offers reassurance that the tools they rely on are being actively tested and maintained. This provides confidence in the stability of Nx and its plugins.</p><h4>Ecosystem CI as part of the Open Source community</h4><p>We are not alone in recognizing the value of an Ecosystem CI approach. Other OSS projects including Nuxt, VueJs, VolarJs, and Rspack, have also adopted this strategy. You can explore their implementations here:</p><ul><li>Nuxt: <a href="https://github.com/nuxt/ecosystem-ci)">https://github.com/nuxt/ecosystem-ci</a></li><li>VueJs: <a href="https://github.com/vuejs/ecosystem-ci">https://github.com/vuejs/ecosystem-ci</a></li><li>VolarJs: <a href="https://github.com/volarjs/ecosystem-ci">https://github.com/volarjs/ecosystem-ci</a></li><li>Rspack: <a href="https://github.com/web-infra-dev/rspack-ecosystem-ci">https://github.com/web-infra-dev/rspack-ecosystem-ci</a></li><li>Storybook: <a href="https://storybook.js.org/blog/storybook-ecosystem-ci/">https://storybook.js.org/blog/storybook-ecosystem-ci/</a></li></ul><p>As we continue to improve and refine the Nx Ecosystem CI, we remain committed to the goal of making Nx a reliable and integral part of your development workflow. If you’re an open-source maintainer, you can create your own Ecosystem CI either from scratch (like Storybook) or by cloning the Vite Ecosystem CI. If your project uses Nx, you can easily add a new test suite for it.</p><h3>Learn more</h3><p>- 🧠 <a href="https://nx.dev">Nx Docs</a><br>- 👩‍💻 <a href="https://github.com/nrwl/nx">Nx GitHub</a><br>- 💬 <a href="https://go.nrwl.io/join-slack">Nx Community Slack</a><br>- 📹 <a href="https://www.youtube.com/@nxdevtools">Nx Youtube Channel</a><br>- 🚀 <a href="https://nx.app">Speed up your CI</a></p><p>Also, if you liked this, click the 👏and make sure to follow <a href="https://twitter.com/psybercity">Katerina</a> and <a href="https://twitter.com/nxdevtools">Nx</a> on Twitter for more!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ad0526d37f83" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nrwl/introducing-nx-ecosystem-ci-ad0526d37f83">Introducing Nx Ecosystem CI</a> was originally published in <a href="https://medium.com/nrwl">Nx Devtools</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A demo for the Sensor APIs on the web]]></title>
            <link>https://pakotinia.medium.com/a-demo-for-the-sensor-apis-on-the-web-300b9c31b27d?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/300b9c31b27d</guid>
            <category><![CDATA[generic-sensor-api]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[sensors]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Tue, 04 Oct 2022 17:43:54 GMT</pubDate>
            <atom:updated>2022-10-04T17:43:54.328Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tCYsSkvy_v4DFPK3dzH6Jw.png" /><figcaption><em>“woman using smartphone to control a video game on a laptop — painting by David Hockney” — DALL-E</em></figcaption></figure><h3>Turn your phone (which has access to sensors) into a remote control (a Wii Remote) for an application that’s running on your computer (which — usually — does not have motion sensors) — using the web</h3><h3>Intro — When and why</h3><p>I wanted to experiment with some <strong>web</strong> technologies and <strong>web</strong> APIs this summer (as if I was not stressed enough), so I decided to revisit (and rebuild for the better) an old idea of mine. The concept of turning my phone into a gamepad (or, more accurately, a <a href="https://en.wikipedia.org/wiki/Wii_Remote">Wii Remote</a> of sorts), and using it to control some graphics on a web application running on my computer. I know that this idea is not super innovative, and I’m sure it has been executed before, maybe even in a better way. But still, I wanted to play around with sensors, data streams, and graphics.</p><p>I had toyed with that idea back in 2015, when I first built a “move your phone and watch a cube follow that motion on the screen, on a different machine”. I explain how I did it in <a href="https://psybercity.wordpress.com/2015/08/19/remoteobserver_android-app/">this</a> (very old, please don’t judge too much) blog post of mine, accompanied by a video. I revisited the idea in 2018, where I demoed a version of this idea, in Vienna, on May 18th, 2018, at the WeAreDevelopers conference. This time, I had decided to use solely <strong>web technologies</strong> and APIs. Here’s the <a href="https://youtu.be/H6L4XT7alXs">YouTube link</a> of my talk!</p><p>Now, in 2022, I decided to revisit this idea. I rebuilt the applications from scratch, and added a number of features to make them more user friendly, and generally improved the whole thing. I’ve also added documentation and instructions on how to play the game, and how to build it, too. All you need is Chrome on your phone (for the “gamepad”), and another machine with a web browser (computer, phone, tablet, whatever — the larger the screen the better) to display the actual game to be controlled.</p><h3>Purpose and achievements</h3><p>The purpose of this game|experiment|demo is to showcase the usage of the phone sensors through the web platform. Then, to suggest some ways these sensor data can be used to create interactive experiences on the web. And, most importantly, to <strong><em>provide a visualization of the sensor readings</em></strong>!</p><p>Sensors are already used in a wide variety of applications. For native applications, of course, sensors are a very common feature, and most applications use some kind of sensor in some way (whether it’s a motion sensor or an environmental sensor, or a combination of sensor data). We don’t see that as often on web applications, however. I don’t have any numbers to back up my claim, it’s just from pure observation. I may be wrong. It does not really matter, to be honest. :P</p><h3>Sensor Data on the Web</h3><p>Sensor data is exposed to the Open Web Platform. There are a number of Sensor APIs for the web that access these data. In this example we are mostly accessing sensor data through the Generic Sensor API. I find it necessary here to include the abstract of the <a href="https://www.w3.org/TR/generic-sensor/">Generic Sensor API specification</a>:</p><blockquote><em>“This specification defines a framework for exposing sensor data to the Open Web Platform in a consistent way. It does so by defining a blueprint for writing specifications of concrete sensors along with an abstract Sensor interface that can be extended to accommodate different sensor types.”</em></blockquote><h3>Types of Sensors</h3><p>The following section copies parts of the Generic Sensor API specification, taken from here: <a href="https://www.w3.org/TR/generic-sensor/"><em>https://www.w3.org/TR/generic-sensor/</em></a><em>.</em> You will see me linking this page a lot in this article.</p><h4>High level — Low level</h4><p>There are what we call “low level” sensors and “high level” sensors. The low level sensors are sensors which “<a href="https://www.w3.org/TR/generic-sensor/#ref-for-low-level%E2%91%A8"><em>are characterized by their implementation</em></a>”. That means that they provide readings of an actual sensor chip, for example the Gyroscope. The high level sensors are <a href="https://www.w3.org/TR/generic-sensor/#high-level">“<em>named after their readings, regardless of the implementation</em>”</a>. For example, they can be the result of algorithms applied to low-level sensors, like, for instance, the pedometer.</p><h4>Available sensors</h4><ul><li>Environmental</li></ul><p>Sensors that measure physical properties of the environment they are in, and these are the Ambient Light Sensor, the Proximity Sensor and the Magnetometer</p><ul><li>Inertial</li></ul><p>Sensors based on inertia and relevant measuring concepts. Such sensors are the Accelerometer and the Gyroscope.</p><ul><li><a href="https://www.w3.org/TR/generic-sensor/#sensor-fusion">Fusion Sensors</a></li></ul><p>This group of sensors provide measurements that are ‘fused together’ by fusion algorithms. The fusion algorithms might require data from one or multiple sources.</p><h3>How the Generic Sensor API definition works</h3><p>The Generic Sensor API provides a generic sensor interface which each sensor extends. The interface accepts some inputs and returns some outputs. It also exposes methods to start and stop the sensor, as well as exposes event handlers for the sensors. It defines a lifecycle as well. You can read all the details <a href="https://www.w3.org/TR/generic-sensor/#the-sensor-interface">here</a>.</p><h3>Compatibility</h3><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Sensor_APIs#browser_compatibility">Here</a> you can see the Sensor interface browser availability.</p><p>In Chrome, for the sensors to work, you may have to enable some flags, to give access to the browser. These flags, on Google Chrome, are the following:</p><ul><li>chrome://flags/#enable-generic-sensor</li><li>chrome://flags/#enable-generic-sensor-extra-classes</li></ul><p><a href="https://www.chromium.org/developers/design-documents/generic-sensor/">This</a> is an interesting read that explains the implementation of the sensors in Chromium.</p><h4>Safeguards</h4><p>In any case, you can check the compatibility by calling the sensor. If you’re getting back readings, you are OK. If you are NOT getting back readings, then there may be a number of issues:</p><ol><li>Your device has the sensor but your browser does not support the sensor</li><li>Your browser supports the sensor but your device does not have the sensor</li><li>Your device has the sensor, your browser supports the sensor, but the user did not give permission</li></ol><p>The solutions to these issues are the following:</p><ol><li>Use try — catch when instantiating the sensor object</li><li>Check for the sensor existence in `window`</li><li>Use the Permissions API to ask for the user’s permission</li></ol><h3>Threats</h3><p>Of course, using device sensors, and gaining access to device sensors, has many security and privacy implications. The more information and data you have about a device, the easier that device becomes trackable, traceable, exploitable. Some common threats are listed <a href="https://www.w3.org/TR/generic-sensor/#security-and-privacy">here</a>. Briefly:</p><ul><li>Location Tracking</li><li>Keystroke Monitoring</li><li>Device Fingerprinting</li><li>User Identifying</li><li>Eavesdropping (yes, with the <a href="https://www.wired.co.uk/article/gyroscope-listening-hack">gyroscope</a>)</li></ul><h4>Safeguards</h4><p>Again, copying from the docs (<a href="https://www.w3.org/TR/generic-sensor/#mitigation-strategies">https://www.w3.org/TR/generic-sensor/#mitigation-strategies</a>):</p><ul><li>Use https</li><li>Ask for user permission using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API">Permissions API</a></li><li>The sensors only work on visible and focused elements</li><li>Sampling frequency and accuracy control</li><li>Always inform the user of a sensor API usage</li></ul><h3>Why is this important? Why do I need it?</h3><p>This is the million dollar question. I find the use of sensors on the web, apart from exciting, extremely helpful. It opens up lots of possibilities, and brings the web experience even closer to the native experience. Sensors are tied to our mobile phones, and the way we think about our mobile apps. From device orientation, to pedometers, to activity tracking, geolocation, brightness adjustment, and the list goes on. All these features are available to the web, and the way we build our apps and our PWAs can be even more enhanced when we provide the user with the full native experience that the sensor readings offer.</p><h3>Possible applications</h3><p>This is just some brainstorming, I cannot wait to hear your ideas:</p><ul><li>Adjust colors &amp; theme according to light</li><li>Create games</li><li>Native-like interactions and events</li><li>Experiment with potential applications in navigation</li><li>Build your own fusion sensors</li></ul><h3>The game I created</h3><p>I created a simple game, that takes the readings of a number of different sensors from a mobile device, and sends these data (through a websocket server) to an application running on another machine (preferably a computer of a device with a larger screen), so that it can control some animations.</p><p>This is definitely not an everyday usage example, but it certainly visualizes the readings these sensors expose to the web platform. So, though playing this game, the user can see in a visual way the data they can access if they start using the Generic Sensor API on the web.</p><h4>The game parts</h4><p>The game consists of two parts. The “gamepad” or the “Wii Remote”, and the “Dashboard of Activities” or “Playground”. You can access the application here:</p><p><a href="https://ws-pakotinia.web.app/">https://ws-pakotinia.web.app/</a></p><p>Or here:</p><p><a href="https://ws-pakotinia.firebaseapp.com/">https://ws-pakotinia.firebaseapp.com/</a></p><p>If you are on your phone, click the “Gamepad” button, if you’re on your computer, click the “Playground” button.</p><p>Please, read <a href="https://mandarini.github.io/sensors-demo/#how-to-play">the instructions</a> before playing. They will clear up any questions you may have.</p><h4>Gamepad</h4><p>The “Gamepad” is your mobile device. It works best if you use Google Chrome, and you enable the flags, as instructed in the intro screen. Once you “log in” the game, you can start moving your phone, to see the visualization of the movements on the dashboard. Read <a href="https://mandarini.github.io/sensors-demo/#on-your-phone">the instructions</a> if something is unclear.</p><h4>Dashboard of activities</h4><p>The “Dashboard of Activities” or the “Playground” is a collection of 6 different games you can play. Read <a href="https://mandarini.github.io/sensors-demo/#on-your-computer">the instructions</a> carefully, please.</p><h3>Challenges</h3><h4>Types</h4><p>The main challenge I faced while building this project, was that I wanted to use TypeScript, so I needed all the types for the sensors I would be using. <a href="https://github.com/kenchris">Kenneth</a> has created the <a href="https://www.npmjs.com/package/@types/w3c-generic-sensor">https://@types/w3c-generic-sensor</a> package. This lacked the types for the AmbientLightSensor, so <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/pull/61541">I added them</a> :). For some reason, I am so proud of that PR. I’ve written more complicated code in my life, I’m sure, but that specific one, just the thought that it’s used by so many people, puts a grin on my face.</p><h4>Polyfills</h4><p>Of course, <a href="https://github.com/kenchris">Kenneth</a> has also created the <a href="https://www.npmjs.com/package/motion-sensors-polyfill">motion-sensors-polyfill</a>, as well. So that was a life saver, too.</p><h4>Limitations</h4><p>Of course, there are limitations, both to building this game, and to using these sensors, and to integrating sensors to your web application. I’m listing just a few that I thought of (or that I ran into):</p><ul><li>not all devices have all sensors</li><li>not all devices have the same frequency in obtaining data</li><li>not all devices sensors have the same sensitivity</li><li>not all browsers use the same coordinate system</li><li>even if a device has a sensor, the browser might not support reading it (eg. light)!</li></ul><h4>Accessibility</h4><p>Make sure, if you are going to integrate sensors to your web applications, that you provide alternative ways of interactions, and consider the accessibility implications and limitations.</p><h3>Where can I find your game and play it?</h3><p>Here is the game:</p><p><a href="https://ws-pakotinia.web.app/">https://ws-pakotinia.web.app/</a></p><p>Or here:</p><p><a href="https://ws-pakotinia.firebaseapp.com/">https://ws-pakotinia.firebaseapp.com/</a></p><p>And here are the docs/instructions: <a href="https://mandarini.github.io/sensors-demo/">https://mandarini.github.io/sensors-demo/</a></p><p>Here is the repository for the code:</p><p><a href="https://github.com/mandarini/sensors-demo">https://github.com/mandarini/sensors-demo</a></p><h3>Further reading and references</h3><p>This is the most important section of this article, since this article does not go deep into explaining sensors. It just explains what I did, and it points you to the detailed explanation of how I created this demo.</p><p>So, here go some links for you:</p><ul><li>W3c Generic Sensor API: <a href="https://www.w3.org/TR/generic-sensor/">https://www.w3.org/TR/generic-sensor/</a></li><li>Sensors for the web: <a href="https://web.dev/generic-sensor/">https://web.dev/generic-sensor/</a></li><li>Intel’s demo for the Generic Sensor API: <a href="https://intel.github.io/generic-sensor-demos/">https://intel.github.io/generic-sensor-demos/</a></li><li>My Demo: <a href="https://ws-pakotinia.web.app/">https://ws-pakotinia.web.app/</a></li><li>My Documentation / Demo explanation: <a href="https://mandarini.github.io/sensors-demo/">https://mandarini.github.io/sensors-demo/</a></li></ul><h3>Now what?</h3><p>I’d love to speak more about this, so, yes, reach out to me!</p><p>I also want to hear your feedback, and your suggestions for more implementations of the Sensors in our web applications!</p><p><em>If I forgot to give credit to anyone, please let me know and I’ll update this with the missing credit.</em></p><p>And, follow me on <a href="https://twitter.com/psybercity">twitter</a>, and visit my website: <a href="https://psyber.city/%F0%9F%90%88">https://psyber.city</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=300b9c31b27d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tainted Moments — or “Hello Vecna my old friend”]]></title>
            <link>https://medium.com/fileaspub/tainted-moments-or-hello-vecna-my-old-friend-cc5336d274d2?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/cc5336d274d2</guid>
            <category><![CDATA[stranger-things]]></category>
            <category><![CDATA[depression]]></category>
            <category><![CDATA[mental-health-awareness]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Thu, 04 Aug 2022 19:19:52 GMT</pubDate>
            <atom:updated>2022-08-04T19:19:52.234Z</atom:updated>
            <content:encoded><![CDATA[<h3>Tainted Moments — or “Hello Vecna my old friend”</h3><p><em>tl;dr: Katerina speaks about her mental health, making analogies with Stranger Things. The reason she shares this is because maybe more people are having the same experiences, and it’s nice to know you’re not alone. Also, it’s good to know that people who are functional and happy and active may still suffer sometimes.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Nj9NpfQGyY8E0AO4gfooJg.jpeg" /><figcaption>“night, a girl and depression as a monster on a beach after a party — digital art” — DALL-E</figcaption></figure><p>A few weeks ago I was at the after-party of a wedding. The wedding was of an old friend of mine, and the party was very enjoyable. It was held at a beautiful beach on a beautiful Greek island. There was good food, music, dancing, friends. A sort of ideal setting. I was having a lot of fun. It was late, already, and some friends were sitting on sunbeds on the beach. I went to join them, I sat on the edge of a sunbed, and sort of stopped. Negative thoughts started filling my mind, guilt, regret, for anything and for everything. It was as if the lights faded and the music stopped or was muffled, my friends disappeared and I was suddenly alone in this bad bad place. It must’ve shown on my face, because a friend who was sitting across from me shouted:</p><p>“Hey, Katerina, get out of the Upside Down”.</p><p>I jerked, lifted my stare, looked into her eyes, and smiled.</p><p>“That’s where I was”.</p><p><em>(thank you </em><a href="https://labs.openai.com/"><em>DALL-E</em></a><em> for the — perfect really — imagery)</em></p><p>I think I speak more about my mental health as I grow older. I don’t know when I first met depression, but it was before I got PTSD. So it must not be PTSD-induced, my depression. Not strictly, at least. It started off as a panic attack. It must’ve been 2007. I lost control of my mind and was certain I was dying. It went away, then it visited me again in 2008, and 2010. The panic attack.</p><p>I think I started seeing my therapist, my first therapist, in 2009. I think it was after I told my mother about the panic attack. Or my sadness. I’m not sure which of the two.</p><p>In-between, there were moments of deep sadness and guilt, but I never paid too much attention to these. It was just the way it was. But it mainly expressed itself as a panic attack. My therapist tells me it’s the lack of a sense of security with my Self, which comes from my deep attachment to my parents, which I blame on my only-child-ness. <strong>I</strong> blame it on my inability to accept me as who I am. As <strong>not the best person in the world</strong>.</p><h3>Some negative memories Vecna can grab onto</h3><p>I think when I realized I’m not safe, that’s when everything started spilling out. A negative memory.</p><p>In 2006 I was chased by a young man, who was half naked, stroking his erection. I ran to escape the rape, and was hit by a car. Lightly, I just got a bruise. I ran into a shop to ask for help, and the shop-owner asked me if I’m Greek.</p><p>In 2008 it must have been, I was assaulted by a group of young men, who touched me on places that should not be touched and shouted vulgarities at me, and spat at me (literally). It was in the middle of a busy street, I was shouting for help, but nobody so much as turned their heads.</p><p>In 2011 I was strangled almost to death outside my house by a petty thief. I passed out due to lack of oxygen, and that’s when he released my neck. He must’ve thought I had died. Before dying (I call it dying because that was almost what it was) I had a vision of my mother, happily waving at me from the living-room window. The most beautiful, happy and serene face I have seen in my life. The serenity engulfed me, until I stopped being for a few moments.</p><p>Naturally, PTSD enhanced the depression. The first few months I could not walk alone. It was in 2013 that I stopped being 100% functional due to the panic attacks. I had panic attacks almost daily, and I had to go to the hospital, so that I felt I was in a safe place, where if I stopped breathing they would do something. That was the way the panic would always express itself after the 2011 event. It was the fear of not being able to breathe.</p><p>Cognitive Behavioral Therapy sort of worked, at least it made me sort of functional, to be able to make it through my Master’s and the essential social situations which I felt compelled I should attend to. But the monster was inside me. I was having a daily fight with my body. My Self. I felt my body like a prison, like this prison that’s keeping me from being carefree and happy.</p><h3>Non-functionality</h3><p>In 2016 was when I stopped being functional. I could not get out of the house. I could not move, I could not walk, I could not let myself feel tired. That’s when I also changed therapists. My first session with my new therapist, I barely made it there. My mother walked me. When I entered his office I started crying. He asked me what I wanted. I told him I wanted to see light at the end of the tunnel.</p><p>He helped me see light. I’m functional now. And I’m happy more than half the time. And I know to hide it when I’m not. That’s what’s killing me, right? Hiding my struggle.</p><p>What I tell my therapist, and what I tell my friends, is that depression is there. It never really leaves you. But you sort of learn to manage it. You sort of learn its tricks and you handle it in a way to not let it completely destroy your life. You live with it, and you know how to battle it when it’s getting stronger. It’s a part of what makes me, me.</p><h3>The social aspect</h3><p>I was watching Stranger Things the other day, the final season. In that season they sort of make it crystal clear that the monster is depression (guilt, PTSD, remorse). And I like the imagery of a whole group of people — a whole support group — fighting it. Maybe that’s the only way it can really go away. I was not lucky to have many people on my side. My parents and some friends were on my side, but a bit oblivious, or uneducated in this matter. I was surprised later to understand that my parents had given me the two main weapons against depression that I own. Writing, and walking. Unfortunately, other friends were not really supportive back then. When I confront them about it now, they tell me they did know what I was going through. I was not really open about it. <strong>I don’t blame them</strong>. You could see I had moments where I was not well, but all other moments I was playing normal. In reality, I was not myself.</p><p>I had a number of coping mechanisms, which made me act not-normal, and made me act in a peculiar way. I was making sure not to get tired. Not to drink alcohol or any other substance that may make me feel dizzy or reduce my ability to be in control. I was making sure not to run or get an elevated heart-rate so as not to mistake it with anxiety. I was making sure not to be very far away from home (a safe place) so that I could easily retreat there, should I need to have a full-blown panic attack and nobody noticing. And of course I should be able to retreat alone. So that nobody would understand I am unwell (or <strong>weak</strong>).</p><p>The problem was that part of my social circle was a little mean about it all. The thing that still puzzles me is that they could tell something was wrong, so why be mean to someone who looks unwell. I think they did not know how to handle it. So some of them gave me hell about not drinking, not staying out late, not being able to run or get tired. Hell. Again, I don’t blame them, they didn’t know, I was hiding my weakness. One of the worst things that someone had told me was “I cannot hang out with you when you are not well”. I tried to act pleasant, because I didn’t want to impose and force my “unpleasantness” onto anyone.</p><h3>The lone wolf</h3><p>So, as always, I was alone. And sometimes, being this lone wolf, this lone soldier, made me feel powerful. My partner never gave me hell about any of this shit, so that was nice. Then my friends started getting depressed and having panic attacks, and we could share bad experiences, finally. The thing is, in Stranger Things, there’s a whole group of people collectively fighting a collective depression. I wish that were the case. But it’s not very realistic. Since, even if you have a support group, still it’s you against yourself, in the end. It’s nice to have a “Mike” next to you, who’s usually oblivious of what’s going on deep inside, but they still want to support you, regardless. By just sort of being “there”. And nothing more. But even if you don’t have that “Mike”, there’s still a way out of the Upside Down. It just takes longer, maybe. I don’t know if it would have taken me less time with the right support group. Maybe it would not. Anyway.</p><h3>Tainted moments</h3><p>Moments can be tainted. By a sudden grip of a bad feeling, a sudden recollection of a bad memory, an unexpected trigger of the PTSD. Imagine a perfect moment. Laying in bed at night, next to my partner, my cats kneading my belly. Sitting on the couch, after a nice, tasty lunch, it’s summer, 40 degrees, Sunday, I have nothing to do, and I’m just relaxing. These moments, these moments in these safe places, can be darkened. The Stranger Things imagery conveys that feeling completely. Imagine Max in that scene in the Snow Ball. Everything was pretty and fun. And suddenly, she starts seeing these particles, sounds are distorted, and everything darkens. That’s what happens. That’s exactly what happens.</p><p>When I first started experiencing panic attacks, my greatest fear was the unpredictability of when they were going to hit. I don’t know if I would call them panic attacks now, even though they were panic attacks. I am trying to think of a better term, because it was panic indeed, but the trigger of the panic was not anxiety, but it was the deeply rooted depression. And the feeling of desperation that comes with depression. The feeling that all’s lost and you cannot escape. You cannot escape your skin, you cannot escape yourself, you cannot escape your body, you are <strong>infected</strong>, you are <strong>tainted</strong>. It’s going to get you. It’s there. It’s just waiting for the right moment, the moment you’re most vulnerable. Or maybe the moment you don’t believe it’s going to hit you, the moment you least expect it.</p><h3>Fear as the master of all</h3><p>And then <strong>fear becomes the master of all moments</strong>. Because all beautiful moments are susceptible to being tainted and destroyed, and you are susceptible to being destroyed at any moment. And during those beautiful moments is when you fear the most. Because you fear that this is when it will hurt the most for it to get you. This is when the damage will be the maximum.</p><p>And you start, slowly, to not be able to distinguish between excitement and happiness, and fear, and anxiety and pain, and panic. Any intense feeling can be all, so it’s panic, so it’s suffocation, so it’s fear, so it’s depression. Good news and bad news have the same effect on the body, the excitement fires similar neurons with anxiety. And sometimes you cannot tell which is which, so you give in to anxiety. And you’re alone.</p><p>That feeling of being alone in all this, is the most crippling feeling of all. You cannot really convey this to anyone. And if you convey this to anyone, then you feel weak. And you burn bridges or you pretend to be functional and smiling, to just cut the people out. Because you’re alone. Others don’t understand. It’s you who’s tainted by this. It’s you who’s infected. It’s got <strong>you</strong> (the Mind Flayer). Others don’t have these bad experiences that depression feeds upon. Others don’t have a panic attack to remember and fear. Or someone killing them. Or someone chasing them. You’re all alone. Because it’s you versus your mind after all. So, you feel even more isolated.</p><h3>Therapy and loving my Self</h3><p>At this point you think “why don’t you get therapy”. I’ve been in therapy for 13 years. Therapy is what helped me make it through, and therapy is what has helped me have a functional life. Sometimes I think that maybe I should have tried some medication, and maybe a therapist should have suggested some medication. For some reason nobody suggested medication, and for many years I was not even 100% sincere with my therapists about how bad the situation was. I was too proud. I was taught to be a “soldier”. And that’s the worst fallacy that will delay the ability to cope. The determination to remain proud and strong. And not yield. But you have to yield, you have to admit how hard it is, you have to open up.</p><p>I started being more able to cope, and started seeing light, after 7 years of therapy, and after changing therapists. And the reason why that happened was that I hit the bottom. I could not move, I could not leave my bed, I could not leave my parents house, without thinking I will die. My life was non-existent and I was just surviving. And that’s when I really started gradually admitting all of my weaknesses to my new therapist. And that’s when I started to really believe<strong> I can fight Vecna</strong>.</p><p>It was not until 2 years ago that I started admitting my worst deeds. To myself first, to my therapist afterwards. Describing all the bad things I have done in my life, and all the bad things I continue to do. Saying how I used to feel like the worst person in the world. And the most cursed, too (why me? etc). And after I started accepting all the things that make me, me (all the bad, and the good, and the not so bad, and the not so good), after that, then I started being able to share my shortcomings and my weaknesses with friends, too. And standing up for my mental health issues, and my disability to cope sometimes. And laugh about it in the end. Because <strong>I do make me laugh</strong> sometimes, with all the obstacles that I am setting up against me. All the little things that Vecna can grab onto and get hold of me.</p><h3>Ariana and the savage</h3><p>Last week my PTSD got retriggered really intensely. An event took place that was almost identical to my strangulation, one block away from my event. It was a bad week, with nightmares. I went to the police to check if it was the same person, because they arrested someone, it was not him. Then I heard that a past stalker of mine was asking about me. Another batch of nightmares. These people, my almost-killer, my stalkers, they almost make my life unlivable sometimes. So naturally I wish their lives to be unlivable. Or non-existent. :)</p><p>As Ariana says <em>“been through some bad shit, I should be a sad bitch — who would have thought it’d turn me to a savage?”</em>. I’ve been unlucky, but what the hell. I don’t want to look like I’m using my mental health as a means of getting away with anything. On the contrary, it sort of made me into a badass bitch. I say bring it on, and I know that since I’ve coped with all these things, there’s really nothing I cannot face. Really, I’ve become the lone soldier I was taught to be when I was little.</p><blockquote>it sort of made me into a badass bitch</blockquote><h3>Vecna as my furry little friend</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jokxIloxbrlV-5rRisgBHA.png" /><figcaption>“a girl and depression as a monster on a beach after a party” — DALL-E</figcaption></figure><p>Vecna is there, I feel him. And I know that he will taint more of my moments. I will visit the Upside Down again. I will see the little particles, the sound will be distorted, the light will fade, again. But I am not afraid of it any more. It’s part of me, part of my life, and part of the world around me, and everyone’s world. I know that I can escape it, because I have the memory of me escaping it in the past. And I have the tools I need to escape it. But the most comforting thought is that this, Vecna, the Upside Down, the Shadow Monster, the Mind Flayer, are all part of me. And I embrace them as such.</p><p>So, yeah, I guess, <strong>bring it on</strong>! :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc5336d274d2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fileaspub/tainted-moments-or-hello-vecna-my-old-friend-cc5336d274d2">Tainted Moments — or “Hello Vecna my old friend”</a> was originally published in <a href="https://medium.com/fileaspub">fileas.</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Speed up Storybook with Vite and SWC — with the help of Nx]]></title>
            <link>https://medium.com/nrwl/speed-up-storybook-with-vite-and-swc-with-the-help-of-nx-b1e4c488e0fd?source=rss-d83b4a2663b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/b1e4c488e0fd</guid>
            <category><![CDATA[vitejs]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[storybook]]></category>
            <category><![CDATA[swc]]></category>
            <dc:creator><![CDATA[Katerina Skroumpelou]]></dc:creator>
            <pubDate>Mon, 18 Jul 2022 15:15:09 GMT</pubDate>
            <atom:updated>2023-01-05T12:53:04.203Z</atom:updated>
            <content:encoded><![CDATA[<h3>Speed up Storybook with Vite and SWC — with the help of Nx</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*suutOd685sJtw3uKjMbwhg.png" /></figure><h3>Vite for Storybook in your Nx workspace</h3><p><a href="https://vitejs.dev">Vite</a> is a build tool that ensures faster load times, faster updates, smaller server start times, and more efficient bundling. As described in the <a href="https://vitejs.dev/guide/why.html">docs</a>, it consists of a dev server using ES modules and bundling using Rollup. Starting with Storybook 6.3, <a href="https://storybook.js.org/blog/storybook-for-vite/">Storybook announced</a> the community-led project for Vite support on Storybook, <a href="https://github.com/storybookjs/builder-vite">Storybook Vite builder</a>. You can also take a look at this <a href="https://storybook.js.org/blog/storybook-performance-from-webpack-to-vite/">comparison of Storybook performance</a> when using Webpack vs Vite.</p><p>You can take advantage of Vite’s speed in Storybook right now. We are going to be following the installation guide of the <a href="https://github.com/storybookjs/builder-vite#usage">Storybook Vite builder</a>, and applying the steps for an Nx Storybook setup.</p><h3>How to use Vite for existing Storybook configurations</h3><p><a href="https://github.com/mandarini/nx-storybook-vite-swc/commit/92f1fb91715c3a89cb2f66cb00a9b297aa7ef2ae">Here is a repo with the changes</a>.</p><p>You can easily add Storybook configuration to your project (application or library) using the <a href="https://nx.dev/packages/storybook/generators/configuration">Storybook configuration generator</a> for Nx. This will generate the Storybook configuration files for you, and in the project’s .storybook/main.js it will set the builder to webpack5. If your project is using Angular or React, our generator will also generate Stories for you (*.stories.*) based on your components and your components’ inputs/props.</p><p>After you have generated your Storybook configuration for your project, you can follow the steps described above to switch to Vite.</p><p>If you already have a project in your Nx workspace that has Storybook configured, here are the steps you need to follow to switch to Vite for Storybook:</p><ol><li>First, you need to install the required dependencies:</li></ol><pre>yarn add -D vite @storybook/builder-vite</pre><p>or</p><pre>npm i -D vite @storybook/builder-vite</pre><p>2. In your project’s .storybook/main.js file (eg.apps/my-app/.storybook/main.js) change the builder value to @storybook/builder-vite .</p><p>You also need to set the Vite configuration, to correctly resolve the paths of your workspace.</p><p>For that you need to use the viteFinal function, as described <a href="https://storybook.js.org/docs/react/builders/vite#configuration">here</a>, and add the vite-tsconfig-paths plugin in the plugins array of the Vite configuration.</p><p>The result will look like this:</p><pre>// apps/my-app/.storybook/main.js</pre><pre>const rootMain = require(‘../../../.storybook/main’);<br><strong>const { mergeConfig } = require(&#39;vite&#39;);<br>const viteTsConfigPaths = require(&#39;vite-tsconfig-paths&#39;).default;</strong></pre><pre>module.exports = {</pre><pre>  …rootMain,</pre><pre>  core: { …rootMain.core,<strong> builder: ‘@storybook/builder-vite’</strong> },</pre><pre>  stories: [</pre><pre>    …rootMain.stories,</pre><pre>    ‘../src/app/**/*.stories.mdx’,</pre><pre>    ‘../src/app/**/*.stories.@(js|jsx|ts|tsx)’,</pre><pre>  ],</pre><pre>  addons: […rootMain.addons, ‘@nrwl/react/plugins/storybook’],</pre><pre><strong>  async viteFinal(config, { configType }) {    <br>      return mergeConfig(config, {      <br>          plugins: [            <br>              viteTsConfigPaths({          <br>                  root: &#39;../../../&#39;,<br>              }),      <br>          ],    <br>      });  <br>  },</strong></pre><pre>};</pre><p>3. Then, just run Storybook for your project and you will see how it’s now using Vite instead!</p><pre>nx storybook my-app</pre><p>4. Observe the console for the nice Vite logs</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_JBUeTagGzg32dzd" /></figure><h3>SWC for Storybook in your Nx workspace</h3><p>SWC (speedy web compiler) is a compiler written in Rust that can be used for both compilation and bundling.</p><p><a href="https://nx.dev/getting-started/nx-and-typescript#use-swc-as-the-compiler">Nx supports SWC</a>. Also, the <a href="https://nextjs.org/docs/advanced-features/compiler">Next.js compiler</a>, starting with <a href="https://nextjs.org/blog/next-12">version 12</a>, uses SWC. So, if you’re using SWC in your project, or if you have a Next.js application, then you can use SWC for your Storybook as well.</p><h3>How to use SWC for existing Storybook configurations</h3><p><a href="https://github.com/mandarini/nx-storybook-vite-swc/commit/cc8adc5f2f20ef2ab120902f77906856b37a8cae">Here is a repo with the changes</a>.</p><p>Storybook supports SWC using the <a href="https://storybook.js.org/addons/storybook-addon-swc">storybook-addon-swc</a> addon. If you want to use SWC with Storybook, please read the <a href="https://storybook.js.org/addons/storybook-addon-swc">documentation</a>.</p><p>The steps you need to follow to switch to SWC are the following:</p><ol><li>Install the addon:</li></ol><pre>yarn add -D storybook-addon-swc</pre><p>or</p><pre>npm i -D storybook-addon-swc</pre><p>2. In your project level .storybook/main.js, in the addons array, add the storybook-addon-swc addon.</p><h3>Next.js 12, Storybook, Nx and SWC</h3><p><a href="https://github.com/mandarini/nx-storybook-vite-swc/commit/59174c2c05018485898ab0b959c1387372a7480d">Here is a repo with the changes</a>.</p><p>If you’re using Next.js, you should also add the <a href="https://storybook.js.org/addons/storybook-addon-next">storybook-addon-next</a>. You can read more in the <a href="https://storybook.js.org/addons/storybook-addon-next">documentation</a>.</p><p>Here are the steps you can follow to add this addon:</p><ol><li>Install the addon:</li></ol><pre>yarn add -D storybook-addon-next</pre><p>or</p><pre>npm i -D storybook-addon-next</pre><p>2. In your project level .storybook/main.js, in the addons array, add the storybook-addon-next addon like this:</p><pre>const rootMain = require(‘../../../.storybook/main’);<br>const path = require(‘path’);</pre><pre>module.exports = {</pre><pre>  …</pre><pre>  addons: [</pre><pre>    …</pre><pre>    ‘storybook-addon-swc’,</pre><pre><strong>    {</strong></pre><pre><strong>      name: ‘storybook-addon-next’,</strong></pre><pre><strong>      options: {</strong></pre><pre><strong>        nextConfigPath: path.resolve(__dirname, ‘../next.config.js’),</strong></pre><pre><strong>      },</strong></pre><pre><strong>    },</strong></pre><pre>  ],</pre><pre>  …</pre><pre>};</pre><h3>SWC for new Storybook configurations</h3><p>Nx will generate Storybook configured with SWC <strong>if</strong> you use SWC as a compiler to your build target in your project.json, in your project’s configuration, or <strong>if</strong> you use the Next.js @nrwl/next:buildbuilder. If you’re generating Storybook configuration for a Next.js application (one that uses @nrwl/next:build), then Nx will make sure to add the storybook-addon-swc and the storybook-addon-next addons to your project’s .storybook/main.js or .storybook/main.ts.</p><h3>Conclusion</h3><p>Nx offers full support for Vite and SWC with Storybook, and the generators will make sure the transition is seamless and easy. So, go ahead and speed up your Storybook!</p><p>We would love for you to try it out and let us know what you think!</p><p>Also, make sure you don’t miss anything by</p><ul><li>Following us <a href="https://twitter.com/NxDevTools">on Twitter</a>, and</li><li>Subscribe to the <a href="https://youtube.com/nrwl_io?sub_confirmation=1">YouTube Channel</a> for more information on <a href="https://angular.io/">Angular</a>, <a href="https://reactjs.org/">React</a>, <a href="https://nx.dev/">Nx</a>, and more!</li><li>Subscribing to <a href="https://go.nrwl.io/nx-newsletter">our newsletter</a>!</li></ul><p>As always, if you are looking for enterprise consulting, training and support, you can find out more about how we work with our clients <a href="https://nrwl.io/services">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b1e4c488e0fd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nrwl/speed-up-storybook-with-vite-and-swc-with-the-help-of-nx-b1e4c488e0fd">Speed up Storybook with Vite and SWC — with the help of Nx</a> was originally published in <a href="https://medium.com/nrwl">Nx Devtools</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>