<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged extension</title>
<link>https://coderlegion.com/tag/extension</link>
<description>Powered by Question2Answer</description>
<item>
<title>🚀 Managing MCP Servers in VS Code? Explore SEOSiri MCP Suite Manager!</title>
<link>https://coderlegion.com/31223/managing-mcp-servers-in-vs-code-explore-seosiri-mcp-suite-manager</link>
<description>&lt;p&gt;As AI-powered development evolves, managing Model Context Protocol (MCP) integrations is becoming an important part of building connected developer workflows. A well-organized MCP setup can help developers connect AI tools with external services, tools, and data sources.&lt;/p&gt;
&lt;p&gt;🛠️ Meet SEOSiri MCP Suite Manager&lt;/p&gt;
&lt;p&gt;Explore our VS Code extension for MCP server management and developer workflows.&lt;/p&gt;
&lt;p&gt;💡 Why explore MCP management?&lt;br&gt;
🔹 Developer Productivity: Organize your MCP-related development workflow within VS Code.&lt;br&gt;
🔹 AI Tool Integration: Explore MCP server configurations for compatible AI clients and development environments.&lt;br&gt;
🔹 Workflow Optimization: Simplify the process of working with MCP server setups.&lt;br&gt;
🔹 Developer Ecosystem: Build toward more connected AI-assisted development workflows.&lt;br&gt;
🔹 Project Opportunities: Get in touch with SEOSiri for development, optimization, and digital marketing support.&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13321562692722168591&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;🔗 Visit the SEOSiri Visual Studio Marketplace:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/publishers/seosiri&quot;&gt;https://marketplace.visualstudio.com/publishers/seosiri&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📦 Get MCP Suite Manager:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=SEOSiri.seosiri-vscode-mcp-manager&quot;&gt;https://marketplace.visualstudio.com/items?itemName=SEOSiri.seosiri-vscode-mcp-manager&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;🤝 Need help developing, optimizing, or marketing your developer product? DM SEOSiri to discuss your project and potential collaboration.&lt;/p&gt;
&lt;p&gt;The future of AI development depends not only on capable models, but also on the tools and integrations that connect them.&lt;/p&gt;
&lt;p&gt;Build. Integrate. Optimize. Make it discoverable.&lt;/p&gt;
&lt;h2 id=&quot;mcp-modelcontextprotocol-vscode-aidevelopment-developertools-seosiri&quot;&gt;MCP #ModelContextProtocol #VSCode #AIDevelopment #DeveloperTools #SEOSiri&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31223/managing-mcp-servers-in-vs-code-explore-seosiri-mcp-suite-manager</guid>
<pubDate>Thu, 15 Oct 2026 15:41:32 +0000</pubDate>
</item>
<item>
<title>Vue.js Web Browser Extension Template</title>
<link>https://coderlegion.com/29753/vue-js-web-browser-extension-template</link>
<description>&lt;p&gt;Starting out with browser extensions can often be a pain in the back.&lt;/p&gt;
&lt;p&gt;A while back, I was looking for a template I could use to build Epubespiar. Most of the ones I tried had the same problem: I couldn’t get them started and running. Since this was my first time building an extension, that was really frustrating.&lt;/p&gt;
&lt;p&gt;The instructions would say something like: &lt;em&gt;clone&lt;/em&gt; the project, &lt;em&gt;pnpm install&lt;/em&gt;, then &lt;em&gt;pnpm run dev&lt;/em&gt;. After that, Chrome would suddenly open and... voilà. A blank tab, staring at you as if asking, &quot;what are you doing, bruv?&quot; And if you didn't have Chrome installed and wanted to check the template UI in Firefox, hell broke loose. :P&lt;/p&gt;
&lt;p&gt;So you'd end up back in a 500+ line codebase, trying to figure out what was actually breaking and that can be demoralizing, yeah. Imagine signing up to a social platform and, on first impression... not being able to create posts or publish content the way you would on other apps. Sucks, right?&lt;/p&gt;
&lt;p&gt;After a few trials, I decided to build Epubespiar from the ground up with &lt;strong&gt;Vue.js&lt;/strong&gt;. Since then, life has been good.&lt;/p&gt;
&lt;p&gt;Recently, that experience stuck with me, so I built a browser extension template to help developers get their extensions up much quicker.&lt;/p&gt;
&lt;p&gt;All you need is the repo in your dev environment, then:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pnpm install&lt;/code&gt; thereafter, &lt;code&gt;pnpm run dev:all&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What you see next should feel familiar, and if you're a web developer, it's even easier, because it follows the same flow you already use day to day. The UI runs on localhost which you can open in any browser, be it &lt;strong&gt;Chromium-based&lt;/strong&gt; or &lt;strong&gt;Gecko/Firefox&lt;/strong&gt; for testing.&lt;/p&gt;
&lt;p&gt;Try it here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/rezzcode/vuejs-extensions.git&quot;&gt;https://github.com/rezzcode/vuejs-extensions.git&lt;/a&gt; and tell me what you think.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/29753/vue-js-web-browser-extension-template</guid>
<pubDate>Sun, 04 Oct 2026 18:51:54 +0000</pubDate>
</item>
<item>
<title>api-performance-profiler</title>
<link>https://coderlegion.com/28855/api-performance-profiler</link>
<description>&lt;p&gt;  As backend developers, we often fly blind when building APIs locally. You write a route, but you don't really know how fast it runs until you leave your editor, set up Postman,&lt;br&gt;
  or configure a heavy APM tool. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;I wanted performance testing to feel native and frictionless, so I built an open-source, zero-dependency local profiling extension for Node.js (Express &amp;amp; NestJS). 

Here is what it brings directly to your editor:

🟢 Inline Latency Metrics: See real-time execution speeds (e.g., 🟢 84ms) rendered right beside your route definitions in the code.
🚀 1-Click Load Testing: A '▶ Load test' CodeLens sits directly above your route. Click it to replay the last recorded request 1,000 times under heavy load, simulating real
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  traffic instantly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;📊 Routes Dashboard: A dedicated VS Code sidebar showing all observed routes in your app, sorted from slowest to fastest.
🔐 100% Local &amp;amp; Secure: Everything is stored in local memory. No cloud accounts, no external dashboards, and no bloated tracking dependencies. 

It takes less than a minute to set up—just install the lightweight middleware and the VS Code extension, and your editor comes alive with performance data.

I’d love for you to try it out on your local Express or NestJS projects! Let me know your thoughts, feature requests, or feedback in the comments.
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/28855/api-performance-profiler</guid>
<pubDate>Mon, 28 Sep 2026 04:48:58 +0000</pubDate>
</item>
<item>
<title>Trying to intercept Slack push notifications from a Chrome extension. Here's why it's Impossible</title>
<link>https://coderlegion.com/26467/trying-intercept-slack-push-notifications-from-chrome-extension-heres-why-its-impossible</link>
<description>&lt;p&gt;After I published my last article about building a Chrome extension that speaks browser notifications aloud, a commenter asked a question I didn't have a good answer to.&lt;/p&gt;
&lt;p&gt;He pointed out that a lot of web apps — Slack, Gmail, most modern tools — fire their notifications from a service worker via &lt;code&gt;registration.showNotification()&lt;/code&gt;, not from the page's JavaScript context. My MAIN world override of &lt;code&gt;window.Notification&lt;/code&gt; would never reach those.&lt;/p&gt;
&lt;p&gt;He was right. And I told him I'd look into it.&lt;/p&gt;
&lt;p&gt;I spent a week researching whether there was any way to close that gap. There isn't. But the reason why is more interesting than a simple &quot;no.&quot;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Two ways a website can show you a notification&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When a website sends you a browser notification, it can do it in one of two ways.&lt;/p&gt;
&lt;p&gt;The first is the constructor path. The page's own JavaScript calls &lt;code&gt;new Notification(&quot;You have a message&quot;)&lt;/code&gt; directly. This is common for in-tab alerts, real-time updates when you're actively on the site, or any notification triggered by something you just did.&lt;/p&gt;
&lt;p&gt;The second is the push path. The browser receives a push event from the website's server, wakes up the website's service worker in the background, and the service worker calls &lt;code&gt;self.registration.showNotification()&lt;/code&gt; from inside its own scope. This is what happens when Slack notifies you of a new message while the tab is closed or backgrounded. The page never runs. No page JavaScript ever fires.&lt;/p&gt;
&lt;p&gt;My extension catches the first path. The MAIN world content script overrides &lt;code&gt;window.Notification&lt;/code&gt; before any page code runs. But the service worker never touches the page's window. It has no &lt;code&gt;window&lt;/code&gt;. It runs in a completely isolated thread, completely separate from the page, and calls &lt;code&gt;showNotification&lt;/code&gt; on itself.&lt;/p&gt;
&lt;p&gt;The override is never reached.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Why can't the extension reach the service worker?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the part that took me a week to fully accept.&lt;/p&gt;
&lt;p&gt;Chrome extensions can inject content scripts into web pages. They can run code in the MAIN world or the ISOLATED world of a page. They can observe network requests, intercept navigations, and modify headers.&lt;/p&gt;
&lt;p&gt;What they cannot do is inject code into a third-party website's service worker.&lt;/p&gt;
&lt;p&gt;Service workers operate in a separate thread with no DOM, no window, and no connection to the page's JavaScript context. The Chrome Extensions API simply does not offer a mechanism to run extension code inside an arbitrary third-party service worker scope. It's a hard security boundary — and for good reasons. Allowing extensions to arbitrarily patch service workers for any website on the internet would be a significant attack surface.&lt;/p&gt;
&lt;p&gt;So: the extension lives on the page side. The push notification lives on the service worker side. There is a wall between them.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Three approaches I investigated&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I didn't accept &quot;impossible&quot; without looking for workarounds. Here's what I found.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approach 1: Override ServiceWorkerRegistration.prototype.showNotification in the MAIN world&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A page's JavaScript can call &lt;code&gt;navigator.serviceWorker.ready.then(reg =&amp;gt; reg.showNotification(...))&lt;/code&gt;. If you override &lt;code&gt;ServiceWorkerRegistration.prototype.showNotification&lt;/code&gt; in the MAIN world, you'd intercept that pattern.&lt;/p&gt;
&lt;p&gt;The problem: Slack's push notifications don't come from page JavaScript. They come from inside the service worker script itself, where it calls &lt;code&gt;self.registration.showNotification()&lt;/code&gt;. The service worker runs in its own scope. The MAIN world prototype override doesn't reach there.&lt;/p&gt;
&lt;p&gt;This approach catches one narrow pattern and misses the actual push notification path entirely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approach 2: chrome.debugger API with CDP&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the most discussed workaround in the Chromium Extensions community. The &lt;code&gt;chrome.debugger&lt;/code&gt; API gives an extension access to the Chrome DevTools Protocol. With CDP's &lt;code&gt;Fetch.fulfillRequest&lt;/code&gt; command, you can intercept a network response — including a service worker JavaScript file — and modify it before the browser processes it. Inject your code into the SW script, and it runs inside the service worker scope.&lt;/p&gt;
&lt;p&gt;This technically works. It is completely unusable for a consumer extension.&lt;/p&gt;
&lt;p&gt;When you attach the debugger to any tab, Chrome displays a persistent banner at the top of the browser: &quot;Chrome is being controlled by automated test software.&quot; This banner appears on every tab. It cannot be dismissed. It cannot be hidden by the extension. The only way to suppress it is to launch Chrome with &lt;code&gt;--silent-debugger-extension-api&lt;/code&gt;, which is not something a real user would do.&lt;/p&gt;
&lt;p&gt;For a Chrome Web Store extension that people install on their own machine, this approach would immediately look like malware. Ruled out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approach 3: declarativeNetRequest&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Manifest V3's &lt;code&gt;declarativeNetRequest&lt;/code&gt; API lets extensions block or redirect network requests. Could you redirect the Slack service worker script to a modified version?&lt;/p&gt;
&lt;p&gt;No. &lt;code&gt;declarativeNetRequest&lt;/code&gt; can block or redirect a request, but it cannot modify the response body. And even if it could redirect the service worker script URL, the replacement would need to be served from the same origin as the site — extensions can't serve content from &lt;code&gt;slack.com&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;What Firefox can do that Chrome can't&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here's where it gets interesting.&lt;/p&gt;
&lt;p&gt;Firefox has an API called &lt;code&gt;webRequest.filterResponseData()&lt;/code&gt;. It lets an extension intercept a network response and modify its body before the browser processes it. Applied to a service worker JavaScript file, you could inject code that runs inside the SW scope. That code could override &lt;code&gt;self.registration.showNotification&lt;/code&gt; and intercept push notifications.&lt;/p&gt;
&lt;p&gt;This capability exists in Firefox. It was advocated for by Giorgio Maone, the developer of NoScript, who needed exactly this kind of deep injection for a privacy tool.&lt;/p&gt;
&lt;p&gt;Chrome removed blocking &lt;code&gt;webRequest&lt;/code&gt; in MV3 and never added a &lt;code&gt;filterResponseData&lt;/code&gt; equivalent. This is one of the more significant functional divergences between the two extension models post-MV3. Extension developers have raised it on the Chromium mailing lists. For now, it's a genuine gap.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;What this means for &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/hnaggblalhlbihfaegbknioadncpcged?utm_source=coderlegion&amp;amp;utm_medium=content&amp;amp;utm_campaign=article2-sw&quot;&gt;Serious Notification&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The extension works well for the constructor path — notifications fired from page JavaScript when a tab is active. For many apps and many use cases, that's most of their notification volume.&lt;/p&gt;
&lt;p&gt;For pure push-via-service-worker notifications from backgrounded tabs — like Slack when you've minimised the window — the extension will miss them. I've added a Known Limitations section to the original article to reflect this honestly.&lt;/p&gt;
&lt;p&gt;What would need to change for this to be solvable on Chrome? Some form of extension access to service worker scope — whether through a filtered response body API like Firefox's, or through a purpose-built notification interception hook in the Chrome Extensions API. Neither exists today.&lt;/p&gt;
&lt;p&gt;If you want to see this change, the Chromium issue tracker is the place to make that case. I'm watching it.&lt;/p&gt;
&lt;p&gt;In the meantime, if you want to try what's possible now:&lt;/p&gt;
&lt;p&gt;🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/hnaggblalhlbihfaegbknioadncpcged?utm_source=coderlegion&amp;amp;utm_medium=content&amp;amp;utm_campaign=article2-sw&quot;&gt;https://chromewebstore.google.com/detail/hnaggblalhlbihfaegbknioadncpcged?utm_source=coderlegion&amp;amp;utm_medium=content&amp;amp;utm_campaign=article2-sw&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26467/trying-intercept-slack-push-notifications-from-chrome-extension-heres-why-its-impossible</guid>
<pubDate>Tue, 08 Sep 2026 07:57:05 +0000</pubDate>
</item>
<item>
<title>How I built a Chrome extension that speaks notifications — and the two Manifest V3 problem i faced</title>
<link>https://coderlegion.com/25904/how-built-chrome-extension-that-speaks-notifications-and-the-two-manifest-problem-faced</link>
<description>&lt;p&gt;I counted how many times I glanced at a notification during a coding session last week.&lt;/p&gt;
&lt;p&gt;Thirty-four times. Only four needed my attention.&lt;/p&gt;
&lt;p&gt;So I built &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/hnaggblalhlbihfaegbknioadncpcged?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=technical-article&quot;&gt;Serious Notification&lt;/a&gt; — a Chrome extension that speaks browser notifications aloud, but only when they match keywords you set. Type &quot;failed&quot; and only hear build errors. Type &quot;urgent&quot; and only hear what actually matters. Everything else stays silent.&lt;/p&gt;
&lt;p&gt;It took a weekend to build the core. The two Manifest V3 architectural problems I hit along the way took longer to figure out. Neither of them is well documented. Here's both.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Problem 1: You can't intercept the Notification API in a regular content script&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The plan seemed simple: listen for notifications, check them against keywords, speak the matching ones. A content script listening for notification events should work, right?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;Chrome's &lt;code&gt;Notification&lt;/code&gt; constructor lives on &lt;code&gt;window&lt;/code&gt;. When a website calls &lt;code&gt;new Notification(&quot;You have a message&quot;)&lt;/code&gt;, it's using &lt;code&gt;window.Notification&lt;/code&gt; in the page's own JavaScript context.&lt;/p&gt;
&lt;p&gt;A standard Manifest V3 content script runs in what Chrome calls an &lt;strong&gt;ISOLATED world&lt;/strong&gt; — a sandboxed environment that shares the DOM with the page but has its own separate JavaScript context. Your content script can read the DOM, but it cannot touch &lt;code&gt;window.Notification&lt;/code&gt; on the actual page. By the time your script runs, the page's Notification constructor is completely separate from anything you can reach.&lt;/p&gt;
&lt;p&gt;You can listen for a notification after it fires. You cannot intercept it before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix: MAIN world content scripts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Manifest V3 added support for content scripts that run in the MAIN world. Set &lt;code&gt;&quot;world&quot;: &quot;MAIN&quot;&lt;/code&gt; in your manifest and your script runs directly in the page's JavaScript context — before any page code executes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;content_scripts&quot;: [
    {
      &quot;matches&quot;: [&quot;&amp;lt;all_urls&amp;gt;&quot;],
      &quot;js&quot;: [&quot;interceptor.js&quot;],
      &quot;world&quot;: &quot;MAIN&quot;,
      &quot;run_at&quot;: &quot;document_start&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can override &lt;code&gt;window.Notification&lt;/code&gt; before the page has a chance to use it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const OriginalNotification = window.Notification;

window.Notification = function(title, options = {}) {
  window.dispatchEvent(
    new CustomEvent('__sn_notification__', {
      detail: { title, body: options.body || '' }
    })
  );
  return new OriginalNotification(title, options);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The catch: MAIN world scripts have no Chrome extension APIs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here is the problem with MAIN world scripts. They run in the page context, which means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;chrome.storage&lt;/code&gt; — you cannot read the user's saved keywords&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;speechSynthesis&lt;/code&gt; — you cannot speak the notification aloud&lt;/li&gt;
&lt;li&gt;No extension APIs at all&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You are in the page's world. Chrome's extension APIs are not available there.&lt;/p&gt;
&lt;p&gt;So you need two scripts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The bridge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Script 1 (&lt;code&gt;interceptor.js&lt;/code&gt;) runs in the MAIN world. It overrides &lt;code&gt;window.Notification&lt;/code&gt; and fires a custom DOM event when a notification arrives.&lt;/p&gt;
&lt;p&gt;Script 2 (&lt;code&gt;content.js&lt;/code&gt;) runs in the ISOLATED world. It has full access to &lt;code&gt;chrome.storage&lt;/code&gt; and &lt;code&gt;speechSynthesis&lt;/code&gt;. It listens for the custom event:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;window.addEventListener('__sn_notification__', async (e) =&amp;gt; {
  const { title, body } = e.detail;
  const data = await chrome.storage.local.get(['enabled', 'keywords', 'speed']);

  if (!data.enabled) return;

  const keywords = data.keywords || [];
  const text = `${title} ${body}`.toLowerCase();
  const match = keywords.some(kw =&amp;gt; text.includes(kw.toLowerCase()));

  if (match || keywords.length === 0) {
    const utterance = new SpeechSynthesisUtterance(`${title}. ${body}`);
    utterance.rate = data.speed || 1.0;
    speechSynthesis.speak(utterance);
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two scripts. One custom DOM event as the bridge. The MAIN world intercepts, the ISOLATED world decides.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Problem 2: Chrome showed a &quot;read and change all your data on all websites&quot; warning at install&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The extension needs to inject on every page — that's legitimate, since you don't know which tab will fire a notification. So &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt; host access is genuinely required.&lt;/p&gt;
&lt;p&gt;But I was declaring it as a required permission in &lt;code&gt;manifest.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;content_scripts&quot;: [
  {
    &quot;matches&quot;: [&quot;&amp;lt;all_urls&amp;gt;&quot;],
    ...
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This caused Chrome to show the scariest possible install warning before users had any reason to trust the extension. I was watching my analytics and seeing a 33% uninstall rate. Some of that was almost certainly people bouncing at the permission screen, before they ever heard the extension speak a single word.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix: optional host permissions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Manifest V3 supports &lt;code&gt;optional_host_permissions&lt;/code&gt;. Move &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt; there instead of requiring it at install:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;permissions&quot;: [&quot;storage&quot;, &quot;scripting&quot;],
  &quot;optional_host_permissions&quot;: [&quot;&amp;lt;all_urls&amp;gt;&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then register content scripts dynamically at runtime using &lt;code&gt;chrome.scripting.registerContentScripts()&lt;/code&gt;, only after the user explicitly grants the permission:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// background.js
chrome.permissions.onAdded.addListener(async (permissions) =&amp;gt; {
  if (permissions.origins?.includes('&amp;lt;all_urls&amp;gt;')) {
    await chrome.scripting.registerContentScripts([
      {
        id: 'interceptor',
        matches: ['&amp;lt;all_urls&amp;gt;'],
        js: ['interceptor.js'],
        world: 'MAIN',
        runAt: 'document_start',
      },
      {
        id: 'content',
        matches: ['&amp;lt;all_urls&amp;gt;'],
        js: ['content.js'],
        runAt: 'document_start',
      }
    ]);
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the popup, show a &quot;Turn on to hear alerts&quot; button that triggers the permission request:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;chrome.permissions.request({ origins: ['&amp;lt;all_urls&amp;gt;'] });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result: the Chrome Web Store install dialog now shows &lt;strong&gt;Storage only&lt;/strong&gt;. No broad-access warning. The scary prompt only appears when the user explicitly clicks &quot;Turn on&quot; inside the popup — after they have already installed the extension and understand what it does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One edge case: updating from a version that required the permission&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you update an extension and a previously required permission becomes optional, Chrome removes it from the user's granted set. Existing users lose host access silently on update — the extension just stops working with no explanation.&lt;/p&gt;
&lt;p&gt;The fix: fire a native notification immediately after update using the &lt;code&gt;notifications&lt;/code&gt; permission:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;chrome.runtime.onInstalled.addListener(({ reason }) =&amp;gt; {
  if (reason === 'update') {
    chrome.notifications.create('update-notice', {
      type: 'basic',
      iconUrl: 'icon.png',
      title: 'Serious Notification — One quick step needed',
      message: 'We improved how permissions work. Open the extension and click &quot;Turn on&quot; to re-enable.',
      requireInteraction: true,
    });
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silent failure is the worst outcome for retention. If something breaks on update, tell the user immediately and tell them exactly what to do.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;What I built&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Serious Notification speaks Chrome notifications aloud when they match keywords you set. Free, no account, works offline. Just shipped v0.2.0 with the optional permissions model above.&lt;/p&gt;
&lt;p&gt;If you're building Chrome extensions and hit either of these problems — happy to answer questions in the comments. The MAIN world injection in particular has some edge cases around static property copying (&lt;code&gt;permission&lt;/code&gt;, &lt;code&gt;requestPermission&lt;/code&gt;) that took me a while to get right.&lt;/p&gt;
&lt;p&gt;🔗&lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/hnaggblalhlbihfaegbknioadncpcged?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=technical-article&quot;&gt;Serious Notification - Live Chrome WebStore&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;known-limitations&quot;&gt;Known Limitations&lt;/h3&gt;
&lt;p&gt;This approach only intercepts notifications fired via the Notification constructor in the page's JavaScript context.&lt;/p&gt;
&lt;p&gt;Notifications fired from a service worker via registration.showNotification() — which is the path used by background push notifications in apps like Slack — bypass the MAIN world override entirely. The service worker runs in its own scope and never touches the page's window.&lt;/p&gt;
&lt;p&gt;Chrome's extension model does not currently offer a hook into arbitrary third-party service workers, so this gap cannot be closed with the current architecture.&lt;/p&gt;
&lt;p&gt;The extension works well for in-tab notifications and apps that call the constructor directly from page scripts. For pure push-via-service-worker apps when the tab is backgrounded, it will miss notifications.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25904/how-built-chrome-extension-that-speaks-notifications-and-the-two-manifest-problem-faced</guid>
<pubDate>Mon, 31 Aug 2026 09:17:48 +0000</pubDate>
</item>
<item>
<title>10 VS Code Extensions Every Frontend Developer Needs</title>
<link>https://coderlegion.com/22992/10-vs-code-extensions-every-frontend-developer-needs</link>
<description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I used to install every extension that looked interesting — ended up with 40+ extensions and a VS Code that took 10 seconds just to open. Eventually I stripped it back down to the ones I actually use every single day.&lt;/p&gt;
&lt;p&gt;These 10 are the ones that survived that cleanup. If you're setting up a fresh frontend dev environment, start here.&lt;/p&gt;
&lt;h3 id=&quot;1-eslint&quot;&gt;1. ESLint&lt;/h3&gt;
&lt;p&gt;Catches bugs and enforces consistent code style before you even run your code. If you're working in a team (or want to look like you know what you're doing), this is non-negotiable.&lt;/p&gt;
&lt;h3 id=&quot;2-prettier-code-formatter&quot;&gt;2. Prettier — Code Formatter&lt;/h3&gt;
&lt;p&gt;Auto-formats your code on save. No more arguing about tabs vs. spaces or where semicolons go — Prettier just decides and moves on.&lt;/p&gt;
&lt;p&gt;Setup tip: Enable &quot;Format on Save&quot; in VS Code settings so it happens automatically.&lt;/p&gt;
&lt;h3 id=&quot;3-tailwind-css-intellisense&quot;&gt;3. Tailwind CSS IntelliSense&lt;/h3&gt;
&lt;p&gt;If you're using Tailwind (and you should be, per Article 3), this gives you autocomplete for class names, shows the actual CSS values on hover, and even highlights conflicting classes.&lt;/p&gt;
&lt;h3 id=&quot;4-auto-rename-tag&quot;&gt;4. Auto Rename Tag&lt;/h3&gt;
&lt;p&gt;Rename an opening HTML/JSX tag and the closing tag updates automatically. Small thing, saves real time, especially in nested JSX.&lt;/p&gt;
&lt;h3 id=&quot;5-bracket-pair-colorizer-built-in-now-&quot;&gt;5. Bracket Pair Colorizer (Built-in now)&lt;/h3&gt;
&lt;p&gt;VS Code now has this built in by default — matching brackets get color-coded so you can actually see which { belongs to which } in deeply nested code. Just make sure it's enabled in settings.&lt;/p&gt;
&lt;h3 id=&quot;6-gitlens&quot;&gt;6. GitLens&lt;/h3&gt;
&lt;p&gt;Shows you who changed a line of code and when, right inline in the editor. Incredibly useful once you're working with any kind of version history — even solo projects benefit from seeing your own commit history at a glance.&lt;/p&gt;
&lt;h3 id=&quot;7-live-server&quot;&gt;7. Live Server&lt;/h3&gt;
&lt;p&gt;Spins up a local development server with live reload in one click — no need to manually refresh the browser every time you save a file. Especially useful for plain HTML/CSS/JS projects without a build tool.&lt;/p&gt;
&lt;h3 id=&quot;8-thunder-client&quot;&gt;8. Thunder Client&lt;/h3&gt;
&lt;p&gt;A lightweight REST API client built directly into VS Code. If you're testing an API endpoint for a project, you don't need to leave your editor or open Postman separately.&lt;/p&gt;
&lt;h3 id=&quot;9-error-lens&quot;&gt;9. Error Lens&lt;/h3&gt;
&lt;p&gt;Shows error and warning messages inline, right next to the line of code causing the problem, instead of making you check the Problems panel. Cuts down debugging time noticeably.&lt;/p&gt;
&lt;h3 id=&quot;10-material-icon-theme&quot;&gt;10. Material Icon Theme&lt;/h3&gt;
&lt;p&gt;Purely visual, but genuinely useful — gives every file type a distinct, recognizable icon in your file explorer. Makes navigating a large project noticeably faster once your eyes learn the icon shapes.&lt;/p&gt;
&lt;p&gt;How to Install Any of These&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open VS Code&lt;/li&gt;
&lt;li&gt;Click the Extensions icon in the sidebar (or Ctrl+Shift+X)&lt;/li&gt;
&lt;li&gt;Search the extension name&lt;/li&gt;
&lt;li&gt;Click Install&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That's it — no restart usually needed.&lt;/p&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;You don't need 40 extensions to be productive — you need the right 8 to 10. Install these, use them for a week, and you'll notice fewer small annoyances and less time spent on formatting, tag matching, and manual browser refreshing.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Muhammad Farhan&lt;/strong&gt; is a Frontend Developer specializing in React.js and Tailwind CSS, based in Dera Ismail Khan, Pakistan.&lt;br&gt;
&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;em&gt;muhammad-farhan-dev.github.io/muhammadfarhan.dev&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22992/10-vs-code-extensions-every-frontend-developer-needs</guid>
<pubDate>Fri, 24 Jul 2026 05:00:00 +0000</pubDate>
</item>
<item>
<title>website blocker Do not distracted</title>
<link>https://coderlegion.com/19889/website-blocker-do-not-distracted</link>
<description>&lt;p&gt;it locks the website only user selected website be unlock so user can focus on work.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&quot;&gt;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;website blocker locks all the website only user selected website be unlock so user can focus on work or you can use this for the children. &lt;/p&gt;
&lt;p&gt;How to work:- click on the extension open the extension popup click on the set password create the password and type the password and click to the ok and go to website you want to be unlock by default. goes to the website url and click to the save tab all the selected tab are become unlock all the time other website become lock if you want to open that type the right password and click to the ok that website become unlock but after removing the tab it automatic lock the website.&lt;/p&gt;
&lt;p&gt;after creating and changing the password if the any tab is opened before setting the password refresh the page.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/19889/website-blocker-do-not-distracted</guid>
<pubDate>Sun, 07 Jun 2026 07:41:37 +0000</pubDate>
</item>
<item>
<title>Do not distracted website blocker</title>
<link>https://coderlegion.com/19888/do-not-distracted-website-blocker</link>
<description>&lt;p&gt;it locks the website only user selected website be unlock so user can focus on work.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&quot;&gt;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;website blocker locks all the website only user selected website be unlock so user can focus on work or you can use this for the children. &lt;/p&gt;
&lt;p&gt;How to work:- click on the extension open the extension popup click on the set password create the password and type the password and click to the ok and go to website you want to be unlock by default. goes to the website url and click to the save tab all the selected tab are become unlock all the time other website become lock if you want to open that type the right password and click to the ok that website become unlock but after removing the tab it automatic lock the website.&lt;/p&gt;
&lt;p&gt;after creating and changing the password if the any tab is opened before setting the password refresh the page.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/19888/do-not-distracted-website-blocker</guid>
<pubDate>Sun, 07 Jun 2026 07:39:02 +0000</pubDate>
</item>
<item>
<title>chrome extension Do not distracted website blocker locks all website only user selected be unlock.</title>
<link>https://coderlegion.com/19887/chrome-extension-not-distracted-website-blocker-locks-website-only-user-selected-unlock</link>
<description>&lt;p&gt;it locks the website only user selected website be unlock so user can focus on work.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&quot;&gt;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;website blocker locks all the website only user selected website be unlock so user can focus on work or you can use this for the children. &lt;/p&gt;
&lt;p&gt;How to work:- click on the extension open the extension popup click on the set password create the password and type the password and click to the ok and go to website you want to be unlock by default. goes to the website url and click to the save tab all the selected tab are become unlock all the time other website become lock if you want to open that type the right password and click to the ok that website become unlock but after removing the tab it automatic lock the website.&lt;/p&gt;
&lt;p&gt;after creating and changing the password if the any tab is opened before setting the password refresh the page.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19887/chrome-extension-not-distracted-website-blocker-locks-website-only-user-selected-unlock</guid>
<pubDate>Sun, 07 Jun 2026 07:36:47 +0000</pubDate>
</item>
<item>
<title>Do not distracted chrome extension locks website so can user focus on work only selected be unlock.</title>
<link>https://coderlegion.com/19338/not-distracted-chrome-extension-locks-website-can-user-focus-on-work-only-selected-unlock</link>
<description>&lt;p&gt;It locks all the website only user selected website be unlock so user can focus on work or you can use this for the children. &lt;/p&gt;
&lt;p&gt;[chrome web store link to download]&lt;br&gt;
  [1]: &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&quot;&gt;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;How to work:- click on the extension open the extension popup click on the set password create the password and type the password and click to the ok and go to website you want to be unlock by default. goes to the website url and click to the save tab all the selected tab are become unlock all the time other website become lock if you want to open that type the right password and click to the ok that website become unlock but after removing the tab it automatic lock the website.&lt;/p&gt;
&lt;p&gt;after creating and changing the password if the any tab is opened before setting the password refresh the page.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19338/not-distracted-chrome-extension-locks-website-can-user-focus-on-work-only-selected-unlock</guid>
<pubDate>Sun, 31 May 2026 05:20:57 +0000</pubDate>
</item>
<item>
<title>Your AI Chats Are Locked in Corporate Silos. I Built an Open-Source Vault to Rescue Them</title>
<link>https://coderlegion.com/19011/your-ai-chats-are-locked-in-corporate-silos-i-built-an-open-source-vault-to-rescue-them</link>
<description>&lt;h2 id=&quot;i-kept-losing-context-switching-between-ai-tools-so-i-built-a-browser-extension-that-remembers-everything&quot;&gt;I Kept Losing Context Switching Between AI Tools — So I Built a Browser Extension That Remembers Everything&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cvuf736i09mdxasfhg2.gif&quot; alt=&quot;ContextVault Demo&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Your AI conversations should not be trapped inside platforms.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;By 2025, most developers use multiple AI tools every day.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ChatGPT for quick questions&lt;/li&gt;
&lt;li&gt;Claude for long reasoning&lt;/li&gt;
&lt;li&gt;Gemini for research&lt;/li&gt;
&lt;li&gt;Perplexity for analysis&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But every platform stores conversations separately.&lt;/p&gt;
&lt;p&gt;That creates a frustrating workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conversations get cut when context windows fill up&lt;/li&gt;
&lt;li&gt;Switching platforms destroys continuity&lt;/li&gt;
&lt;li&gt;Exporting chats is painful or incomplete&lt;/li&gt;
&lt;li&gt;Your history is tied to accounts you don’t control&lt;/li&gt;
&lt;li&gt;There’s no unified search across platforms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The knowledge you build with AI is trapped inside isolated silos.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-existing-solutions-didn-t-feel-right&quot;&gt;Why Existing Solutions Didn’t Feel Right&lt;/h3&gt;
&lt;p&gt;Some tools try solving this problem, but they usually require:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Paid APIs&lt;/li&gt;
&lt;li&gt;Backend servers&lt;/li&gt;
&lt;li&gt;Account linking&lt;/li&gt;
&lt;li&gt;Cloud syncing&lt;/li&gt;
&lt;li&gt;Monthly subscriptions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wanted something simpler.&lt;/p&gt;
&lt;p&gt;A question kept bothering me:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What if every AI conversation stayed entirely inside the browser?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No backend. No accounts. No tracking.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-solution-contextvault&quot;&gt;The Solution: ContextVault&lt;/h3&gt;
&lt;p&gt;ContextVault is an open-source Chrome extension that captures AI conversations locally while you use platforms like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;li&gt;Claude&lt;/li&gt;
&lt;li&gt;Gemini&lt;/li&gt;
&lt;li&gt;Perplexity&lt;/li&gt;
&lt;li&gt;Poe&lt;/li&gt;
&lt;li&gt;DeepSeek&lt;/li&gt;
&lt;li&gt;Grok&lt;/li&gt;
&lt;li&gt;Microsoft Copilot&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything stays on your machine.&lt;/p&gt;
&lt;p&gt;No telemetry. No external storage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/309keyjzqzhcaaqo7oy2.png&quot; alt=&quot;ContextVault Screenshot&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h2&gt;
&lt;p&gt;Capturing AI conversations reliably turned out to be harder than expected.&lt;/p&gt;
&lt;p&gt;There were two major problems:&lt;/p&gt;
&lt;h4 id=&quot;1-every-platform-has-a-different-dom-structure&quot;&gt;1. Every platform has a different DOM structure&lt;/h4&gt;
&lt;p&gt;Selectors, layouts, and rendering logic differ completely between ChatGPT, Claude, Gemini, and others.&lt;/p&gt;
&lt;h4 id=&quot;2-ai-responses-stream-in-real-time&quot;&gt;2. AI responses stream in real time&lt;/h4&gt;
&lt;p&gt;Messages arrive token-by-token instead of all at once.&lt;/p&gt;
&lt;p&gt;To solve this, ContextVault uses a 6-layer architecture:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Adapters → Capture → Stream Assembler → Background Worker → Storage → Export
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-adapters-layer&quot;&gt;1. Adapters Layer&lt;/h3&gt;
&lt;p&gt;Each platform has its own adapter implementing a shared interface:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;interface LLMAdapter {
  platform: Platform
  urlPattern: RegExp
  messageContainer: string
  userSelector: string
  assistantSelector: string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each adapter knows how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Detect the platform&lt;/li&gt;
&lt;li&gt;Locate messages&lt;/li&gt;
&lt;li&gt;Handle UI differences&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-capture-layer-dom-network-&quot;&gt;2. Capture Layer (DOM + Network)&lt;/h3&gt;
&lt;p&gt;This is the core system.&lt;/p&gt;
&lt;h4 id=&quot;dom-observer&quot;&gt;DOM Observer&lt;/h4&gt;
&lt;p&gt;A MutationObserver watches the conversation UI in real time.&lt;/p&gt;
&lt;p&gt;It detects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New messages&lt;/li&gt;
&lt;li&gt;Streaming updates&lt;/li&gt;
&lt;li&gt;Edited content&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;network-monitor&quot;&gt;Network Monitor&lt;/h4&gt;
&lt;p&gt;The extension also patches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fetch&lt;/li&gt;
&lt;li&gt;XMLHttpRequest&lt;/li&gt;
&lt;li&gt;WebSocket&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This provides early access to streaming responses directly from network traffic.&lt;/p&gt;
&lt;h4 id=&quot;why-both-&quot;&gt;Why both?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;DOM reflects exactly what the user sees&lt;/li&gt;
&lt;li&gt;Network capture improves streaming accuracy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using both together dramatically reduced missed messages.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-stream-assembler&quot;&gt;3. Stream Assembler&lt;/h3&gt;
&lt;p&gt;AI responses arrive in chunks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;Hel&quot;
&quot;lo &quot;
&quot;world&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Stream Assembler rebuilds complete messages while handling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chunk merging&lt;/li&gt;
&lt;li&gt;Duplicate removal&lt;/li&gt;
&lt;li&gt;Completion detection&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-background-worker&quot;&gt;4. Background Worker&lt;/h3&gt;
&lt;p&gt;The background worker manages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Active sessions&lt;/li&gt;
&lt;li&gt;Conversation lifecycle&lt;/li&gt;
&lt;li&gt;Deduplication&lt;/li&gt;
&lt;li&gt;Streaming updates&lt;/li&gt;
&lt;li&gt;Conversation linking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also prevents duplicate saves during rapid streaming events.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-storage-layer-indexeddb-&quot;&gt;5. Storage Layer (IndexedDB)&lt;/h3&gt;
&lt;p&gt;Everything is stored locally using IndexedDB.&lt;/p&gt;
&lt;p&gt;Stored data includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conversations&lt;/li&gt;
&lt;li&gt;Drafts&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each conversation stores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Platform&lt;/li&gt;
&lt;li&gt;Messages&lt;/li&gt;
&lt;li&gt;Timestamps&lt;/li&gt;
&lt;li&gt;Model information&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Conversation chains&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No external database is used.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-export-layer&quot;&gt;6. Export Layer&lt;/h3&gt;
&lt;p&gt;Conversations can be exported as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;ZIP archives&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;## User
How do I design a secure API?

## Assistant
Start with authentication, validation, and rate limiting...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gu15b3qp35uhx9xfn23.png&quot; alt=&quot;Markdown Export&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;privacy-first&quot;&gt;Privacy First&lt;/h2&gt;
&lt;p&gt;ContextVault was designed around one principle:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Your conversations belong to you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the extension:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has no backend server&lt;/li&gt;
&lt;li&gt;Uses no analytics or telemetry&lt;/li&gt;
&lt;li&gt;Works fully offline&lt;/li&gt;
&lt;li&gt;Only activates on supported AI domains&lt;/li&gt;
&lt;li&gt;Never sends your conversations anywhere&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Manifest V3&lt;/li&gt;
&lt;li&gt;CRXJS + Vite&lt;/li&gt;
&lt;li&gt;IndexedDB&lt;/li&gt;
&lt;li&gt;MutationObserver&lt;/li&gt;
&lt;li&gt;JSZip&lt;/li&gt;
&lt;li&gt;Vitest&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-thoughts-1&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;AI tools are becoming part of how we think, not just how we code.&lt;/p&gt;
&lt;p&gt;But memory between tools is still broken.&lt;/p&gt;
&lt;p&gt;Every platform acts like a closed silo where conversations disappear the moment you switch workflows.&lt;/p&gt;
&lt;p&gt;ContextVault is my attempt to fix that.&lt;/p&gt;
&lt;p&gt;A lightweight browser extension that keeps your AI conversations portable, searchable, and fully yours.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;links&quot;&gt;Links&lt;/h3&gt;
&lt;p&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/aliabdm/ContextVault&quot;&gt;https://github.com/aliabdm/ContextVault&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Demo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://context-vault-two.vercel.app/&quot;&gt;https://context-vault-two.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Portfolio: &lt;a rel=&quot;nofollow&quot; href=&quot;https://senior-mohammad-ali.vercel.app/&quot;&gt;https://senior-mohammad-ali.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LinkedIn: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/mohammad-ali-abdul-wahed-1533b9171/&quot;&gt;https://www.linkedin.com/in/mohammad-ali-abdul-wahed-1533b9171/&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19011/your-ai-chats-are-locked-in-corporate-silos-i-built-an-open-source-vault-to-rescue-them</guid>
<pubDate>Tue, 26 May 2026 19:40:20 +0000</pubDate>
</item>
<item>
<title>Do not distracted chrome extension that locks distracted website only selected website's be unlock.</title>
<link>https://coderlegion.com/18893/distracted-chrome-extension-that-locks-distracted-website-only-selected-websites-unlock</link>
<description>&lt;p&gt;it locks the website only user selected website be unlock so user can focus on work&lt;/p&gt;
&lt;p&gt;It locks all the website only user selected website be unlock so user can focus on work or you can use this for the children. &lt;/p&gt;
&lt;p&gt;How to work:- click on the extension open the extension popup click on the set password create the password and type the password and click to the ok and go to website you want to be unlock by default. goes to the website url and click to the save tab all the selected tab are become unlock all the time other website become lock if you want to open that type the right password and click to the ok that website become unlock but after removing the tab it automatic lock the website.&lt;/p&gt;
&lt;p&gt;after creating and changing the password if the any tab is opened before setting the password refresh the page.&lt;/p&gt;
&lt;p&gt;[chrome web store link]&lt;br&gt;
  [1]: &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&quot;&gt;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18893/distracted-chrome-extension-that-locks-distracted-website-only-selected-websites-unlock</guid>
<pubDate>Mon, 25 May 2026 16:05:26 +0000</pubDate>
</item>
<item>
<title>Do not distracted extension</title>
<link>https://coderlegion.com/18892/do-not-distracted-extension</link>
<description>&lt;p&gt;it locks the website only user selected website be unlock so user can focus on work&lt;/p&gt;
&lt;p&gt;It locks all the website only user selected website be unlock so user can focus on work or you can use this for the children. &lt;/p&gt;
&lt;p&gt;How to work:- click on the extension open the extension popup click on the set password create the password and type the password and click to the ok and go to website you want to be unlock by default. goes to the website url and click to the save tab all the selected tab are become unlock all the time other website become lock if you want to open that type the right password and click to the ok that website become unlock but after removing the tab it automatic lock the website.&lt;/p&gt;
&lt;p&gt;after creating and changing the password if the any tab is opened before setting the password refresh the page.&lt;/p&gt;
&lt;p&gt;[chrome web store link] &lt;br&gt;
  [1]: &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&quot;&gt;https://chromewebstore.google.com/detail/do-not-distracted/gacffmmkpjfmepihjjhhlhleepjlmbaj&lt;/a&gt;&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/18892/do-not-distracted-extension</guid>
<pubDate>Mon, 25 May 2026 13:17:17 +0000</pubDate>
</item>
<item>
<title>PerfLens: Web Performance Audits Beyond</title>
<link>https://coderlegion.com/17060/perflens-web-performance-audits-beyond</link>
<description>&lt;p&gt;Lighthouse is great. I use it, and I still think it is one of the most useful performance tools available to web developers.&lt;/p&gt;
&lt;p&gt;But I wanted something closer to the way I actually debug and ship web products, so I built &lt;strong&gt;PerfLens&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;PerfLens is a browser-first performance auditing extension that gives you live Core Web Vitals, Lighthouse-style scoring, resource diagnostics, root cause summaries, performance history, and launch evidence directly inside your browser workflow.&lt;/p&gt;
&lt;p&gt;Install it here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://chromewebstore.google.com/detail/perflens/gkogamlpcnneeficmcdcnnnhobnbebdc&quot;&gt;https://chromewebstore.google.com/detail/perflens/gkogamlpcnneeficmcdcnnnhobnbebdc&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-i-built-it&quot;&gt;Why I Built It&lt;/h3&gt;
&lt;p&gt;A Lighthouse report is a strong snapshot, but performance work rarely ends with one snapshot.&lt;/p&gt;
&lt;p&gt;During real development, I usually want to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is slowing this page down right now?&lt;/li&gt;
&lt;li&gt;Which resources are heavy?&lt;/li&gt;
&lt;li&gt;Are my Core Web Vitals healthy?&lt;/li&gt;
&lt;li&gt;What should I fix first?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PerfLens is built for that loop.&lt;/p&gt;
&lt;h3 id=&quot;what-it-includes&quot;&gt;What It Includes&lt;/h3&gt;
&lt;p&gt;PerfLens currently supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Core Web Vitals: LCP, FID, CLS, INP, FCP, and TTFB.&lt;/li&gt;
&lt;li&gt;0-100 performance scoring.&lt;/li&gt;
&lt;li&gt;Resource breakdowns by type and size.&lt;/li&gt;
&lt;li&gt;Audits for oversized images, missing lazy loading, render-blocking scripts, large bundles, weak caching, compression gaps, and heavy CSS.&lt;/li&gt;
&lt;li&gt;Accessibility checks for alt text, language attributes, viewport setup, headings, and form labels.&lt;/li&gt;
&lt;li&gt;Suggestions ranked by impact, effort, and confidence.&lt;/li&gt;
&lt;li&gt;Root cause summaries.&lt;/li&gt;
&lt;li&gt;Framework and build detection.&lt;/li&gt;
&lt;li&gt;Local history per URL.&lt;/li&gt;
&lt;li&gt;Launch evidence packs with desktop/mobile audits, screenshots, videos, JSON reports, prioritized issues, and readiness status.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;perflens-vs-lighthouse&quot;&gt;PerfLens vs Lighthouse&lt;/h3&gt;
&lt;p&gt;PerfLens is not trying to replace Lighthouse. Lighthouse is still excellent for standardized lab audits.&lt;/p&gt;
&lt;p&gt;PerfLens is focused on the browser-side workflow around the audit.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Lighthouse &lt;/th&gt;&lt;th&gt;PerfLens&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;One-time baseline report &lt;/td&gt;&lt;td&gt;Live browser-context auditing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Standard lab diagnostics &lt;/td&gt;&lt;td&gt;Page-level history and trend signals&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;General recommendations &lt;/td&gt;&lt;td&gt;Suggestions ranked by impact, effort, and confidence&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Separate report flow &lt;/td&gt;&lt;td&gt;Popup and DevTools workflow&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Audit categories &lt;/td&gt;&lt;td&gt;Framework/build detection and root cause stories&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Report export &lt;/td&gt;&lt;td&gt;Launch evidence packs with screenshots, videos, JSON, and readiness status&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For me, the biggest difference is that PerfLens tries to move you from &quot;this is slow&quot; to &quot;this is probably why, and this is what to fix next.&quot;&lt;/p&gt;
&lt;h3 id=&quot;fix-it-with-ai&quot;&gt;Fix It With AI&lt;/h3&gt;
&lt;p&gt;When an issue appears on a localhost URL, PerfLens can identify it and generate a focused prompt for Cursor, Claude, Codex, or a custom coding agent. That makes it easier to pass local audit findings directly into your coding workflow.&lt;/p&gt;
&lt;h3 id=&quot;final&quot;&gt;Final&lt;/h3&gt;
&lt;p&gt;PerfLens came from a simple idea:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Performance auditing should be closer to the browser, closer to the product, and closer to the next fix.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you already use Lighthouse, PerfLens can sit next to it as a practical debugging and launch-readiness companion.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17060/perflens-web-performance-audits-beyond</guid>
<pubDate>Fri, 08 May 2026 12:26:58 +0000</pubDate>
</item>
<item>
<title>How I Built and Published a VS Code Extension to the Marketplace</title>
<link>https://coderlegion.com/16422/how-i-built-and-published-a-vs-code-extension-to-the-marketplace</link>
<description>&lt;h3 id=&quot;introduction-1&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;I recently built &lt;strong&gt;Q Log Session Viewer&lt;/strong&gt; — a VS Code extension that reads Amazon Q chat history and debug logs from your local machine and displays them in a browsable, filterable UI right inside VS Code. In this post I'll walk through every step: scaffolding the project, writing the extension code, packaging it, and publishing it to the VS Code Marketplace.&lt;/p&gt;
&lt;p&gt;By the end you'll have a clear mental model of how VS Code extensions work and a repeatable process for publishing your own.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-we-re-building&quot;&gt;What We're Building&lt;/h3&gt;
&lt;p&gt;The extension adds an &lt;strong&gt;Activity Bar icon&lt;/strong&gt; (sidebar panel) and a &lt;strong&gt;full editor panel&lt;/strong&gt; that reads:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;~/.aws/amazonq/history/chat-history-*.json&lt;/code&gt; — Amazon Q chat history&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;%APPDATA%\Code\logs\...\Amazon Q Logs.log&lt;/code&gt; — VS Code extension host logs&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It parses those files and renders sessions as cards, with drill-down into individual log entries.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/siddheshp/q-log-session-viewer-assets/main/screenshots/sessions-view.png&quot; alt=&quot;Sessions View&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sessions overview — chat history and log sessions shown as cards&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/siddheshp/q-log-session-viewer-assets/main/screenshots/entries-view.png&quot; alt=&quot;Entries View&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Entry detail view — filter by category, search, and inspect full JSON&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before starting, install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; 18+&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;Yeoman&lt;/strong&gt; scaffolder and VS Code extension generator (optional but helpful):&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npm install -g yo generator-code
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-scaffold-the-project&quot;&gt;Step 1 — Scaffold the Project&lt;/h3&gt;
&lt;p&gt;Run the Yeoman generator and answer the prompts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yo code
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Choose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New Extension (TypeScript)&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name: &lt;code&gt;q-log-session-viewer&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identifier: &lt;code&gt;q-log-session-viewer&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Description: &lt;em&gt;View and analyze local Q-related debug logs and chat history from VS Code&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Initialize git: Yes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bundle with webpack/esbuild: &lt;strong&gt;esbuild&lt;/strong&gt; (faster builds)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you prefer to skip Yeoman, just create the folder structure manually. The generator only saves a few minutes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The generated structure looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;q-log-session-viewer/
├── src/
│   └── extension.ts        ← entry point
├── resources/              ← icons, screenshots
├── .vscodeignore
├── esbuild.js
├── package.json
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-configure-package-json&quot;&gt;Step 2 — Configure &lt;code&gt;package.json&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; is the heart of a VS Code extension. It declares commands, views, menus, and metadata that VS Code reads at install time.&lt;/p&gt;
&lt;p&gt;Here is the full &lt;code&gt;package.json&lt;/code&gt; for this extension:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;q-log-session-viewer&quot;,
  &quot;displayName&quot;: &quot;Q Log Session Viewer (Unofficial)&quot;,
  &quot;description&quot;: &quot;View and analyze local Q-related debug logs and chat history from VS Code&quot;,
  &quot;version&quot;: &quot;0.1.1&quot;,
  &quot;publisher&quot;: &quot;SiddheshPrabhugaonkar&quot;,
  &quot;author&quot;: {
    &quot;name&quot;: &quot;Siddhesh Prabhugankar&quot;,
    &quot;url&quot;: &quot;https://github.com/siddheshp&quot;
  },
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;icon&quot;: &quot;resources/icon.png&quot;,
  &quot;galleryBanner&quot;: { &quot;color&quot;: &quot;#232F3E&quot;, &quot;theme&quot;: &quot;dark&quot; },
  &quot;engines&quot;: { &quot;vscode&quot;: &quot;^1.85.0&quot; },
  &quot;categories&quot;: [&quot;Debuggers&quot;, &quot;Other&quot;],
  &quot;keywords&quot;: [&quot;logs&quot;, &quot;debug&quot;, &quot;chat&quot;, &quot;viewer&quot;, &quot;analysis&quot;],
  &quot;activationEvents&quot;: [],
  &quot;main&quot;: &quot;./out/extension.js&quot;,
  &quot;contributes&quot;: {
    &quot;commands&quot;: [
      {
        &quot;command&quot;: &quot;amazonq-logviewer.open&quot;,
        &quot;title&quot;: &quot;Q Log Session Viewer: Open&quot;,
        &quot;icon&quot;: {
          &quot;light&quot;: &quot;resources/icon-sidebar-light.svg&quot;,
          &quot;dark&quot;: &quot;resources/icon-sidebar-dark.svg&quot;
        }
      },
      {
        &quot;command&quot;: &quot;amazonq-logviewer.refresh&quot;,
        &quot;title&quot;: &quot;Q Log Session Viewer: Refresh&quot;,
        &quot;icon&quot;: &quot;$(refresh)&quot;
      }
    ],
    &quot;viewsContainers&quot;: {
      &quot;activitybar&quot;: [
        {
          &quot;id&quot;: &quot;amazonq-logviewer&quot;,
          &quot;title&quot;: &quot;Q Logs&quot;,
          &quot;icon&quot;: &quot;resources/icon-sidebar-dark.svg&quot;
        }
      ]
    },
    &quot;views&quot;: {
      &quot;amazonq-logviewer&quot;: [
        {
          &quot;type&quot;: &quot;webview&quot;,
          &quot;id&quot;: &quot;amazonq-logviewer.viewer&quot;,
          &quot;name&quot;: &quot;Log Viewer&quot;
        }
      ]
    },
    &quot;menus&quot;: {
      &quot;editor/title&quot;: [
        { &quot;command&quot;: &quot;amazonq-logviewer.open&quot;, &quot;group&quot;: &quot;navigation&quot; }
      ]
    }
  },
  &quot;scripts&quot;: {
    &quot;vscode:prepublish&quot;: &quot;npm run compile&quot;,
    &quot;compile&quot;: &quot;node esbuild.js&quot;,
    &quot;watch&quot;: &quot;node esbuild.js --watch&quot;,
    &quot;package&quot;: &quot;vsce package&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@types/node&quot;: &quot;^20.11.0&quot;,
    &quot;@types/vscode&quot;: &quot;^1.85.0&quot;,
    &quot;@vscode/vsce&quot;: &quot;^3.9.1&quot;,
    &quot;esbuild&quot;: &quot;^0.20.0&quot;,
    &quot;sharp&quot;: &quot;^0.34.5&quot;,
    &quot;typescript&quot;: &quot;^5.3.0&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Key things to understand:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Field &lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;publisher&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Must match your Marketplace publisher ID exactly&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;engines.vscode&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Minimum VS Code version required&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;activationEvents: []&lt;/code&gt; &lt;/td&gt;&lt;td&gt;With modern VS Code, contributed commands/views can activate the extension when used&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;contributes.viewsContainers&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Registers the Activity Bar icon&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;contributes.views&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Registers the webview panel inside the sidebar&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;vscode:prepublish&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Script that runs before &lt;code&gt;vsce package&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-set-up-esbuild&quot;&gt;Step 3 — Set Up esbuild&lt;/h3&gt;
&lt;p&gt;Instead of the default &lt;code&gt;tsc&lt;/code&gt; compiler, this extension uses &lt;strong&gt;esbuild&lt;/strong&gt; for fast bundling. Create &lt;code&gt;esbuild.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const esbuild = require('esbuild');

const watch = process.argv.includes('--watch');

const buildOptions = {
  entryPoints: ['src/extension.ts'],
  bundle: true,
  outfile: 'out/extension.js',
  external: ['vscode'],          // vscode is provided by the host, never bundle it
  format: 'cjs',
  platform: 'node',
  target: 'node18',
  sourcemap: true,
  minify: !watch,
};

if (watch) {
  esbuild.context(buildOptions).then(ctx =&amp;gt; {
    ctx.watch();
    console.log('Watching for changes...');
  });
} else {
  esbuild.build(buildOptions).then(() =&amp;gt; console.log('Build complete'));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Always add &lt;code&gt;vscode&lt;/code&gt; to &lt;code&gt;external&lt;/code&gt;. It is injected by VS Code at runtime and must never be bundled.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-4-write-the-extension-entry-point&quot;&gt;Step 4 — Write the Extension Entry Point&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/extension.ts&lt;/code&gt; is the file VS Code calls when the extension activates. It registers commands and the sidebar webview provider:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import * as vscode from 'vscode';
import { LogViewerPanel, LogViewerSidebarProvider } from './logViewerPanel';

export function activate(context: vscode.ExtensionContext) {
  // Register the sidebar webview (Activity Bar panel)
  const sidebarProvider = new LogViewerSidebarProvider(context.extensionUri);
  context.subscriptions.push(
    vscode.window.registerWebviewViewProvider('amazonq-logviewer.viewer', sidebarProvider)
  );

  // Command: open full editor panel
  context.subscriptions.push(
    vscode.commands.registerCommand('amazonq-logviewer.open', () =&amp;gt; {
      LogViewerPanel.createOrShow(context.extensionUri);
    })
  );

  // Command: refresh data
  context.subscriptions.push(
    vscode.commands.registerCommand('amazonq-logviewer.refresh', () =&amp;gt; {
      LogViewerPanel.currentPanel?.refresh();
      sidebarProvider.refresh();
    })
  );
}

export function deactivate() {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two patterns to note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Push to&lt;/strong&gt; &lt;code&gt;context.subscriptions&lt;/code&gt; — VS Code automatically disposes these when the extension deactivates, preventing memory leaks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;deactivate()&lt;/code&gt; — called when VS Code shuts down or the extension is disabled. Leave it empty if you have nothing to clean up.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-5-read-local-log-files-logprovider-ts-&quot;&gt;Step 5 — Read Local Log Files (&lt;code&gt;logProvider.ts&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;This class handles all filesystem access. It resolves the correct log paths per OS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';

export class LogProvider {
  private logBase: string;
  private historyDir: string;

  constructor() {
    const home = os.homedir();
    const platform = os.platform();

    if (platform === 'win32') {
      const appdata = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
      this.logBase = path.join(appdata, 'Code', 'logs');
    } else if (platform === 'darwin') {
      this.logBase = path.join(home, 'Library', 'Application Support', 'Code', 'logs');
    } else {
      this.logBase = path.join(home, '.config', 'Code', 'logs');
    }

    this.historyDir = path.join(home, '.aws', 'amazonq', 'history');
  }

  // ... getSessionLogs() and getChatHistoryFiles() methods
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log paths by OS:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;OS &lt;/th&gt;&lt;th&gt;Extension Logs &lt;/th&gt;&lt;th&gt;Chat History&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Windows &lt;/td&gt;&lt;td&gt;&lt;code&gt;%APPDATA%\Code\logs\...\Amazon Q Logs.log&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;~\.aws\amazonq\history\&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;macOS &lt;/td&gt;&lt;td&gt;&lt;code&gt;~/Library/Application Support/Code/logs/...&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.aws/amazonq/history/&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Linux &lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.config/Code/logs/...&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.aws/amazonq/history/&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-6-build-the-webview-panel-logviewerpanel-ts-&quot;&gt;Step 6 — Build the Webview Panel (&lt;code&gt;logViewerPanel.ts&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;VS Code extensions can render arbitrary HTML inside &lt;strong&gt;WebviewPanel&lt;/strong&gt; (full editor tab) or &lt;strong&gt;WebviewView&lt;/strong&gt; (sidebar). Both are used here.&lt;/p&gt;
&lt;h4 id=&quot;security-content-security-policy-nonce&quot;&gt;Security: Content Security Policy + Nonce&lt;/h4&gt;
&lt;p&gt;Every webview must set a strict CSP. A &lt;strong&gt;nonce&lt;/strong&gt; (random string per render) is used to allow only your inline scripts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;function getNonce(): string {
  let text = '';
  const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  for (let i = 0; i &amp;lt; 32; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CSP meta tag in the HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;meta http-equiv=&quot;Content-Security-Policy&quot;
  content=&quot;default-src 'none';
           style-src 'nonce-${nonce}';
           script-src 'nonce-${nonce}';&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;two-way-messaging&quot;&gt;Two-Way Messaging&lt;/h4&gt;
&lt;p&gt;The webview and extension communicate via &lt;code&gt;postMessage&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Extension → Webview: send data
panel.webview.postMessage({ command: 'dataLoaded', historyFiles, logSessions });

// Webview → Extension: request data
panel.webview.onDidReceiveMessage(message =&amp;gt; {
  if (message.command === 'loadData') {
    const data = logProvider.loadAllData();
    panel.webview.postMessage({ command: 'dataLoaded', ...data });
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the webview HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const vscode = acquireVsCodeApi();

// Send message to extension
vscode.postMessage({ command: 'loadData' });

// Receive message from extension
window.addEventListener('message', event =&amp;gt; {
  if (event.data.command === 'dataLoaded') {
    renderSessions(event.data.historyFiles, event.data.logSessions);
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;sidebar-provider&quot;&gt;Sidebar Provider&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;export class LogViewerSidebarProvider implements vscode.WebviewViewProvider {
  resolveWebviewView(webviewView: vscode.WebviewView, ...) {
    webviewView.webview.options = {
      enableScripts: true,
      localResourceRoots: [vscode.Uri.joinPath(this._extensionUri, 'resources')]
    };
    webviewView.webview.html = getViewerHtml(getNonce());
    // ... message handler
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-7-add-icons&quot;&gt;Step 7 — Add Icons&lt;/h3&gt;
&lt;p&gt;VS Code requires icons in specific formats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Marketplace icon&lt;/strong&gt;: &lt;code&gt;resources/icon.png&lt;/code&gt; — 128×128 PNG, referenced in &lt;code&gt;package.json&lt;/code&gt; as &lt;code&gt;&quot;icon&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activity Bar icon&lt;/strong&gt;: SVG file — VS Code tints it automatically to match the theme; keep it a simple monochrome shape&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;viewsContainers&quot;: {
  &quot;activitybar&quot;: [
    {
      &quot;id&quot;: &quot;amazonq-logviewer&quot;,
      &quot;title&quot;: &quot;Q Logs&quot;,
      &quot;icon&quot;: &quot;resources/icon-sidebar-dark.svg&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Activity Bar icons are always rendered as monochrome by VS Code regardless of the SVG colors. Design them as single-color silhouettes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-8-configure-vscodeignore&quot;&gt;Step 8 — Configure &lt;code&gt;.vscodeignore&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.vscodeignore&lt;/code&gt; works like &lt;code&gt;.gitignore&lt;/code&gt; but for the packaged &lt;code&gt;.vsix&lt;/code&gt; file. Exclude everything that isn't needed at runtime:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;.vscode/**
node_modules/**
src/**
esbuild.js
tsconfig.json
**/*.map
**/*-b64.txt
resources/screenshots/*.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep in the package:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;out/extension.js&lt;/code&gt; (compiled bundle)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;resources/&lt;/code&gt; (icons used by the extension)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;LICENSE&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-9-test-locally&quot;&gt;Step 9 — Test Locally&lt;/h3&gt;
&lt;p&gt;Press &lt;strong&gt;F5&lt;/strong&gt; in VS Code to launch the &lt;strong&gt;Extension Development Host&lt;/strong&gt; — a second VS Code window with your extension loaded.&lt;/p&gt;
&lt;p&gt;You'll see the Q Logs icon appear in the Activity Bar:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/siddheshp/q-log-session-viewer-assets/main/screenshots/sessions-view.png&quot; alt=&quot;Activity Bar Icon&quot;&gt;&lt;/p&gt;
&lt;p&gt;Iterate quickly with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm run watch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;esbuild rebuilds in milliseconds on every save. Reload the Extension Development Host with &lt;strong&gt;Ctrl+R&lt;/strong&gt; (or &lt;strong&gt;Cmd+R&lt;/strong&gt; on Mac) to pick up changes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-10-package-the-extension&quot;&gt;Step 10 — Package the Extension&lt;/h3&gt;
&lt;p&gt;Install &lt;code&gt;vsce&lt;/code&gt; (the VS Code Extension CLI) if you haven't already:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g @vscode/vsce
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then package:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vsce package
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces a &lt;code&gt;.vsix&lt;/code&gt; file (e.g. &lt;code&gt;q-log-session-viewer-0.1.1.vsix&lt;/code&gt;). You can install it locally to test the final artifact:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;code --install-extension q-log-session-viewer-0.1.1.vsix
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-11-create-a-publisher-account&quot;&gt;Step 11 — Create a Publisher Account&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/manage&quot;&gt;https://marketplace.visualstudio.com/manage&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sign in with a Microsoft account&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Create publisher&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose a publisher ID (e.g. &lt;code&gt;SiddheshPrabhugaonkar&lt;/code&gt;) — this must match the &lt;code&gt;&quot;publisher&quot;&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; exactly&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You also need a &lt;strong&gt;Personal Access Token (PAT)&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.azure.com&quot;&gt;https://dev.azure.com&lt;/a&gt; → your organization → &lt;strong&gt;User Settings&lt;/strong&gt; → &lt;strong&gt;Personal Access Tokens&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;New Token&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set scope to &lt;strong&gt;Marketplace → Manage&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the token — you won't see it again&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Authenticate &lt;code&gt;vsce&lt;/code&gt; with your token:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vsce login SiddheshPrabhugaonkar
# Paste your PAT when prompted
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-12-write-a-good-readme&quot;&gt;Step 12 — Write a Good README&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;README.md&lt;/code&gt; in your extension folder becomes the &lt;strong&gt;Marketplace listing page&lt;/strong&gt;. Make it count:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lead with what the extension does and who it's for&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Include screenshots (host them on GitHub or a CDN — relative paths don't work on the Marketplace)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;List features, commands, and requirements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a disclaimer if your extension reads data from another product&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Screenshot URLs must be absolute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![Sessions View](https://raw.githubusercontent.com/youruser/your-assets-repo/main/screenshots/sessions-view.png)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Create a separate public GitHub repo just for assets (screenshots, GIFs). This keeps your extension repo clean and the URLs stable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-13-publish-to-the-marketplace&quot;&gt;Step 13 — Publish to the Marketplace&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vsce publish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it. &lt;code&gt;vsce&lt;/code&gt; will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm run vscode:prepublish&lt;/code&gt; (which runs &lt;code&gt;npm run compile&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Package the &lt;code&gt;.vsix&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upload it to the Marketplace&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To publish a specific version bump:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vsce publish patch   # 0.1.0 → 0.1.1
vsce publish minor   # 0.1.0 → 0.2.0
vsce publish major   # 0.1.0 → 1.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a few minutes your extension appears at: &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=SiddheshPrabhugaonkar.q-log-session-viewer&amp;amp;ssr=false#review-details&quot;&gt;https://marketplace.visualstudio.com/items?itemName=SiddheshPrabhugaonkar.q-log-session-viewer&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-14-update-the-extension&quot;&gt;Step 14 — Update the Extension&lt;/h3&gt;
&lt;p&gt;For subsequent releases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Make your code changes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update &lt;code&gt;CHANGELOG&lt;/code&gt; / release notes in &lt;code&gt;README.md&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;vsce publish patch&lt;/code&gt; (or &lt;code&gt;minor&lt;/code&gt;/&lt;code&gt;major&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Marketplace auto-notifies users who have the extension installed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;project-file-structure-final-&quot;&gt;Project File Structure (Final)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;VSCodeExtention/
├── resources/
│   ├── icon.png                  ← Marketplace icon (128×128 PNG)
│   ├── icon-sidebar-dark.svg     ← Activity Bar icon
│   └── icon-sidebar-light.svg
├── src/
│   ├── extension.ts              ← activate() / deactivate()
│   ├── logProvider.ts            ← filesystem reads
│   └── logViewerPanel.ts         ← WebviewPanel + WebviewView + HTML
├── .vscodeignore
├── esbuild.js
├── package.json
├── tsconfig.json
└── README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;key-concepts-recap&quot;&gt;Key Concepts Recap&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Concept &lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;contributes.viewsContainers&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Adds an icon to the Activity Bar&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;contributes.views&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Registers a panel inside that container&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;WebviewPanel&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Full editor tab with custom HTML&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;WebviewViewProvider&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Sidebar panel with custom HTML&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;postMessage&lt;/code&gt; / &lt;code&gt;onDidReceiveMessage&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Two-way communication between extension and webview&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Nonce + CSP &lt;/td&gt;&lt;td&gt;Security: prevents XSS in webviews&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;context.subscriptions&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Automatic cleanup on deactivation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;vsce package&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Creates the installable &lt;code&gt;.vsix&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;vsce publish&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Uploads to the VS Code Marketplace&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;common-gotchas&quot;&gt;Common Gotchas&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;vscode&lt;/code&gt; &lt;strong&gt;must be in&lt;/strong&gt; &lt;code&gt;external&lt;/code&gt; in your bundler config — never bundle it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Marketplace icon must be PNG&lt;/strong&gt;, not SVG&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Screenshot URLs in README must be absolute&lt;/strong&gt; — relative paths break on the Marketplace page&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Publisher ID in&lt;/strong&gt; &lt;code&gt;package.json&lt;/code&gt; &lt;strong&gt;must exactly match&lt;/strong&gt; your Marketplace publisher account&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activity Bar SVG icons are always monochrome&lt;/strong&gt; — VS Code tints them; don't rely on color&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSP&lt;/strong&gt; &lt;code&gt;default-src 'none'&lt;/code&gt; — be explicit about what your webview is allowed to load; no external CDNs unless you add them to the CSP&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://code.visualstudio.com/api&quot;&gt;VS Code Extension API&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://code.visualstudio.com/api/extension-guides/webview&quot;&gt;Webview API Guide&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://code.visualstudio.com/api/working-with-extensions/publishing-extension&quot;&gt;Publishing Extensions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/microsoft/vscode-vsce&quot;&gt;vsce CLI Reference&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=SiddheshPrabhugaonkar.q-log-session-viewer&amp;amp;ssr=false#review-details&quot;&gt;Q Log Session Viewer on Marketplace&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Built by Siddhesh Prabhugankar — Microsoft Certified Trainer &amp;amp; AI Consultant&lt;/em&gt;&lt;br&gt;
&lt;em&gt;GitHub:&lt;/em&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/siddheshp&quot;&gt;&lt;em&gt;github.com/siddheshp&lt;/em&gt;&lt;/a&gt; &lt;em&gt;· LinkedIn:&lt;/em&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/siddheshprabhugaonkar&quot;&gt;&lt;em&gt;linkedin.com/in/siddheshprabhugaonkar&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/16422/how-i-built-and-published-a-vs-code-extension-to-the-marketplace</guid>
<pubDate>Sat, 02 May 2026 21:36:11 +0000</pubDate>
</item>
<item>
<title>How GitHub Copilot Helped Me Roast Its Own Users</title>
<link>https://coderlegion.com/11243/how-github-copilot-helped-me-roast-its-own-users</link>
<description>&lt;blockquote&gt;&lt;p&gt;What if GitHub Copilot didn’t just help you write code but judged you for it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-i-built&quot;&gt;What I Built&lt;/h3&gt;
&lt;p&gt;I had been waiting for a challenge that truly sounded like me for a long time, and that’s when the GitHub Copilot CLI Challenge showed up.&lt;/p&gt;
&lt;p&gt;Within five minutes, the idea that popped into my head was to use Copilot CLI to do something with Copilot itself.&lt;/p&gt;
&lt;p&gt;That’s when the idea of roasting the user for their GitHub Copilot usage came up.&lt;/p&gt;
&lt;p&gt;Then I started wondering how to implement it.&lt;/p&gt;
&lt;p&gt;Whether to build something that runs in the terminal but that would consume RAM.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Should I try a different approach?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s when the idea of a VS Code extension popped up (GitHub CLI actually helped me there ).&lt;/p&gt;
&lt;p&gt;This was my first time using an AI pair programmer to help me build a project completely, because I had never built, or even thought about creating a VS Code extension before.&lt;/p&gt;
&lt;p&gt;The roasts are static for now and don’t use AI, because I couldn’t afford an API key .&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;demo&quot;&gt;Demo&lt;/h3&gt;
&lt;p&gt;Click the following link:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=VigneshJ.copilot-roaster&quot;&gt;Copilot Roaster Extension&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Install the extension using the link.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p8kd7cmoa87g3e4bp5dn.png&quot; alt=&quot;Extenstion&quot;&gt;&lt;/p&gt;
&lt;p&gt;And your setup is complete.&lt;/p&gt;
&lt;p&gt;Then, open any sample file to test it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z8739gfssiuep4wghrca.png&quot; alt=&quot;Sample File&quot;&gt;&lt;/p&gt;
&lt;p&gt;Ask GitHub Copilot to write something and accept its suggestion to see the magic ✨&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y256grfxydcbfngkdcru.png&quot; alt=&quot;Copilot Chat&quot;&gt;&lt;/p&gt;
&lt;p&gt;Find my demo below:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.loom.com/share/e9206dad95044bdba7127ad198710722&quot;&gt;Demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find the GitHub repository &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Vignesh025/copilot-roast-extension&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Feel free to fork, modify, and upgrade it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;my-experience-with-github-copilot-cli&quot;&gt;My Experience with GitHub Copilot CLI&lt;/h3&gt;
&amp;lt;!-- Explain how you used GitHub Copilot CLI while building your project and how it impacted your development experience. --&amp;gt;
&lt;p&gt;This was my first time using an AI pair programmer in the CLI. I was really excited to see how it worked. After installing it and typing copilot in the terminal, it felt like magic when this showed up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vre4uytrbplpzkp3dzed.png&quot; alt=&quot;Copilot CLI&quot;&gt;&lt;/p&gt;
&lt;p&gt;I started wondering how it would work,  which subscription it would use and which profile it would be linked to.&lt;/p&gt;
&lt;p&gt;While thinking about all this, I typed a prompt, and that’s when I got an error saying I needed to log in to proceed further.&lt;/p&gt;
&lt;p&gt;First, we need to use /login to sign in with our GitHub account.&lt;/p&gt;
&lt;p&gt;After that, everything was smooth. The suggestions were almost exactly what I expected (maybe my prompt was too good ).&lt;/p&gt;
&lt;p&gt;Then I tweaked things a bit, and tadaaa &lt;/p&gt;
&lt;p&gt;The extension was ready and looked cool.&lt;/p&gt;
&lt;p&gt;I added a few more roasts just to make sure the same ones didn’t repeat.&lt;/p&gt;
&lt;p&gt;Then I published it on the Marketplace.&lt;/p&gt;
&lt;p&gt;GitHub Copilot CLI provided me with the exact steps to publish the extension, because I had no clue how to do it and the whole process was smooth as well.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;So go ahead, install the extension, let Copilot write your code, and brace yourself… because this time, you’re the one getting reviewed &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Follow me on&lt;/em&gt;&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/vigneshj-&quot;&gt;&lt;em&gt;Linkedin&lt;/em&gt;&lt;/a&gt; | &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Vignesh025&quot;&gt;&lt;em&gt;GitHub&lt;/em&gt;&lt;/a&gt; | &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/vigneshtwt_&quot;&gt;&lt;em&gt;Twitter&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Subscribe to my &lt;a rel=&quot;nofollow&quot; href=&quot;https://thedeveloperstack.substack.com&quot;&gt;newsletter&lt;/a&gt; and get articles delivered straight to your inbox.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11243/how-github-copilot-helped-me-roast-its-own-users</guid>
<pubDate>Sun, 08 Feb 2026 07:19:53 +0000</pubDate>
</item>
</channel>
</rss>