Back
Chapter 13

Wrapping Up Our Web Extension

View Source Code

Let’s start with our popup

If you notice when I click on the icon the focus is not automatically in the textarea but I have to click once and that’s something super easy to fix

Auto-focus the textarea

So maybe I go over to the popup’s code base and here in the load existing note method I can just add the focus to the noteText element which is our textarea

const loadExistingNote = async () => {
  const [tab] = await browser.tabs.query({ 
    active: true, 
    currentWindow: true 
  });
  
  if (!tab.url) return;
  
  const pageKey = getPageKey(tab.url);
  const notesStorage = await storage.getItem('sync:notesStorage');
  
  if (notesStorage?.[pageKey]) {
    noteText.value = notesStorage[pageKey].note;
  }
  
  noteText.focus(); // Focus after loading
};

Let’s see I saved my changes should be automatically updated

I click and you can see that the cursor is blinking in the right place

Perfect

Moving HTML to the HTML file

Now that I’m already in the popup file I can see that the WXT framework injected the HTML code this way but I think it’s totally unnecessary

So instead I will just take it out from here and we’ll paste it in the index.html directly

<!-- popup/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>TabNotes</title>
</head>
<body>
  <div class="sticky-note">
    <form class="note-form">
      <textarea 
        id="note-text"
        placeholder="Write your note here..."
      ></textarea>
    </form>
  </div>
  <script type="module" src="./main.ts"></script>
</body>
</html>

The only thing left to do is that I don’t need that app in my styles either so let’s get rid of it

I can verify that it’s still working without any changes

Fixing z-index issues

Next I head over to the style of my ribbon which is in the content folder and set a very high z-index

I know it’s not nice but let me show you before

If you open let’s say YouTube and you set a note you might be able to see down here but sometimes the ribbon gets hidden behind other elements

So let’s fix that

#tabnotes-ribbon {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 100000; /* High enough to appear above most content */
  /* other styles */
}

Perfect

Now the ribbon always appears on top

Handling empty notes

When users delete all text from a note we should remove it from storage

noteText.addEventListener('keyup', async () => {
  const text = noteText.value.trim();
  
  if (text === '') {
    // Delete the note if empty
    delete notesStorage[pageKey];
  } else {
    // Save the note
    notesStorage[pageKey] = {
      note: text,
      createdAt: notesStorage[pageKey]?.createdAt || new Date().toISOString(),
      updatedAt: new Date().toISOString()
    };
  }
  
  await storage.setItem('sync:notesStorage', notesStorage);
  await changeIcon(tab.id);
  await sendNoteUpdateMessage(tab.id);
});

Perfect

Now empty notes are properly cleaned up

Final checklist

Before submitting make sure

  • All features work as expected
  • No console errors or warnings
  • Icons display correctly at all sizes
  • Extension name and description are accurate
  • Version number is set correctly
  • Permissions are minimized to what’s needed
  • UI elements are properly styled
  • Text inputs auto-focus when appropriate
  • Storage is handled correctly empty values removed
  • All edge cases are handled

Testing

Test your extension thoroughly

  1. Fresh install remove and reinstall to test the onboarding flow
  2. Multiple tabs switch between tabs with and without notes
  3. Empty states test behavior when no notes exist
  4. Storage limits add many notes to test performance
  5. Different websites test on various sites with different layouts
  6. Delete operations ensure notes are properly removed

What’s next

We’ve polished our extension with final UX improvements

With these final touches TabNotes is ready for submission to the Chrome Web Store

In the next chapter we’ll learn how to submit it