Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add browser extension to project #37

Merged
merged 43 commits into from
Dec 21, 2024

Conversation

nickytonline
Copy link
Contributor

@nickytonline nickytonline commented Dec 18, 2024

This PR introduces the unsight.dev browser extension to the project. The browser extension when installed, enhances GitHub issue pages with a Similar Issues section in the issue header section.

Note, don't be intimidated by the number of files. The majority of them are boilerplate from the web extension template.

The main files that are important in regards to functionality are currently:

  • packages/webext/src/contentScripts/index.ts (wire up browser extension in the issue header)
  • packages/webext/src/contentScripts/views/App.vue (the actual Vue app)
  • packages/webext/src/components/SimilarIssue.vue (only component currently being used)

I'm still quite new to Vue, so I'm probably holding it wrong. 😅 Any feedback there is very welcome.

Closes #4

For some reason, in c070f9e, I can't access import.meta.env.VITE_UNSIGHT_DOT_DEV_BASE_URL, when it's set in a .env or .env.local file.

I've tried VITE_UNSIGHT_DOT_DEV_BASE_URL, VUE_APP_UNSIGHT_DOT_DEV_BASE_URL, but they never get recognized, but the other vars under import.meta.env do, e.g.

CleanShot 2024-12-19 at 16 38 55

I've also tried explicitly adding this to the vite config, but no difference.

export default defineConfig({
  envPrefix: 'VITE_',
  // ...existing code...
})

These are usually picked up by Vite, so not sure what's up. This is the only blocker at the moment, and I'm sure it's something super silly.

Screenshots & Recordings

Initial POC loading real data, although we probably want the clustered data where the current issue is part of that cluster.

CleanShot 2024-12-19 at 13 21 36

Steps to get the browser extension working locally

  1. Get the latest of this branch by running gh co 37
  2. Run pnpm i to install the latest dependencies
  3. Run pnpm -F webext dev to run the web extension project.
  4. Add the browser extension to your browser by loading it as an unpacked browser extension. In Chromium based browsers that support extensions (like Chrome and Edge), navigate to chrome://extensions/. Enabled developer mode, then click the "Load Unpacked" button.

CleanShot 2024-12-18 at 19 02 57

  1. The folder to open unpacked is packages/webext/extension
  2. The browser extension is now loaded.
  3. Navigate to Race condition in api/websocket dispatch subscription handler directus/directus#23508
  4. Open the Similar Issues details
  5. Notice the list of issues

CleanShot 2024-12-19 at 13 21 36

Copy link

coderabbitai bot commented Dec 18, 2024

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • packages/webext/package.json is excluded by !**/package.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces a comprehensive web browser extension for the Unsight.dev project, focusing on enhancing GitHub issue pages by displaying similar issues inline. The extension is built using Vue.js, Vite, and modern web technologies, with support for multiple browsers including Chrome and Firefox. It includes functionality to fetch and display related issues dynamically, along with configuration for development, testing, and deployment.

Changes

File/Directory Change Summary
.gitignore Added entries for test-results, .eslintcache, and TypeScript auto-generated files
packages/webext/ Added comprehensive web extension project structure with Vue components, scripts, configurations
packages/webext/src/contentScripts/ Implemented content script to inject similar issues on GitHub issue pages
packages/webext/src/components/SimilarIssue.vue Created component for rendering individual similar issues
packages/webext/tests/ Added unit and end-to-end testing configurations

Assessment against linked issues

Objective Addressed Explanation
Display related issues inline
Browser extension functionality
GitHub issue page integration

Sequence Diagram

sequenceDiagram
    participant User
    participant GitHubIssuePage
    participant ContentScript
    participant UnsightAPI
    
    User->>GitHubIssuePage: Visits GitHub issue page
    ContentScript->>UnsightAPI: Fetch similar issues
    UnsightAPI-->>ContentScript: Return similar issues
    ContentScript->>GitHubIssuePage: Inject similar issues section
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@nickytonline
Copy link
Contributor Author

Looks like Code Rabbit cleaned up some code from the web extension template in ea7dbdd.

@nickytonline
Copy link
Contributor Author

nickytonline commented Dec 18, 2024

I've used GitHub's relative-time web component before, but I ran into issues with it in my SimilarIssue.vue component. I imported it and it kept reporting that the custom elements registry was null, so it couldn't define it. I removed it for now, but will look into that tomorrow.

Added in 377ec78

@nickytonline
Copy link
Contributor Author

@danielroe, I have this loading real data now. There are some URLs still hard-coded/cleanup still, but things are shaping up.

We probably want to load issues though where the current issue is a part of that cluster. We probably need a different endpoint for that.

Also, is it possible that an issue could be part of multiple clusters? Possibly?

@nickytonline
Copy link
Contributor Author

I've also noticed that Code Rabbit and the eslint rules in the project seem to be at odds as it keeps auto fixing files.

@danielroe
Copy link
Owner

danielroe commented Dec 19, 2024

The autofix is caused by running pnpm lint --fix in the root of the workspace:

- name: 🔠 Fix lint errors
run: pnpm lint --fix

You can ignore any formatting/linting suggestions that Coderabbit may give.

To prevent autofix, just make sure to lint the code by running pnpm lint --fix from the root directory before pushing.

@nickytonline nickytonline force-pushed the web-ext branch 2 times, most recently from fff2e42 to e7a8e9f Compare December 19, 2024 21:20
@nickytonline nickytonline marked this pull request as ready for review December 19, 2024 21:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

🧹 Nitpick comments (28)
packages/webext/src/sidepanel/main.ts (2)

1-4: Consider adding file extension to styles import

The import statement for styles lacks a file extension, which might cause issues with certain bundlers or make it less clear what type of file is being imported.

-import '../styles'
+import '../styles.css'  // or appropriate extension (.scss, .less, etc.)

6-8: Add error handling for mount operation

The current implementation assumes the '#app' element always exists. Consider adding error handling to provide a better developer experience when the mount fails.

 const app = createApp(App)
 setupApp(app)
-app.mount('#app')
+try {
+  app.mount('#app')
+} catch (error) {
+  console.error('Failed to mount Vue application:', error)
+  // Consider adding telemetry or logging here
+}
packages/webext/vite.config.background.mts (2)

22-23: Consider security implications of source maps

The current configuration allows source maps in development but disables them in production, which is good. However, emptyOutDir: false might retain old build artifacts. Consider enabling emptyOutDir to ensure clean builds.

-    emptyOutDir: false,
+    emptyOutDir: true,

30-33: Consider adding content hashing for better cache control

The static filename index.mjs might lead to caching issues during updates. Consider adding a content hash to the filename.

     output: {
-      entryFileNames: 'index.mjs',
+      entryFileNames: 'index.[hash].mjs',
       extend: true,
     },
packages/webext/e2e/fixtures.ts (1)

42-48: Validate file I/O overhead in testing pipelines.

Reading and parsing the manifest file for each test run introduces slight overhead. Though acceptable in most cases, consider caching its contents if you encounter performance issues in large test suites.

packages/webext/src/composables/useWebExtensionStorage.ts (1)

142-146: Assess performance implications of repeatedly processing storage changes.

The listener applies a full read on every storage change event. If the extension processes frequent updates or large datasets, consider batching or selectively handling changes for improved performance.

packages/webext/.gitpod.Dockerfile (1)

6-7: Enhance Dockerfile with best practices

Consider these improvements:

  1. Pin the Firefox version for reproducible builds
  2. Clean up apt cache to reduce image size
  3. Combine RUN commands to reduce layers

Apply these modifications:

-RUN apt-get update                                            \
-    && apt-get install -y firefox
+RUN apt-get update \
+    && apt-get install -y firefox=* \
+    && apt-get clean \
+    && rm -rf /var/lib/apt/lists/*
packages/webext/modules.d.ts (2)

1-7: Enhance type safety and documentation for Vue augmentation

The $app interface could benefit from:

  1. More specific typing for the context property
  2. JSDoc documentation explaining its purpose
  3. Validation for allowed context values

Consider this enhanced version:

/**
 * Augments Vue's runtime to provide application context.
 * @property {Object} $app - Global application context
 * @property {AppContext} $app.context - Indicates the current view context
 */
declare module '@vue/runtime-core' {
  type AppContext = 'sidebar' | 'popup' | 'options'
  
  interface ComponentCustomProperties {
    $app: {
      context: AppContext
    }
  }
}

9-10: Consider adding comprehensive type declarations

The empty export is correctly used to treat this as a module. However, consider expanding this file to include other extension-specific type declarations that might be needed across the codebase.

You might want to declare types for:

  • Extension message formats
  • Storage schema
  • API responses for similar issues
packages/webext/src/popup/main.ts (1)

4-4: Consider adding type safety to styles import

The styles import could benefit from explicit typing to ensure type safety across the application.

Consider adding a type declaration:

declare module '../styles' {
  const styles: void
  export default styles
}
packages/webext/src/options/main.ts (1)

1-8: Consider creating a shared bootstrap utility

This file is nearly identical to popup/main.ts. Consider creating a shared bootstrap utility to reduce code duplication.

Example implementation:

// src/utils/bootstrap.ts
import { App, createApp } from 'vue'
import { setupApp } from '~/logic/common-setup'
import '../styles'

export function bootstrapVueApp(RootComponent: Component) {
  const app = createApp(RootComponent)
  setupApp(app)
  app.mount('#app')
  return app
}

Then both popup and options could simply use:

import { bootstrapVueApp } from '~/utils/bootstrap'
import App from './Options.vue' // or Popup.vue
bootstrapVueApp(App)
packages/webext/src/components/__tests__/Logo.test.ts (1)

5-11: Enhance test coverage for the Logo component

The current test only verifies that the component renders without errors. Consider adding:

  • Snapshot testing to track visual changes
  • Specific assertions about logo structure and attributes
  • Edge cases and negative scenarios

Here's a suggested enhancement:

 describe('logo component', () => {
   it('should render', () => {
     const wrapper = mount(Logo)
-
     expect(wrapper.html()).toBeTruthy()
   })
+  
+  it('should match snapshot', () => {
+    const wrapper = mount(Logo)
+    expect(wrapper.element).toMatchSnapshot()
+  })
+
+  it('should have correct attributes', () => {
+    const wrapper = mount(Logo)
+    const img = wrapper.find('img')
+    expect(img.exists()).toBe(true)
+    expect(img.attributes('alt')).toBe('Logo')
+  })
 })
packages/webext/scripts/manifest.ts (1)

10-10: Consider making the script more modular

The immediate invocation of writeManifest() makes the script less reusable and harder to test. Consider making this conditional or moving it to a separate entry point.

-writeManifest()
+if (require.main === module) {
+  writeManifest()
+    .catch((error) => {
+      console.error(error)
+      process.exit(1)
+    })
+}
packages/webext/unocss.config.ts (1)

4-13: Consider enhancing UnoCSS configuration

The current configuration includes basic presets but could benefit from additional customisation for the browser extension context.

Consider adding:

 export default defineConfig({
   presets: [
     presetUno(),
     presetAttributify(),
     presetIcons(),
   ],
   transformers: [
     transformerDirectives(),
   ],
+  // Safelist for dynamically generated classes
+  safelist: [
+    'bg-primary',
+    'text-secondary',
+  ],
+  // Custom shortcuts for common patterns
+  shortcuts: {
+    'btn': 'px-4 py-2 rounded-lg',
+    'card': 'bg-white shadow-md rounded-lg p-4',
+  },
+  // Theme customisation
+  theme: {
+    colors: {
+      primary: '#4F46E5',
+      secondary: '#7C3AED',
+    },
+  },
 })
packages/webext/shim.d.ts (1)

4-9: Brilliant implementation of type-safe protocols!

The protocol definitions are well-structured and provide proper type safety. However, consider adding brief JSDoc comments for each protocol to document their specific use cases.

Consider adding documentation like this:

 export interface ProtocolMap {
   // define message protocol types
   // see https://github.com/antfu/webext-bridge#type-safe-protocols
+  /** Protocol for handling previous tab information */
   'tab-prev': { title: string | undefined }
+  /** Protocol for retrieving current tab details */
   'get-current-tab': ProtocolWithReturn<{ tabId: number }, { title?: string }>
 }
packages/webext/src/env.ts (1)

1-8: Consider making the forbidden protocols list more maintainable

The list of forbidden protocols is comprehensive, but consider moving it to a configuration file for easier maintenance and reusability across the extension.

Consider creating a separate config file:

// config/protocols.ts
export const forbiddenProtocols = [
  'chrome-extension://',
  'chrome-search://',
  // ... other protocols
] as const;
packages/webext/playwright.config.ts (1)

6-15: Consider enhancing the test configuration for CI/CD environments

Whilst the current configuration is suitable for local development, consider adding CI/CD-specific settings.

Consider these additions:

 export default defineConfig({
   testDir: './e2e',
   retries: 2,
+  timeout: 30000, // Add timeout for CI environments
+  workers: process.env.CI ? 1 : undefined, // Limit workers in CI
   webServer: {
     command: 'npm run dev',
     url: 'http://localhost:3303/popup/main.ts',
     reuseExistingServer: true,
+    timeout: 120000, // Add server startup timeout
   },
+  use: {
+    trace: process.env.CI ? 'on-first-retry' : 'off',
+  },
 })
packages/webext/.gitpod.yml (1)

4-17: Consider adding error handling to task commands

The task commands would benefit from error handling to ensure the development environment is properly initialised.

 tasks:
   - init: pnpm install && pnpm run build
     name: dev
     command: |
+      set -e  # Exit on error
       gp sync-done ready
       pnpm run dev
   - name: pnpm start:chromium
     command: |
+      set -e  # Exit on error
       gp sync-await ready
       gp ports await 6080
       gp preview $(gp url 6080)
packages/webext/scripts/utils.ts (2)

6-6: Enhance path resolution robustness

The path resolution utility could benefit from additional error handling and path normalisation.

-export const r = (...args: string[]) => resolve(__dirname, '..', ...args)
+export const r = (...args: string[]) => {
+  const path = resolve(__dirname, '..', ...args)
+  // Ensure path doesn't escape base directory
+  if (!path.startsWith(resolve(__dirname, '..')))
+    throw new Error('Path traversal detected')
+  return path
+}

7-8: Add type assertions for environment variables

Environment variables should have proper type assertions to ensure type safety.

-export const isDev = process.env.NODE_ENV !== 'production'
-export const isFirefox = process.env.EXTENSION === 'firefox'
+export const isDev = process.env.NODE_ENV as string !== 'production'
+export const isFirefox = (process.env.EXTENSION as string | undefined) === 'firefox'
packages/webext/src/logic/common-setup.ts (2)

9-10: Enhance type safety for dependency injection

The provided value should be typed to ensure type safety when using injection.

-  app.provide('app', app.config.globalProperties.$app)
+  const APP_KEY = Symbol('app')
+  app.provide<AppGlobalProperties>(APP_KEY, app.config.globalProperties.$app)

12-14: Improve documentation for plugin installation

The comments about plugin installation could be more comprehensive with TypeScript examples.

/**
 * Install additional plugins for different contexts.
 * 
 * @example
 * // Install for all contexts
 * app.use(i18n)
 * 
 * @example
 * // Install excluding content-script context
 * if (context !== 'content-script') {
 *   app.use(i18n, {
 *     locale: 'en-GB',
 *     messages: {...}
 *   })
 * }
 */
packages/webext/src/background/contentScriptHMR.ts (1)

14-17: Enhance error handling and type safety

The error handling could be more informative for debugging purposes. Additionally, the script injection lacks type safety for the error object.

Consider applying these improvements:

-  }).catch(error => console.error(error))
+  }).catch((error: unknown) => {
+    console.error('Failed to inject content script:', error)
+    if (error instanceof Error) {
+      console.error('Error details:', error.message)
+    }
+  })
packages/webext/e2e/basic.spec.ts (1)

8-9: Consider making the selector more robust

Using dynamic IDs for selectors can be fragile. Consider using data attributes for more reliable test selectors.

-  await page.locator(`#${name} button`).click()
-  await expect(page.locator(`#${name} h1`)).toHaveText('Vitesse WebExt')
+  await page.locator('[data-testid="extension-button"]').click()
+  await expect(page.locator('[data-testid="extension-heading"]')).toHaveText('Vitesse WebExt')
packages/webext/src/contentScripts/views/App.vue (1)

19-24: Enhance loading and error states

The current loading and error states lack detail and visual polish.

Consider enhancing the UI:

-    <div v-if="status === 'loading'">
-      Loading...
-    </div>
-    <div v-else-if="status === 'error'" className="text-red-700">
-      Error loading similar issues
-    </div>
+    <div v-if="status === 'loading'" class="flex items-center gap-2">
+      <div class="animate-spin h-4 w-4 border-2 border-gray-500 rounded-full" />
+      <span>Loading similar issues...</span>
+    </div>
+    <div v-else-if="status === 'error'" class="p-2 rounded bg-red-50 text-red-700">
+      <p class="font-medium">Failed to load similar issues</p>
+      <p class="text-sm">Please try again later or contact support if the issue persists.</p>
+    </div>
packages/webext/src/background/main.ts (2)

16-21: Improve error handling for side panel setup

The error handling could be more specific and include proper error logging.

 if (USE_SIDE_PANEL) {
   // @ts-expect-error missing types
   browser.sidePanel
     .setPanelBehavior({ openPanelOnActionClick: true })
-    .catch((error: unknown) => console.error(error))
+    .catch((error: unknown) => {
+      if (error instanceof Error) {
+        console.error('Failed to set side panel behaviour:', error.message)
+      } else {
+        console.error('Failed to set side panel behaviour:', error)
+      }
+    })
 }

51-63: Add error type handling for tab retrieval

The error handling in the message handler could be more specific about the type of error encountered.

 onMessage('get-current-tab', async () => {
   try {
     const tab = await browser.tabs.get(previousTabId)
     return {
       title: tab?.title,
     }
   }
-  catch {
+  catch (error) {
+    console.error('Failed to retrieve tab:', error instanceof Error ? error.message : error)
     return {
       title: undefined,
     }
   }
 })
packages/webext/README.md (1)

96-111: Enhance development setup instructions

The development setup instructions could be more detailed about browser-specific requirements and troubleshooting steps.

Add a section about browser-specific development requirements and common troubleshooting steps, particularly for Firefox's web-ext usage.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~110-~110: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...bash pnpm dev-firefox web-ext auto reload the extension when extension/ files c...

(AUTO_HYPHEN)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 652b0fe and e7a8e9f.

⛔ Files ignored due to path filters (6)
  • package.json is excluded by !package.json, !**/package.json
  • packages/webext/extension/assets/icon-512.png is excluded by !**/*.png
  • packages/webext/extension/assets/icon.svg is excluded by !**/*.svg
  • packages/webext/package.json is excluded by !**/package.json
  • packages/webext/src/assets/logo.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml
📒 Files selected for processing (45)
  • .gitignore (1 hunks)
  • packages/webext/.gitpod.Dockerfile (1 hunks)
  • packages/webext/.gitpod.yml (1 hunks)
  • packages/webext/LICENSE (1 hunks)
  • packages/webext/README.md (1 hunks)
  • packages/webext/e2e/basic.spec.ts (1 hunks)
  • packages/webext/e2e/fixtures.ts (1 hunks)
  • packages/webext/extension/manifest.json (1 hunks)
  • packages/webext/modules.d.ts (1 hunks)
  • packages/webext/playwright.config.ts (1 hunks)
  • packages/webext/scripts/manifest.ts (1 hunks)
  • packages/webext/scripts/prepare.ts (1 hunks)
  • packages/webext/scripts/utils.ts (1 hunks)
  • packages/webext/shim.d.ts (1 hunks)
  • packages/webext/src/auto-imports.d.ts (1 hunks)
  • packages/webext/src/background/contentScriptHMR.ts (1 hunks)
  • packages/webext/src/background/main.ts (1 hunks)
  • packages/webext/src/components.d.ts (1 hunks)
  • packages/webext/src/components/README.md (1 hunks)
  • packages/webext/src/components/SimilarIssue.vue (1 hunks)
  • packages/webext/src/components/__tests__/Logo.test.ts (1 hunks)
  • packages/webext/src/composables/useWebExtensionStorage.ts (1 hunks)
  • packages/webext/src/contentScripts/index.ts (1 hunks)
  • packages/webext/src/contentScripts/views/App.vue (1 hunks)
  • packages/webext/src/env.ts (1 hunks)
  • packages/webext/src/global.d.ts (1 hunks)
  • packages/webext/src/logic/common-setup.ts (1 hunks)
  • packages/webext/src/manifest.ts (1 hunks)
  • packages/webext/src/options/Options.vue (1 hunks)
  • packages/webext/src/options/index.html (1 hunks)
  • packages/webext/src/options/main.ts (1 hunks)
  • packages/webext/src/popup/Popup.vue (1 hunks)
  • packages/webext/src/popup/index.html (1 hunks)
  • packages/webext/src/popup/main.ts (1 hunks)
  • packages/webext/src/sidepanel/Sidepanel.vue (1 hunks)
  • packages/webext/src/sidepanel/index.html (1 hunks)
  • packages/webext/src/sidepanel/main.ts (1 hunks)
  • packages/webext/src/styles/index.ts (1 hunks)
  • packages/webext/src/styles/main.css (1 hunks)
  • packages/webext/src/tests/demo.spec.ts (1 hunks)
  • packages/webext/tsconfig.json (1 hunks)
  • packages/webext/unocss.config.ts (1 hunks)
  • packages/webext/vite.config.background.mts (1 hunks)
  • packages/webext/vite.config.content.mts (1 hunks)
  • packages/webext/vite.config.mts (1 hunks)
✅ Files skipped from review due to trivial changes (14)
  • .gitignore
  • packages/webext/src/options/Options.vue
  • packages/webext/src/sidepanel/Sidepanel.vue
  • packages/webext/LICENSE
  • packages/webext/src/components/README.md
  • packages/webext/src/components.d.ts
  • packages/webext/src/styles/index.ts
  • packages/webext/src/options/index.html
  • packages/webext/src/popup/index.html
  • packages/webext/src/styles/main.css
  • packages/webext/src/sidepanel/index.html
  • packages/webext/tsconfig.json
  • packages/webext/src/popup/Popup.vue
  • packages/webext/src/auto-imports.d.ts
🧰 Additional context used
🪛 LanguageTool
packages/webext/README.md

[uncategorized] ~21-~21: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...lang.org/) - type safe - 📦 Components auto importing - 🌟 [Icons](./src/c...

(AUTO_HYPHEN)


[uncategorized] ~110-~110: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...bash pnpm dev-firefox web-ext auto reload the extension when extension/ files c...

(AUTO_HYPHEN)

🪛 Markdownlint (0.37.0)
packages/webext/README.md

7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


9-9: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


11-11: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

🪛 Biome (1.9.4)
packages/webext/src/manifest.ts

[error] 87-87: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 83-83: Unexpected constant condition.

(lint/correctness/noConstantCondition)

🔇 Additional comments (9)
packages/webext/src/sidepanel/main.ts (1)

1-8: Verify the common setup implementation

The code looks good overall and follows Vue.js best practices. However, let's verify the implementation of the common setup function to ensure consistent configuration across the extension.

✅ Verification successful

Common setup implementation is consistent and properly used across extension contexts

The setupApp function in common-setup.ts provides a consistent configuration across all extension contexts (popup, sidepanel, options page, and content scripts). It:

  • Injects a global $app object with context information
  • Provides app configuration via dependency injection
  • Includes a designated location for installing additional plugins

The implementation is properly used in all relevant entry points:

  • packages/webext/src/popup/main.ts
  • packages/webext/src/sidepanel/main.ts
  • packages/webext/src/options/main.ts
  • packages/webext/src/contentScripts/index.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the implementation of common setup function
# and its usage across the codebase

# Look for the common-setup.ts file
echo "Searching for common-setup.ts implementation:"
rg -A 10 "export function setupApp"

# Check for other files using the same setup
echo "\nChecking usage of setupApp across the codebase:"
rg "setupApp\(app\)"

Length of output: 1366

packages/webext/vite.config.content.mts (1)

20-28: Verify Content Security Policy compatibility

The IIFE format is correct for content scripts, but ensure the output is compatible with the extension's Content Security Policy (CSP).

✅ Verification successful

Content Security Policy is correctly configured for IIFE bundle

The CSP in the manifest.json is properly configured:

  • In production: script-src 'self'; object-src 'self'
  • In development: script-src 'self' http://localhost:${port}; object-src 'self'

The IIFE bundle from Vite will be served from the extension's own origin ('self'), making it fully compatible with the CSP configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for CSP in manifest files
rg -l "content_security_policy" | while read -r file; do
  echo "=== CSP in $file ==="
  cat "$file"
done

Length of output: 5777

packages/webext/vite.config.mts (2)

60-68: Consider edge cases for asset rewriting.

Rewriting asset paths via a regex might break in edge cases where nested directories or absolute paths are required. Test thoroughly for situations in which HTML files live in deeper or unexpected folder structures.


100-102: Note on disabling mangling.

Disabling code mangling can help debugging but results in larger bundle sizes. Confirm if the better readability justifies the potential impact on performance and bandwidth.

packages/webext/src/composables/useWebExtensionStorage.ts (1)

95-101: Check merging logic for unintended key overwrites.

Merging defaults into existing objects can overwrite keys unintentionally if they also appear in the saved data. Validate whether this behaviour aligns with user expectations or if a deeper merge strategy is desired.

packages/webext/src/popup/main.ts (1)

1-8: Well-structured Vue.js application setup

The application setup follows Vue.js best practices with proper separation of concerns. The use of a common setup function promotes code reuse across different parts of the extension.

packages/webext/src/global.d.ts (1)

1-3: Well-documented global constants

The global constants are properly typed and documented. The comment for NAME is particularly helpful for understanding its source.

packages/webext/unocss.config.ts (1)

1-2: Verify UnoCSS dependencies

Ensure all required UnoCSS dependencies are correctly specified in package.json.

✅ Verification successful

UnoCSS dependencies are properly configured

The required UnoCSS dependencies are correctly specified in package.json:

  • unocss (^0.62.2) - provides the core functionality including presets and transformers
  • @unocss/reset (^0.62.2) - provides CSS reset utilities

Both packages are at the same version, which ensures compatibility between the components being imported and used in the configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all required UnoCSS dependencies are specified
jq -r '.dependencies + .devDependencies | keys[]' packages/webext/package.json | grep -E 'unocss|@unocss'

Length of output: 128


Script:

#!/bin/bash
# Check the versions of UnoCSS dependencies
jq -r '.dependencies["unocss"], .devDependencies["unocss"], .dependencies["@unocss/reset"], .devDependencies["@unocss/reset"]' packages/webext/package.json

Length of output: 183

packages/webext/src/env.ts (1)

14-14: Consider a more robust browser detection method

The current Firefox detection might be insufficient for all use cases. Consider using the browser.runtime.getBrowserInfo() API for more reliable detection.

Let's verify the browser detection usage:

Comment on lines 27 to 49
let previousTabId = 0

// communication example: send previous tab title from background page
// see shim.d.ts for type declaration
browser.tabs.onActivated.addListener(async ({ tabId }) => {
if (!previousTabId) {
previousTabId = tabId
return
}

let tab: Tabs.Tab

try {
tab = await browser.tabs.get(previousTabId)
previousTabId = tabId
}
catch {
return
}

console.log('previous tab', tab)
sendMessage('tab-prev', { title: tab.title }, { context: 'content-script', tabId })
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Memory leak risk: Add tab cleanup handler

The verification confirms that there's no cleanup mechanism for closed tabs. The previousTabId could potentially reference a closed tab, leading to unnecessary memory retention and failed browser.tabs.get() calls.

  • Add a browser.tabs.onRemoved event listener to reset previousTabId when the tracked tab is closed
  • Consider handling edge cases where the active tab is closed
🔗 Analysis chain

Consider memory management for tab tracking

The current implementation keeps track of only one previous tab. Consider implementing a cleanup mechanism for when tabs are closed.


Let me verify if there are any other tab-related event listeners or cleanup mechanisms in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for tab cleanup handling in the codebase
rg -A 5 "tabs.onRemoved" || echo "No tab cleanup handler found"

Length of output: 91


Script:

#!/bin/bash
# Search for any tab-related event listeners and cleanup
rg -A 5 "browser\.tabs\.(on|remove)" || echo "No tab event handlers found"

# Check if previousTabId is reset anywhere else
ast-grep --pattern 'previousTabId = 0'

# Look for any tab cleanup or management patterns
rg -A 5 "tab.*cleanup|cleanup.*tab"

Length of output: 477

Comment on lines 17 to 22
function generateIssueUrl(issue: Issue): string {
return new URL(`/${issue.owner}/${issue.repository}/issues/${issue.number}`, 'https://github.com').toString()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add URL validation for issue links

The URL generation should include validation to prevent potential XSS attacks through malformed input.

 function generateIssueUrl(issue: Issue): string {
+  const sanitizedOwner = encodeURIComponent(issue.owner)
+  const sanitizedRepo = encodeURIComponent(issue.repository)
+  const sanitizedNumber = encodeURIComponent(issue.number.toString())
-  return new URL(`/${issue.owner}/${issue.repository}/issues/${issue.number}`, 'https://github.com').toString()
+  return new URL(`/${sanitizedOwner}/${sanitizedRepo}/issues/${sanitizedNumber}`, 'https://github.com').toString()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function generateIssueUrl(issue: Issue): string {
return new URL(`/${issue.owner}/${issue.repository}/issues/${issue.number}`, 'https://github.com').toString()
}
function generateIssueUrl(issue: Issue): string {
const sanitizedOwner = encodeURIComponent(issue.owner)
const sanitizedRepo = encodeURIComponent(issue.repository)
const sanitizedNumber = encodeURIComponent(issue.number.toString())
return new URL(`/${sanitizedOwner}/${sanitizedRepo}/issues/${sanitizedNumber}`, 'https://github.com').toString()
}

Comment on lines 30 to 57
<template>
<article class="flex flex-row gap-2 leading-tightest">
<span class="flex-shrink-0 inline-block w-5 h-5 i-tabler-circle-dot text-green-500" />
<div class="flex flex-row gap-2 flex-wrap md:flex-nowrap md:pb-6 flex-grow">
<a
:href="generateIssueUrl(issue)" target="_blank"
class="line-clamp-1 flex-grow text-sm md:text-base lg:flex-grow-0 no-underline color-current hover:underline"
>{{
issue.title
}}</a>
<div class="text-xs relative md:absolute md:mt-6 text-gray-700">
<a
aria-current="page" :href="generateUnsightDotDevUrl(issue)"
class="no-underline hover:underline color-current"
>{{
issue.repository }}</a>
· updated
<relative-time :datetime="issue.updated_at" /> · <a
:href="generateUnsightDotDevUrl(issue)"
class="no-underline hover:underline color-current"
>{{
scoreAsPercentage(issue.score) }}% similar </a>
</div>
</div>
</article>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance accessibility features

The component could benefit from additional accessibility attributes.

-  <article class="flex flex-row gap-2 leading-tightest">
+  <article class="flex flex-row gap-2 leading-tightest" role="listitem">
     <span class="flex-shrink-0 inline-block w-5 h-5 i-tabler-circle-dot text-green-500" />
     <div class="flex flex-row gap-2 flex-wrap md:flex-nowrap md:pb-6 flex-grow">
       <a
         :href="generateIssueUrl(issue)" target="_blank"
-        class="line-clamp-1 flex-grow text-sm md:text-base lg:flex-grow-0 no-underline color-current hover:underline"
+        class="line-clamp-1 flex-grow text-sm md:text-base lg:flex-grow-0 no-underline color-current hover:underline"
+        :aria-label="'View issue: ' + issue.title"
       >{{
         issue.title
       }}</a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<template>
<article class="flex flex-row gap-2 leading-tightest">
<span class="flex-shrink-0 inline-block w-5 h-5 i-tabler-circle-dot text-green-500" />
<div class="flex flex-row gap-2 flex-wrap md:flex-nowrap md:pb-6 flex-grow">
<a
:href="generateIssueUrl(issue)" target="_blank"
class="line-clamp-1 flex-grow text-sm md:text-base lg:flex-grow-0 no-underline color-current hover:underline"
>{{
issue.title
}}</a>
<div class="text-xs relative md:absolute md:mt-6 text-gray-700">
<a
aria-current="page" :href="generateUnsightDotDevUrl(issue)"
class="no-underline hover:underline color-current"
>{{
issue.repository }}</a>
· updated
<relative-time :datetime="issue.updated_at" /> · <a
:href="generateUnsightDotDevUrl(issue)"
class="no-underline hover:underline color-current"
>{{
scoreAsPercentage(issue.score) }}% similar </a>
</div>
</div>
</article>
<template>
<article class="flex flex-row gap-2 leading-tightest" role="listitem">
<span class="flex-shrink-0 inline-block w-5 h-5 i-tabler-circle-dot text-green-500" />
<div class="flex flex-row gap-2 flex-wrap md:flex-nowrap md:pb-6 flex-grow">
<a
:href="generateIssueUrl(issue)" target="_blank"
class="line-clamp-1 flex-grow text-sm md:text-base lg:flex-grow-0 no-underline color-current hover:underline"
:aria-label="'View issue: ' + issue.title"
>{{
issue.title
}}</a>
<div class="text-xs relative md:absolute md:mt-6 text-gray-700">
<a
aria-current="page" :href="generateUnsightDotDevUrl(issue)"
class="no-underline hover:underline color-current"
>{{
issue.repository }}</a>
· updated
<relative-time :datetime="issue.updated_at" /> · <a
:href="generateUnsightDotDevUrl(issue)"
class="no-underline hover:underline color-current"
>{{
scoreAsPercentage(issue.score) }}% similar </a>
</div>
</div>
</article>

Comment on lines 5 to 12
<p align="center">
<sub>Popup</sub><br/>
<img width="655" src="https://user-images.githubusercontent.com/11247099/126741643-813b3773-17ff-4281-9737-f319e00feddc.png"><br/>
<sub>Options Page</sub><br/>
<img width="655" src="https://user-images.githubusercontent.com/11247099/126741653-43125b62-6578-4452-83a7-bee19be2eaa2.png"><br/>
<sub>Inject Vue App into the Content Script</sub><br/>
<img src="https://user-images.githubusercontent.com/11247099/130695439-52418cf0-e186-4085-8e19-23fe808a274e.png">
</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add alt text to images for accessibility

The images in the documentation lack alt text, which is essential for accessibility.

 <p align="center">
 <sub>Popup</sub><br/>
-<img width="655" src="https://user-images.githubusercontent.com/11247099/126741643-813b3773-17ff-4281-9737-f319e00feddc.png"><br/>
+<img width="655" src="https://user-images.githubusercontent.com/11247099/126741643-813b3773-17ff-4281-9737-f319e00feddc.png" alt="Screenshot of the extension popup interface"><br/>
 <sub>Options Page</sub><br/>
-<img width="655" src="https://user-images.githubusercontent.com/11247099/126741653-43125b62-6578-4452-83a7-bee19be2eaa2.png"><br/>
+<img width="655" src="https://user-images.githubusercontent.com/11247099/126741653-43125b62-6578-4452-83a7-bee19be2eaa2.png" alt="Screenshot of the extension options page"><br/>
 <sub>Inject Vue App into the Content Script</sub><br/>
-<img src="https://user-images.githubusercontent.com/11247099/130695439-52418cf0-e186-4085-8e19-23fe808a274e.png">
+<img src="https://user-images.githubusercontent.com/11247099/130695439-52418cf0-e186-4085-8e19-23fe808a274e.png" alt="Screenshot showing Vue app injection into content script">
 </p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p align="center">
<sub>Popup</sub><br/>
<img width="655" src="https://user-images.githubusercontent.com/11247099/126741643-813b3773-17ff-4281-9737-f319e00feddc.png"><br/>
<sub>Options Page</sub><br/>
<img width="655" src="https://user-images.githubusercontent.com/11247099/126741653-43125b62-6578-4452-83a7-bee19be2eaa2.png"><br/>
<sub>Inject Vue App into the Content Script</sub><br/>
<img src="https://user-images.githubusercontent.com/11247099/130695439-52418cf0-e186-4085-8e19-23fe808a274e.png">
</p>
<p align="center">
<sub>Popup</sub><br/>
<img width="655" src="https://user-images.githubusercontent.com/11247099/126741643-813b3773-17ff-4281-9737-f319e00feddc.png" alt="Screenshot of the extension popup interface"><br/>
<sub>Options Page</sub><br/>
<img width="655" src="https://user-images.githubusercontent.com/11247099/126741653-43125b62-6578-4452-83a7-bee19be2eaa2.png" alt="Screenshot of the extension options page"><br/>
<sub>Inject Vue App into the Content Script</sub><br/>
<img src="https://user-images.githubusercontent.com/11247099/130695439-52418cf0-e186-4085-8e19-23fe808a274e.png" alt="Screenshot showing Vue app injection into content script">
</p>
🧰 Tools
🪛 Markdownlint (0.37.0)

7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


9-9: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


11-11: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/webext/src/global.d.ts (1)

5-8: 🛠️ Refactor suggestion

Strengthen type safety for Vue components

Using any type for Vue components reduces TypeScript's effectiveness.

Apply this improvement:

 declare module '*.vue' {
-  const component: any
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<{}, {}, any>
   export default component
 }
packages/webext/src/contentScripts/views/App.vue (1)

13-17: ⚠️ Potential issue

Enhance URL parsing robustness

The current path parsing method is fragile and could break with URL variations.

Consider implementing a more robust URL parsing solution:

-const [, ownerPart, repoPart, , issuePart] = window.location.pathname.split('/')
-
-const issueUrl = computed(() => new URL(`/api/similarity/${ownerPart}/${repoPart}/${issuePart}`, VITE_UNSIGHT_DOT_DEV_BASE_URL).toString())

+function parseGitHubPath(pathname: string) {
+  const matches = pathname.match(/^\/([^/]+)\/([^/]+)\/issues\/(\d+)/)
+  if (!matches) {
+    throw new Error('Invalid GitHub issue URL')
+  }
+  return {
+    owner: matches[1],
+    repo: matches[2],
+    issue: matches[3],
+  }
+}
+
+const { owner, repo, issue } = parseGitHubPath(window.location.pathname)
+const issueUrl = computed(() => 
+  new URL(`/api/similarity/${owner}/${repo}/${issue}`, VITE_UNSIGHT_DOT_DEV_BASE_URL).toString()
+)
🧹 Nitpick comments (5)
packages/webext/README.md (3)

103-106: Enhance environment setup instructions

The environment setup instructions could be more detailed:

  1. The .env.example file location isn't specified
  2. There's no mention of other potential environment variables
  3. The importance of the VITE_UNSIGHT_DOT_DEV_BASE_URL isn't explained

Consider adding more context about the environment configuration.


129-135: Enhance build instructions with more details

The build instructions could be improved by:

  1. Explaining what files are generated during the build
  2. Providing instructions for generating .crx and .xpi files
  3. Adding links to the Chrome Web Store and Firefox Add-ons documentation for publishing

Consider expanding this section to make it more helpful for developers.


139-141: Add alt text and context for Volta

  1. The Volta image lacks alt text for accessibility
  2. The connection between Volta and this template could be better explained to provide more context for users

Consider adding alt text to the image and expanding on how this template relates to Volta.

packages/webext/src/global.d.ts (1)

1-3: Add documentation for the DEV constant

The NAME constant has proper documentation, but DEV lacks a description. Consider adding a JSDoc comment explaining its purpose.

 /** Extension name, defined in packageJson.name */
+/** Boolean flag indicating development mode */
 declare const __DEV__: boolean
 declare const __NAME__: string
packages/webext/src/contentScripts/views/App.vue (1)

1-6: Document the purpose of uno.css import

Consider adding a comment explaining why uno.css is required and its role in the component.

 import SimilarIssue from '~/components/SimilarIssue.vue'
+// Import UnoCSS styles for utility-first CSS framework
 import 'uno.css'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7a8e9f and c070f9e.

📒 Files selected for processing (5)
  • packages/webext/.env.example (1 hunks)
  • packages/webext/README.md (1 hunks)
  • packages/webext/src/components/SimilarIssue.vue (1 hunks)
  • packages/webext/src/contentScripts/views/App.vue (1 hunks)
  • packages/webext/src/global.d.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/webext/.env.example
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/webext/src/components/SimilarIssue.vue
🧰 Additional context used
🪛 LanguageTool
packages/webext/README.md

[uncategorized] ~21-~21: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...lang.org/) - type safe - 📦 Components auto importing - 🌟 [Icons](./src/c...

(AUTO_HYPHEN)


[uncategorized] ~117-~117: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...bash pnpm dev-firefox web-ext auto reload the extension when extension/ files c...

(AUTO_HYPHEN)

🪛 Markdownlint (0.37.0)
packages/webext/README.md

7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


9-9: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


11-11: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

🔇 Additional comments (4)
packages/webext/README.md (2)

5-12: Add alt text to images for accessibility

The images in the documentation lack alt text, which is essential for accessibility. This issue was previously identified and should be addressed.

🧰 Tools
🪛 Markdownlint (0.37.0)

7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


9-9: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


11-11: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


21-23: Verify relative paths in documentation

The links to components and icons directories use relative paths (./src/components) which might not work correctly in the GitHub UI. Consider using absolute URLs to ensure the links work consistently.

✅ Verification successful

Relative paths in documentation are correct and well-supported

The relative paths in the README.md are accurate and point to valid directories. The verification shows:

  • The ./src/components directory exists and contains proper documentation
  • The components directory includes icon support through Iconify
  • Content script functionality is present via contentScriptHMR.ts

The documentation accurately reflects the project's structure and features, with working relative paths that are appropriate for both GitHub UI and local navigation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if the referenced directories exist
fd --type d "components" --base-directory packages/webext/src

Length of output: 73


Script:

#!/bin/bash
# Let's verify if there are any icon-related files in the components directory
rg -l "icon" packages/webext/src/components/

# Also check if there are any content script related files
fd "content" --type f packages/webext/src

Length of output: 178


Script:

#!/bin/bash
# Let's check the content of the components README to understand the structure
cat packages/webext/src/components/README.md

# Also check if there's any Vue-related content script setup
rg -l "vue.*content" packages/webext/src

Length of output: 534

🧰 Tools
🪛 LanguageTool

[uncategorized] ~21-~21: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...lang.org/) - type safe - 📦 Components auto importing - 🌟 [Icons](./src/c...

(AUTO_HYPHEN)

packages/webext/src/global.d.ts (1)

10-12: LGTM! Well-structured environment variable typing

The interface correctly defines the type for the environment variable following Vite's conventions.

packages/webext/src/contentScripts/views/App.vue (1)

7-11: LGTM! Robust environment variable validation

The code properly validates the required environment variable with a clear error message.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
packages/webext/README.md (6)

1-4: Consider expanding the introduction with additional context

The introduction effectively describes the core functionality, but could benefit from:

  • Mentioning the current development status and planned features
  • Adding screenshots of the extension in action
  • Including browser compatibility information

28-28: Fix grammar in environment setup instruction

-When building the project locally copy the .env.example file to .env.local and fill in the required values:
+When building the project locally, copy the .env.example file to .env.local and fill in the required values:
🧰 Tools
🪛 LanguageTool

[uncategorized] ~28-~28: A comma might be missing here.
Context: ...xtension ``` When building the project locally copy the .env.example file to .env.loca...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


43-43: Correct grammar in web-ext description

-`web-ext` auto reload the extension when `extension/` files changed.
+`web-ext` auto-reloads the extension when `extension/` files change.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~43-~43: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...pm -F webext dev-firefox ``` web-ext auto reload the extension when `extension/` files c...

(AUTO_HYPHEN)


13-46: Improve development instructions structure

Consider reorganising the development section to:

  1. Environment setup (.env configuration)
  2. Development server options (clearly separated for different browsers)
  3. Hot reload information
  4. Troubleshooting tips
🧰 Tools
🪛 LanguageTool

[uncategorized] ~28-~28: A comma might be missing here.
Context: ...xtension ``` When building the project locally copy the .env.example file to .env.loca...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~43-~43: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...pm -F webext dev-firefox ``` web-ext auto reload the extension when `extension/` files c...

(AUTO_HYPHEN)


55-55: Fix grammar in environment file instruction

-ensure you have a .env file for the production build or pass the values as environment variables.
+Ensure you have a .env file for the production build, or pass the values as environment variables.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~55-~55: Possible missing comma found.
Context: ...you have a .env file for the production build or pass the values as environment varia...

(AI_HYDRA_LEO_MISSING_COMMA)


57-57: Fix grammar in extension store instruction

-And then pack files under `extension`, you can upload `extension.crx` or `extension.xpi` to appropriate extension store.
+And then pack files under `extension`, you can upload `extension.crx` or `extension.xpi` to the appropriate extension store.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~57-~57: You might be missing the article “the” here.
Context: ...d extension.crx or extension.xpi to appropriate extension store. ## Credits Thanks to...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

packages/webext/src/contentScripts/views/App.vue (1)

21-39: Enhance accessibility and error handling in the template

The current template could benefit from improved accessibility and error handling features.

-  <details class="grid gap-2" aria-live="polite">
+  <details class="grid gap-2" aria-live="polite" :open="issues && issues.length > 0">
     <summary>Similar issues</summary>
     <div v-if="isFetching">
-      Loading...
+      <span role="status">Loading similar issues...</span>
     </div>
     <div v-else-if="error" class="text-red-700">
-      Error loading similar issues
+      <p>Failed to load similar issues</p>
+      <button 
+        @click="refetch()" 
+        class="text-sm underline hover:no-underline"
+        aria-label="Retry loading similar issues"
+      >
+        Retry
+      </button>
     </div>
     <template v-else-if="issues">
       <p v-if="issues.length === 0">
         No similar issues
       </p>
-      <ul v-else class="list-none grid gap-4" style="padding-inline-start: 0;">
+      <ul 
+        v-else 
+        class="list-none grid gap-4" 
+        style="padding-inline-start: 0;"
+        aria-label="List of similar issues"
+      >
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad184f5 and 36a56a8.

📒 Files selected for processing (5)
  • packages/webext/README.md (1 hunks)
  • packages/webext/src/components/SimilarIssue.vue (1 hunks)
  • packages/webext/src/contentScripts/index.ts (1 hunks)
  • packages/webext/src/contentScripts/views/App.vue (1 hunks)
  • packages/webext/src/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/webext/src/contentScripts/index.ts
  • packages/webext/src/components/SimilarIssue.vue
🧰 Additional context used
🪛 LanguageTool
packages/webext/README.md

[uncategorized] ~28-~28: A comma might be missing here.
Context: ...xtension ``` When building the project locally copy the .env.example file to .env.loca...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~43-~43: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...pm -F webext dev-firefox ``` web-ext auto reload the extension when `extension/` files c...

(AUTO_HYPHEN)


[uncategorized] ~55-~55: Possible missing comma found.
Context: ...you have a .env file for the production build or pass the values as environment varia...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~57-~57: You might be missing the article “the” here.
Context: ...d extension.crx or extension.xpi to appropriate extension store. ## Credits Thanks to...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🔇 Additional comments (3)
packages/webext/README.md (1)

59-61: LGTM!

The credits section is well-formatted and properly acknowledges the template source.

packages/webext/src/contentScripts/views/App.vue (2)

7-11: Move environment configuration to a separate file

Direct usage of environment variables makes testing and configuration management difficult.


13-15: Enhance path parsing robustness

The current path parsing implementation is fragile and could break with URL variations.

Comment on lines +5 to +11
## Installation

From the root of the monorepo, run:

```bash
pnpm install
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance installation instructions with prerequisites

Consider adding:

  • Node.js version requirements
  • pnpm installation instructions
  • Repository cloning steps
  • Development environment prerequisites

Comment on lines +47 to +58
### Build

To build the extension, run

```bash
pnpm -F webext build
```

ensure you have a .env file for the production build or pass the values as environment variables.

And then pack files under `extension`, you can upload `extension.crx` or `extension.xpi` to appropriate extension store.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance build instructions with additional details

Consider adding:

  • Production environment variable requirements
  • Extension packaging steps for different browsers
  • Store submission guidelines
  • Version bumping instructions
🧰 Tools
🪛 LanguageTool

[uncategorized] ~55-~55: Possible missing comma found.
Context: ...you have a .env file for the production build or pass the values as environment varia...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~57-~57: You might be missing the article “the” here.
Context: ...d extension.crx or extension.xpi to appropriate extension store. ## Credits Thanks to...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/webext/src/manifest.ts (2)

35-39: Optimise icons for different sizes

Currently using the same 512px icon for all sizes. Consider creating appropriately sized icons for better performance and visual quality.

 icons: {
-      16: './assets/icon-512.png',
-      48: './assets/icon-512.png',
-      128: './assets/icon-512.png',
+      16: './assets/icon-16.png',
+      48: './assets/icon-48.png',
+      128: './assets/icon-128.png',
 },

81-83: Improve type safety for side panel configuration

Using any type assertion reduces type safety. Consider defining proper types for the manifest extension.

-    (manifest as any).side_panel = {
+    interface ChromiumManifest extends Manifest.WebExtensionManifest {
+      side_panel?: { default_path: string }
+    }
+    (manifest as ChromiumManifest).side_panel = {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36a56a8 and 479baed.

📒 Files selected for processing (2)
  • .gitignore (1 hunks)
  • packages/webext/src/manifest.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .gitignore
🧰 Additional context used
🪛 Biome (1.9.4)
packages/webext/src/manifest.ts

[error] 91-91: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 87-87: Unexpected constant condition.

(lint/correctness/noConstantCondition)

🔇 Additional comments (1)
packages/webext/src/manifest.ts (1)

6-7: 🛠️ Refactor suggestion

Consider tightening host permissions scope

The host permissions are quite broad. For production, consider restricting unsight.dev to specific API endpoints:

-const baseHostPermissions = ['https://github.com/*', 'https://unsight.dev/*']
+const baseHostPermissions = ['https://github.com/*', 'https://api.unsight.dev/*']

Likely invalid or redundant comment.

nickytonline and others added 2 commits December 20, 2024 11:43
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@nickytonline
Copy link
Contributor Author

For some reason, in c070f9e, I can't access import.meta.env.VITE_UNSIGHT_DOT_DEV_BASE_URL, when it's set in a .env or .env.local file.

I've tried VITE_UNSIGHT_DOT_DEV_BASE_URL, VUE_APP_UNSIGHT_DOT_DEV_BASE_URL, but they never get recognized, but the other vars under import.meta.env do, e.g.

CleanShot 2024-12-19 at 16 38 55

I've also tried explicitly adding this to the vite config, but no difference.

export default defineConfig({
  envPrefix: 'VITE_',
  // ...existing code...
})

These are usually picked up by Vite, so not sure what's up. This is the only blocker at the moment, and I'm sure it's something super silly.

@danielroe
Copy link
Owner

sorry, got carried away! will merge and iterate in main...

this looks great - thank you ❤️

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (11)
packages/webext/README.md (3)

28-34: Improve grammar and clarity in development instructions

Apply these improvements for better readability:

-When building the project locally copy the .env.example file to .env.local and fill in the required values:
+When building the project locally, copy the .env.example file to .env.local and fill in the required values:

-Then **load extension in browser with the `extension/` folder**.
+Then **load the extension in the browser with the `extension/` folder**.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~28-~28: A comma might be missing here.
Context: ...xtension ``` When building the project locally copy the .env.example file to .env.loca...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...http://localhost:3000" ``` Then **load extension in browser with the extension/ folder...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...ost:3000" ``` Then load extension in browser with the extension/ folder. For Fi...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


13-46: Add browser-specific setup instructions

Consider enhancing the development section with:

  • Step-by-step instructions for loading the extension in Chrome
  • Step-by-step instructions for loading the extension in Firefox
  • Screenshots of the browser extension settings pages
  • Troubleshooting tips for common development issues
🧰 Tools
🪛 LanguageTool

[uncategorized] ~28-~28: A comma might be missing here.
Context: ...xtension ``` When building the project locally copy the .env.example file to .env.loca...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...http://localhost:3000" ``` Then **load extension in browser with the extension/ folder...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...ost:3000" ``` Then load extension in browser with the extension/ folder. For Fi...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~43-~43: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...pm -F webext dev-firefox ``` web-ext auto reload the extension when `extension/` files c...

(AUTO_HYPHEN)


55-57: Improve grammar in build instructions

Apply these improvements for better clarity:

-ensure you have a .env file for the production build or pass the values as environment variables.
+Ensure you have a .env file for the production build, or pass the values as environment variables.

-And then pack files under `extension`, you can upload `extension.crx` or `extension.xpi` to appropriate extension store.
+And then pack files under `extension`, you can upload `extension.crx` or `extension.xpi` to the appropriate extension store.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~55-~55: Possible missing comma found.
Context: ...you have a .env file for the production build or pass the values as environment varia...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~57-~57: You might be missing the article “the” here.
Context: ...d extension.crx or extension.xpi to appropriate extension store. ## Credits Thanks to...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

packages/webext/src/contentScripts/views/App.vue (2)

13-24: Enhance error handling with retries

Whilst the basic error handling is present, consider implementing a retry mechanism for transient failures.

 const { 
   data: issues, 
   error, 
   isFetching,
 } = useFetch<Issue[]>(issueUrl, {
   immediate: true,
   timeout: 5000,
+  retry: 3,
+  retryDelay: 1000,
   onFetchError: (ctx) => {
     console.error('Failed to fetch similar issues:', ctx.error)
+    // Add more specific error handling based on error type
+    if (ctx.error instanceof TypeError) {
+      console.error('Network error occurred')
+    }
     return ctx
   }
 }).json()

27-47: Brilliant use of semantic HTML and accessibility!

The implementation shows excellent attention to accessibility with aria-live and semantic elements. Consider enhancing the loading state with more context.

-      Loading...
+      <p class="loading-text">
+        <span class="loading-spinner"></span>
+        Fetching similar issues...
+      </p>
packages/webext/src/components/__tests__/SimilarIssue.spec.ts (1)

1-24: Consider extracting test data to fixtures

The test setup is well-structured, but moving the mock data to a separate fixture file would improve maintainability and reusability.

// __fixtures__/issues.ts
export const mockIssue = {
  owner: 'nuxt',
  repository: 'nuxt',
  number: 26798,
  title: 'FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory',
  url: 'https://github.com/nuxt/nuxt/issues/26798',
  updated_at: '2024-12-12',
  labels: ['bug'],
  score: 0.87,
}
packages/webext/src/manifest.ts (4)

6-7: Consider making host permissions more specific

The host permissions could be more granular. For the unsight.dev domain, consider specifying only the required API endpoints rather than allowing access to the entire domain.

-const baseHostPermissions = ['https://github.com/*', 'https://unsight.dev/*']
+const baseHostPermissions = ['https://github.com/*', 'https://api.unsight.dev/*']

35-39: Consider using different icon sizes

Currently, the same icon file is used for all sizes. Consider providing appropriately sized icons for better visual quality and performance.

 icons: {
-  16: './assets/icon-512.png',
-  48: './assets/icon-512.png',
-  128: './assets/icon-512.png',
+  16: './assets/icon-16.png',
+  48: './assets/icon-48.png',
+  128: './assets/icon-128.png',
 },

65-70: Strengthen content security policy in development

The development CSP could be more restrictive by specifying the exact script sources needed.

-? `script-src 'self' http://localhost:${port}; object-src 'self'`
+? `script-src 'self' http://localhost:${port}/dist/; object-src 'self'`

79-84: Improve type safety for side panel configuration

Instead of using any, consider defining a proper type for the Chromium-specific manifest properties.

+interface ChromiumManifest extends Manifest.WebExtensionManifest {
+  side_panel?: {
+    default_path: string;
+  };
+}

-    (manifest as any).side_panel = {
+    (manifest as ChromiumManifest).side_panel = {
packages/webext/tests/e2e/basic.spec.ts (1)

1-2: Consider adding explicit type imports

Adding type information for the imported utilities would improve code maintainability and IDE support.

-import { expect, isDevArtifact, name, test } from './fixtures'
+import type { expect as ExpectType } from '@playwright/test'
+import { expect, isDevArtifact, name, test } from './fixtures'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 479baed and 1e2e655.

⛔ Files ignored due to path filters (5)
  • package.json is excluded by !package.json, !**/package.json
  • packages/webext/extension/assets/icon-512.png is excluded by !**/*.png
  • packages/webext/extension/assets/icon.svg is excluded by !**/*.svg
  • packages/webext/package.json is excluded by !**/package.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml
📒 Files selected for processing (21)
  • .github/workflows/autofix.yml (1 hunks)
  • .github/workflows/ci.yml (1 hunks)
  • .gitignore (2 hunks)
  • README.md (1 hunks)
  • eslint.config.js (1 hunks)
  • packages/webext/.gitignore (1 hunks)
  • packages/webext/README.md (1 hunks)
  • packages/webext/modules.d.ts (1 hunks)
  • packages/webext/playwright.config.ts (1 hunks)
  • packages/webext/scripts/manifest.ts (1 hunks)
  • packages/webext/scripts/prepare.ts (1 hunks)
  • packages/webext/src/background/main.ts (1 hunks)
  • packages/webext/src/components/SimilarIssue.vue (1 hunks)
  • packages/webext/src/components/__tests__/SimilarIssue.spec.ts (1 hunks)
  • packages/webext/src/composables/useWebExtensionStorage.ts (1 hunks)
  • packages/webext/src/contentScripts/index.ts (1 hunks)
  • packages/webext/src/contentScripts/views/App.vue (1 hunks)
  • packages/webext/src/manifest.ts (1 hunks)
  • packages/webext/tests/e2e/basic.spec.ts (1 hunks)
  • packages/webext/tests/e2e/fixtures.ts (1 hunks)
  • packages/webext/vite.config.mts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/webext/.gitignore
🚧 Files skipped from review as they are similar to previous changes (11)
  • README.md
  • .gitignore
  • packages/webext/modules.d.ts
  • packages/webext/scripts/manifest.ts
  • packages/webext/playwright.config.ts
  • packages/webext/scripts/prepare.ts
  • packages/webext/src/background/main.ts
  • packages/webext/vite.config.mts
  • packages/webext/src/composables/useWebExtensionStorage.ts
  • packages/webext/src/contentScripts/index.ts
  • packages/webext/src/components/SimilarIssue.vue
🧰 Additional context used
🪛 LanguageTool
packages/webext/README.md

[uncategorized] ~28-~28: A comma might be missing here.
Context: ...xtension ``` When building the project locally copy the .env.example file to .env.loca...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...http://localhost:3000" ``` Then **load extension in browser with the extension/ folder...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...ost:3000" ``` Then load extension in browser with the extension/ folder. For Fi...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~43-~43: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...pm -F webext dev-firefox ``` web-ext auto reload the extension when `extension/` files c...

(AUTO_HYPHEN)


[uncategorized] ~55-~55: Possible missing comma found.
Context: ...you have a .env file for the production build or pass the values as environment varia...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~57-~57: You might be missing the article “the” here.
Context: ...d extension.crx or extension.xpi to appropriate extension store. ## Credits Thanks to...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🪛 Biome (1.9.4)
packages/webext/src/manifest.ts

[error] 91-91: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 87-87: Unexpected constant condition.

(lint/correctness/noConstantCondition)

🔇 Additional comments (8)
packages/webext/README.md (2)

5-11: Enhance installation instructions with prerequisites

Consider adding:

  • Node.js version requirements
  • pnpm installation instructions
  • Repository cloning steps
  • Development environment prerequisites

47-58: Enhance build instructions with additional details

Consider adding:

  • Production environment variable requirements
  • Extension packaging steps for different browsers
  • Store submission guidelines
  • Version bumping instructions
🧰 Tools
🪛 LanguageTool

[uncategorized] ~55-~55: Possible missing comma found.
Context: ...you have a .env file for the production build or pass the values as environment varia...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~57-~57: You might be missing the article “the” here.
Context: ...d extension.crx or extension.xpi to appropriate extension store. ## Credits Thanks to...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

packages/webext/src/contentScripts/views/App.vue (3)

1-6: Imports and setup look brilliant!

The imports are well-organised, with proper separation of types, utilities and components.


7-8: Move API URL to environment configuration

The hardcoded fallback URL makes it challenging to switch between environments.


9-11: Enhance path parsing robustness

The current path parsing implementation is brittle and could fail with URL variations.

.github/workflows/autofix.yml (1)

33-34: Brilliant addition of the unit test snapshot update step.

The inclusion of snapshot updates is well-placed. However, consider uncommenting the browser test snapshot updates as well, given that this PR introduces a browser extension.

Let's verify if there are any snapshot files that might need updating:

.github/workflows/ci.yml (2)

46-47: Splendid addition of the unit test step.

The placement after type checking is spot-on, ensuring type safety before running tests.


55-55: Verify Playwright noble image compatibility.

Whilst updating to Playwright v1.49.1 is good, the switch from focal to noble-based image warrants verification, as noble is relatively new.

Let's check the Playwright documentation and image availability:

✅ Verification successful

Let me gather more information about the Playwright configuration and usage in the codebase.


Noble-based Playwright image v1.49.1 is verified and safe to use

The verification shows that:

  • The noble-based Playwright image v1.49.1 is officially available in Microsoft Container Registry
  • The project uses Playwright v1.49.1 as a dev dependency
  • The noble-based images have been consistently released since v1.45.0, indicating stability
  • Both AMD64 and ARM64 architectures are supported
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the availability and stability of the Playwright noble image

# Check if the image exists and its tags
curl -s "https://mcr.microsoft.com/v2/playwright/tags/list" | jq '.tags[] | select(contains("noble"))'

# Look for Playwright-related configurations
fd -e config.ts -e config.js | rg -l 'playwright'

Length of output: 1087


Script:

#!/bin/bash
# Check for Playwright-related files and configurations
rg -l "playwright" --type yaml
rg -l "playwright" --type typescript
rg -l "playwright" --type javascript

# Look for package.json to check Playwright dependencies
fd "package.json" --type f --exec cat {} \; | jq -c 'select(.dependencies != null or .devDependencies != null) | {dependencies, devDependencies}'

Length of output: 1906

@@ -4,6 +4,7 @@ import withNuxt from './packages/web/.nuxt/eslint.config.mjs'

export default withNuxt(antfu()).append(
{
files: ['packages/web/**/*.ts'],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

ESLint configuration should include TypeScript files from both packages

The verification confirms that TypeScript files exist in both packages/web/ (19 files) and packages/webext/ (24 files). The current ESLint configuration only covers the web package, leaving the webext TypeScript files unchecked. The configuration should be updated to include both packages:

-    files: ['packages/web/**/*.ts'],
+    files: ['packages/{web,webext}/**/*.ts'],

• Consider adding separate configurations if the two packages require different ESLint rules
• The console logging rule suggestion remains valid and should be environment-specific

🔗 Analysis chain

Verify the target directory for TypeScript files

The configuration targets TypeScript files in packages/web/**, but this PR introduces files in packages/webext/. Should this rule also apply to the browser extension's TypeScript files?

Let's verify the TypeScript file locations:

Additionally, consider making the console logging rules environment-specific:

 files: ['packages/web/**/*.ts'],
 rules: {
-  'no-console': 'off',
+  'no-console': process.env.NODE_ENV === 'development' ? 'off' : 'warn',
 },
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check TypeScript file locations to ensure correct ESLint coverage

# Find all TypeScript files in packages/web and packages/webext
echo "TypeScript files in packages/web:"
fd -e ts . packages/web || true

echo -e "\nTypeScript files in packages/webext:"
fd -e ts . packages/webext || true

Length of output: 1936

Comment on lines +26 to +34
expect(wrapper.html()).toMatchInlineSnapshot(`
"<article class="flex flex-row gap-2 leading-tightest"><span class="flex-shrink-0 inline-block w-5 h-5 i-tabler-circle-dot text-green-500"></span>
<div class="flex flex-row gap-2 flex-wrap md:flex-nowrap md:pb-6 flex-grow"><a href="https://github.com/nuxt/nuxt/issues/26798" target="_blank" class="issue-link"><span class="sr-only">View issue: </span>FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory</a>
<div class="text-xs relative md:absolute md:mt-6 text-gray-700"><a aria-current="page" href="https://unsight.dev/nuxt/nuxt" class="no-underline hover:underline color-current">nuxt</a> · updated <relative-time-stub datetime="2024-12-12"></relative-time-stub> · <a href="https://unsight.dev/nuxt/nuxt" class="no-underline hover:underline color-current">87.00% similar </a></div>
</div>
</article>"
`)
})
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Expand test coverage with additional test cases

Whilst the snapshot test is a good start, consider adding tests for:

  • Edge cases (empty labels, missing data)
  • Prop validation
  • Score formatting
  • URL generation
it('should handle missing labels', () => {
  const wrapper = mount(SimilarIssue, {
    props: {
      issue: {
        ...mockIssue,
        labels: [],
      },
    },
    global: {
      stubs: ['relative-time'],
    },
  })
  expect(wrapper.find('.labels').exists()).toBe(false)
})

it('should format score correctly', () => {
  const wrapper = mount(SimilarIssue, {
    props: {
      issue: {
        ...mockIssue,
        score: 0.8763,
      },
    },
    global: {
      stubs: ['relative-time'],
    },
  })
  expect(wrapper.text()).toContain('87.63% similar')
})

Comment on lines +40 to +45
permissions: [
'tabs',
'storage',
'activeTab',
'sidePanel',
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Storage permission is required, but 'tabs' permission can be removed

The storage permission is actively used in the codebase through the useWebExtensionStorage composable (packages/webext/src/composables/useWebExtensionStorage.ts), which implements local storage functionality using the WebExtension storage API.

However, since 'activeTab' permission is already present, the 'tabs' permission can be removed as it's redundant and overly broad for the current implementation.

  • Remove 'tabs' from the permissions array in packages/webext/src/manifest.ts
  • Keep 'storage' as it's required for the storage functionality
  • Keep 'activeTab' and 'sidePanel' as they are
🔗 Analysis chain

Review permission requirements

The extension requests broad permissions. Consider:

  1. Whether 'tabs' permission could be replaced with the more specific 'activeTab'.
  2. If 'storage' permission is actually needed for the current functionality.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for storage API usage
rg -l "chrome\.storage|browser\.storage" packages/webext/

Length of output: 59


Script:

#!/bin/bash
# Search for storage API usage with broader pattern and context
rg -i "storage" packages/webext/ -B 2 -A 2

# Also check for any imports or requires related to storage
rg "(import|require).*storage" packages/webext/

# Check manifest.json if it exists
fd manifest.json packages/webext/ --exec cat {}

Length of output: 7969

Comment on lines +3 to +10
test('example test', async ({ page }, testInfo) => {
testInfo.skip(!isDevArtifact(), 'contentScript is in closed ShadowRoot mode')

await page.goto('https://example.com')

await page.locator(`#${name} button`).click()
await expect(page.locator(`#${name} h1`)).toHaveText('Vitesse WebExt')
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace example test with actual extension functionality tests

This test appears to be from a template and tests example.com functionality. Consider replacing it with meaningful tests that verify your extension's core features, such as:

  • Similar issues panel injection
  • Data fetching and display
  • UI component rendering

Comment on lines +12 to +15
test('popup page', async ({ page, extensionId }) => {
await page.goto(`chrome-extension://${extensionId}/dist/popup/index.html`)
await expect(page.locator('button')).toHaveText('Open Options')
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance popup page test coverage

The current test only verifies button text. Consider adding tests for:

  • Popup initialisation state
  • User interactions
  • Error states
  • Navigation between popup views

Comment on lines +17 to +20
test('options page', async ({ page, extensionId }) => {
await page.goto(`chrome-extension://${extensionId}/dist/options/index.html`)
await expect(page.locator('img')).toHaveAttribute('alt', 'extension icon')
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Expand options page test coverage and verify extension URL handling

The test only checks the image alt text. Additionally, the hardcoded extension URL pattern might be fragile.

Consider:

  1. Adding tests for options page functionality
  2. Using a utility function for extension URLs:
function getExtensionUrl(extensionId: string, path: string) {
    return `chrome-extension://${extensionId}/dist/${path}/index.html`;
}

Comment on lines +16 to +29
context: async ({ headless }, use) => {
// workaround for the Vite server has started but contentScript is not yet.
await sleep(1000)
const context = await chromium.launchPersistentContext('', {
headless,
args: [
...(headless ? ['--headless=new'] : []),
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
})
await use(context)
await context.close()
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace fixed delay with proper server readiness check

Using a fixed delay (sleep) for server readiness is unreliable and could lead to flaky tests.

Consider implementing a proper readiness check:

async function waitForViteServer(maxAttempts = 10, interval = 100) {
    for (let i = 0; i < maxAttempts; i++) {
        try {
            const response = await fetch('http://localhost:YOUR_PORT/health');
            if (response.ok) return;
        } catch {
            await sleep(interval);
        }
    }
    throw new Error('Vite server failed to start');
}

Comment on lines +30 to +49
extensionId: async ({ context }, use) => {
// for manifest v3:
let [background] = context.serviceWorkers()
if (!background)
background = await context.waitForEvent('serviceworker')

const extensionId = background.url().split('/')[2]
await use(extensionId)
},
})

export const expect = test.expect

export function isDevArtifact() {
const manifest: Manifest.WebExtensionManifest = JSON.parse(fs.readFileSync(path.resolve(extensionPath, 'manifest.json'), 'utf-8'))
return Boolean(
typeof manifest.content_security_policy === 'object'
&& manifest.content_security_policy.extension_pages?.includes('localhost'),
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance extension ID extraction and manifest parsing robustness

The current implementation could be more resilient to edge cases and provide better type safety.

Consider these improvements:

interface DevManifest extends Manifest.WebExtensionManifest {
    content_security_policy?: {
        extension_pages?: string;
    };
}

export async function getExtensionId(context: BrowserContext): Promise<string> {
    const worker = await context.waitForEvent('serviceworker', {
        timeout: 5000,
        predicate: (worker) => worker.url().includes('chrome-extension://')
    });
    const match = worker.url().match(/chrome-extension:\/\/([^/]+)/);
    if (!match?.[1]) throw new Error('Failed to extract extension ID');
    return match[1];
}

export function isDevArtifact(): boolean {
    try {
        const manifest = JSON.parse(
            fs.readFileSync(path.resolve(extensionPath, 'manifest.json'), 'utf-8')
        ) as DevManifest;
        
        return Boolean(
            manifest.content_security_policy?.extension_pages?.includes('localhost')
        );
    } catch (error) {
        console.error('Failed to parse manifest:', error);
        return false;
    }
}

Comment on lines +1 to +11
import type { Manifest } from 'webextension-polyfill'
import fs from 'node:fs'
import path from 'node:path'
import { setTimeout as sleep } from 'node:timers/promises'

import { test as base, type BrowserContext, chromium } from '@playwright/test'

export { name } from '../../package.json'

export const extensionPath = path.join(__dirname, '../../extension')

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add robust error handling for extension path resolution

The extension path setup lacks validation. Consider adding checks for directory existence and proper extension structure.

 export const extensionPath = path.join(__dirname, '../../extension')
+if (!fs.existsSync(extensionPath)) {
+    throw new Error(`Extension directory not found: ${extensionPath}`);
+}
+if (!fs.existsSync(path.join(extensionPath, 'manifest.json'))) {
+    throw new Error(`manifest.json not found in extension directory`);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import type { Manifest } from 'webextension-polyfill'
import fs from 'node:fs'
import path from 'node:path'
import { setTimeout as sleep } from 'node:timers/promises'
import { test as base, type BrowserContext, chromium } from '@playwright/test'
export { name } from '../../package.json'
export const extensionPath = path.join(__dirname, '../../extension')
import type { Manifest } from 'webextension-polyfill'
import fs from 'node:fs'
import path from 'node:path'
import { setTimeout as sleep } from 'node:timers/promises'
import { test as base, type BrowserContext, chromium } from '@playwright/test'
export { name } from '../../package.json'
export const extensionPath = path.join(__dirname, '../../extension')
if (!fs.existsSync(extensionPath)) {
throw new Error(`Extension directory not found: ${extensionPath}`);
}
if (!fs.existsSync(path.join(extensionPath, 'manifest.json'))) {
throw new Error(`manifest.json not found in extension directory`);
}

@danielroe danielroe merged commit 01b4029 into danielroe:main Dec 21, 2024
7 checks passed
@nickytonline
Copy link
Contributor Author

sorry, got carried away! will merge and iterate in main...

this looks great - thank you ❤️

Yeah a bunch of boilerplate was still in there from the extension template. 😅

@nickytonline nickytonline deleted the web-ext branch December 22, 2024 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

chrome extension for displaying related issues inline
2 participants