From f673099e4b5d253fe0ba1d12cd3833c5b74311e2 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Tue, 9 Sep 2025 17:58:07 -0700 Subject: [PATCH 01/23] Minor cleanup. --- browser-extension/src/entrypoints/background.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/browser-extension/src/entrypoints/background.ts b/browser-extension/src/entrypoints/background.ts index 3e071a1..6cb8e13 100644 --- a/browser-extension/src/entrypoints/background.ts +++ b/browser-extension/src/entrypoints/background.ts @@ -27,14 +27,13 @@ export function handleCommentEvent(message: CommentEvent, sender: any): boolean sender.tab?.windowId ) { if (message.type === 'ENHANCED') { - const tab: Tab = { - tabId: sender.tab.id, - windowId: sender.tab.windowId, - } const commentState: CommentState = { drafts: [], spot: message.spot, - tab, + tab: { + tabId: sender.tab.id, + windowId: sender.tab.windowId, + }, } openSpots.set(message.spot.unique_key, commentState) } else if (message.type === 'DESTROYED') { @@ -52,10 +51,7 @@ export function handlePopupMessage( sendResponse: (response: any) => void, ): typeof CLOSE_MESSAGE_PORT | typeof KEEP_PORT_OPEN { if (isGetOpenSpotsMessage(message)) { - const spots: CommentState[] = [] - for (const [, commentState] of openSpots) { - spots.push(commentState) - } + const spots: CommentState[] = Array.from(openSpots.values()) const response: GetOpenSpotsResponse = { spots } sendResponse(response) return KEEP_PORT_OPEN From b92a4a546b67ae178bf1f8159ed419241ee92918 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 11:46:32 -0700 Subject: [PATCH 02/23] Need to turn off biome `noUnknownAtRules` for tailwind v4 https://github.com/biomejs/biome/pull/7164 --- browser-extension/biome.json | 1 + 1 file changed, 1 insertion(+) diff --git a/browser-extension/biome.json b/browser-extension/biome.json index d4b34d1..54b9867 100644 --- a/browser-extension/biome.json +++ b/browser-extension/biome.json @@ -66,6 +66,7 @@ } }, "noExplicitAny": "off", + "noUnknownAtRules": "off", "noVar": "error" } } From 423a7bc39c7adf89062c4276645a8586f31f6819 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 11:47:26 -0700 Subject: [PATCH 03/23] Working straight from claude, refinement needed. --- browser-extension/package.json | 13 + browser-extension/postcss.config.cjs | 6 + browser-extension/src/components/ui/table.tsx | 91 ++ .../src/entrypoints/popup/index.html | 2 +- .../src/entrypoints/popup/main.ts | 97 --- .../src/entrypoints/popup/main.tsx | 143 +++ .../src/entrypoints/popup/style.css | 49 +- browser-extension/src/lib/enhancer.ts | 3 +- ...ddComment.ts => githubIssueAddComment.tsx} | 10 +- ...PRAddComment.ts => githubPRAddComment.tsx} | 10 +- browser-extension/src/lib/utils.ts | 6 + browser-extension/tailwind.config.cjs | 10 + browser-extension/tsconfig.json | 6 +- browser-extension/wxt.config.ts | 13 + pnpm-lock.yaml | 821 +++++++++++++++++- 15 files changed, 1112 insertions(+), 168 deletions(-) create mode 100644 browser-extension/postcss.config.cjs create mode 100644 browser-extension/src/components/ui/table.tsx delete mode 100644 browser-extension/src/entrypoints/popup/main.ts create mode 100644 browser-extension/src/entrypoints/popup/main.tsx rename browser-extension/src/lib/enhancers/github/{githubIssueAddComment.ts => githubIssueAddComment.tsx} (88%) rename browser-extension/src/lib/enhancers/github/{githubPRAddComment.ts => githubPRAddComment.tsx} (89%) create mode 100644 browser-extension/src/lib/utils.ts create mode 100644 browser-extension/tailwind.config.cjs diff --git a/browser-extension/package.json b/browser-extension/package.json index 128aa79..92790c0 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -1,23 +1,36 @@ { "author": "DiffPlug", "dependencies": { + "@types/react": "^19.1.12", + "@types/react-dom": "^19.1.9", "@wxt-dev/webextension-polyfill": "^1.0.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "highlight.js": "^11.11.1", + "lucide-react": "^0.543.0", "overtype": "workspace:*", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "tailwind-merge": "^3.3.1", "webextension-polyfill": "^0.12.0" }, "description": "Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).", "devDependencies": { "@biomejs/biome": "^2.1.2", "@playwright/test": "^1.46.0", + "@tailwindcss/postcss": "^4.1.13", "@testing-library/jest-dom": "^6.6.4", "@types/express": "^4.17.21", "@types/har-format": "^1.2.16", "@types/node": "^22.16.5", + "@vitejs/plugin-react": "^5.0.2", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", + "autoprefixer": "^10.4.21", "express": "^4.19.2", "linkedom": "^0.18.12", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.13", "tsx": "^4.19.1", "typescript": "^5.8.3", "vitest": "^3.2.4", diff --git a/browser-extension/postcss.config.cjs b/browser-extension/postcss.config.cjs new file mode 100644 index 0000000..dc655aa --- /dev/null +++ b/browser-extension/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/browser-extension/src/components/ui/table.tsx b/browser-extension/src/components/ui/table.tsx new file mode 100644 index 0000000..e8548bf --- /dev/null +++ b/browser-extension/src/components/ui/table.tsx @@ -0,0 +1,91 @@ +import * as React from 'react' + +import { cn } from '@/lib/utils' + +const Table = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ + + ), +) +Table.displayName = 'Table' + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableHeader.displayName = 'TableHeader' + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableBody.displayName = 'TableBody' + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0', className)} + {...props} + /> +)) +TableFooter.displayName = 'TableFooter' + +const TableRow = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +) +TableRow.displayName = 'TableRow' + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableHead.displayName = 'TableHead' + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableCell.displayName = 'TableCell' + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableCaption.displayName = 'TableCaption' + +export { Table, TableHeader, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableRow } diff --git a/browser-extension/src/entrypoints/popup/index.html b/browser-extension/src/entrypoints/popup/index.html index af3b409..8a09020 100644 --- a/browser-extension/src/entrypoints/popup/index.html +++ b/browser-extension/src/entrypoints/popup/index.html @@ -8,6 +8,6 @@
- + \ No newline at end of file diff --git a/browser-extension/src/entrypoints/popup/main.ts b/browser-extension/src/entrypoints/popup/main.ts deleted file mode 100644 index 4ccea9a..0000000 --- a/browser-extension/src/entrypoints/popup/main.ts +++ /dev/null @@ -1,97 +0,0 @@ -import './style.css' -import { logger } from '../../lib/logger' -import type { - GetOpenSpotsMessage, - GetOpenSpotsResponse, - SwitchToTabMessage, -} from '../../lib/messages' -import { EnhancerRegistry } from '../../lib/registries' -import type { CommentState } from '../background' - -// Test basic DOM access -try { - const app = document.getElementById('app')! - logger.debug('Found app element:', app) - app.innerHTML = '
Script is running...
' -} catch (error) { - logger.error('Error accessing DOM:', error) -} - -const enhancers = new EnhancerRegistry() - -async function getOpenSpots(): Promise { - logger.debug('Sending message to background script...') - try { - const message: GetOpenSpotsMessage = { type: 'GET_OPEN_SPOTS' } - const response = (await browser.runtime.sendMessage(message)) as GetOpenSpotsResponse - logger.debug('Received response:', response) - return response.spots || [] - } catch (error) { - logger.error('Error sending message to background:', error) - return [] - } -} - -function switchToTab(tabId: number, windowId: number): void { - // Send message to background script to handle tab switching - // This avoids the popup context being destroyed before completion - const message: SwitchToTabMessage = { - tabId, - type: 'SWITCH_TO_TAB', - windowId, - } - browser.runtime.sendMessage(message) - window.close() -} - -function createSpotElement(commentState: CommentState): HTMLElement { - const item = document.createElement('div') - item.className = 'spot-item' - - logger.debug('Creating spot element for:', commentState.spot) - const enhancer = enhancers.enhancerFor(commentState.spot) - if (!enhancer) { - logger.error('No enhancer found for:', commentState.spot) - logger.error('Only have enhancers for:', enhancers.byType) - } - - const title = document.createElement('div') - title.className = 'spot-title' - title.textContent = enhancer.tableTitle(commentState.spot) - item.appendChild(title) - item.addEventListener('click', () => { - switchToTab(commentState.tab.tabId, commentState.tab.windowId) - }) - return item -} - -async function renderOpenSpots(): Promise { - logger.debug('renderOpenSpots called') - const app = document.getElementById('app')! - const spots = await getOpenSpots() - logger.debug('Got spots:', spots) - - if (spots.length === 0) { - app.innerHTML = '
No open comment spots
' - return - } - - const header = document.createElement('h2') - header.textContent = 'Open Comment Spots' - app.appendChild(header) - - const list = document.createElement('div') - list.className = 'spots-list' - - spots.forEach((spot) => { - list.appendChild(createSpotElement(spot)) - }) - - app.appendChild(list) -} - -renderOpenSpots().catch((error) => { - logger.error('Error in renderOpenSpots:', error) - const app = document.getElementById('app')! - app.innerHTML = `
Error loading spots: ${error.message}
` -}) diff --git a/browser-extension/src/entrypoints/popup/main.tsx b/browser-extension/src/entrypoints/popup/main.tsx new file mode 100644 index 0000000..fb2290b --- /dev/null +++ b/browser-extension/src/entrypoints/popup/main.tsx @@ -0,0 +1,143 @@ +import './style.css' +import React from 'react' +import { createRoot } from 'react-dom/client' +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' +import { cn } from '@/lib/utils' +import { logger } from '../../lib/logger' +import type { + GetOpenSpotsMessage, + GetOpenSpotsResponse, + SwitchToTabMessage, +} from '../../lib/messages' +import { EnhancerRegistry } from '../../lib/registries' +import type { CommentState } from '../background' + +const enhancers = new EnhancerRegistry() + +async function getOpenSpots(): Promise { + logger.debug('Sending message to background script...') + try { + const message: GetOpenSpotsMessage = { type: 'GET_OPEN_SPOTS' } + const response = (await browser.runtime.sendMessage(message)) as GetOpenSpotsResponse + logger.debug('Received response:', response) + return response.spots || [] + } catch (error) { + logger.error('Error sending message to background:', error) + return [] + } +} + +function switchToTab(tabId: number, windowId: number): void { + const message: SwitchToTabMessage = { + tabId, + type: 'SWITCH_TO_TAB', + windowId, + } + browser.runtime.sendMessage(message) + window.close() +} + +interface SpotRowProps { + commentState: CommentState + onClick: () => void +} + +function SpotRow({ commentState, onClick }: SpotRowProps) { + const enhancer = enhancers.enhancerFor(commentState.spot) + + if (!enhancer) { + logger.error('No enhancer found for:', commentState.spot) + logger.error('Only have enhancers for:', enhancers.byType) + return null + } + + return ( + + +
+ {enhancer.tableIcon(commentState.spot)} +
+ {enhancer.tableTitle(commentState.spot)} +
+
+
+
+ ) +} + +function PopupApp() { + const [spots, setSpots] = React.useState([]) + const [isLoading, setIsLoading] = React.useState(true) + + React.useEffect(() => { + const loadSpots = async () => { + try { + const openSpots = await getOpenSpots() + setSpots(openSpots) + } catch (error) { + logger.error('Error loading spots:', error) + } finally { + setIsLoading(false) + } + } + + loadSpots() + }, []) + + if (isLoading) { + return
Loading...
+ } + + if (spots.length === 0) { + return ( +
No open comment spots
+ ) + } + + return ( +
+

Open Comment Spots

+ +
+ + + + Comment Spots + + + + {spots.map((spot) => ( + switchToTab(spot.tab.tabId, spot.tab.windowId)} + /> + ))} + +
+
+
+ ) +} + +// Initialize React app +const app = document.getElementById('app') +if (app) { + const root = createRoot(app) + root.render() +} else { + logger.error('App element not found') +} diff --git a/browser-extension/src/entrypoints/popup/style.css b/browser-extension/src/entrypoints/popup/style.css index 0d63390..b773d69 100644 --- a/browser-extension/src/entrypoints/popup/style.css +++ b/browser-extension/src/entrypoints/popup/style.css @@ -1,3 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + body { width: 300px; padding: 15px; @@ -6,48 +10,3 @@ body { line-height: 1.4; margin: 0; } - -h2 { - margin: 0 0 15px 0; - font-size: 16px; - font-weight: 600; - color: #333; -} - -.spots-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.spot-item { - padding: 10px; - border: 1px solid #e0e0e0; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - background: white; -} - -.spot-item:hover { - background: #f5f5f5; - border-color: #d0d0d0; - transform: translateY(-1px); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.spot-title { - font-weight: 500; - color: #333; - margin-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.no-spots { - text-align: center; - color: #666; - padding: 40px 20px; - font-style: italic; -} diff --git a/browser-extension/src/lib/enhancer.ts b/browser-extension/src/lib/enhancer.ts index 0895454..8d5f661 100644 --- a/browser-extension/src/lib/enhancer.ts +++ b/browser-extension/src/lib/enhancer.ts @@ -1,4 +1,5 @@ import type { OverTypeInstance } from 'overtype' +import type { ReactNode } from 'react' /** * Stores enough info about the location of a draft to: @@ -42,5 +43,5 @@ export interface CommentEnhancer { enhance(textarea: HTMLTextAreaElement, spot: Spot): OverTypeInstance tableIcon(spot: Spot): string - tableTitle(spot: Spot): string + tableTitle(spot: Spot): ReactNode } diff --git a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.ts b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx similarity index 88% rename from browser-extension/src/lib/enhancers/github/githubIssueAddComment.ts rename to browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx index bfdc02f..b17b7f7 100644 --- a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.ts +++ b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx @@ -1,4 +1,5 @@ import OverType, { type OverTypeInstance } from 'overtype' +import type React from 'react' import type { CommentEnhancer, CommentSpot } from '../../enhancer' import { logger } from '../../logger' import { modifyDOM } from '../modifyDOM' @@ -54,9 +55,14 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer + {slug} + Issue #{number} + + ) } tableIcon(_: GitHubIssueAddCommentSpot): string { diff --git a/browser-extension/src/lib/enhancers/github/githubPRAddComment.ts b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx similarity index 89% rename from browser-extension/src/lib/enhancers/github/githubPRAddComment.ts rename to browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx index 8514000..a4ba1bc 100644 --- a/browser-extension/src/lib/enhancers/github/githubPRAddComment.ts +++ b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx @@ -1,4 +1,5 @@ import OverType, { type OverTypeInstance } from 'overtype' +import type React from 'react' import type { CommentEnhancer, CommentSpot } from '../../enhancer' import { logger } from '../../logger' import { modifyDOM } from '../modifyDOM' @@ -58,9 +59,14 @@ export class GitHubPRAddCommentEnhancer implements CommentEnhancer + {slug} + PR #{number} + + ) } tableIcon(_: GitHubPRAddCommentSpot): string { diff --git a/browser-extension/src/lib/utils.ts b/browser-extension/src/lib/utils.ts new file mode 100644 index 0000000..d32b0fe --- /dev/null +++ b/browser-extension/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/browser-extension/tailwind.config.cjs b/browser-extension/tailwind.config.cjs new file mode 100644 index 0000000..08621b4 --- /dev/null +++ b/browser-extension/tailwind.config.cjs @@ -0,0 +1,10 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} \ No newline at end of file diff --git a/browser-extension/tsconfig.json b/browser-extension/tsconfig.json index 90169d5..d0ce398 100644 --- a/browser-extension/tsconfig.json +++ b/browser-extension/tsconfig.json @@ -8,9 +8,13 @@ "esModuleInterop": true, "exactOptionalPropertyTypes": true, "forceConsistentCasingInFileNames": true, - "jsx": "preserve", + "jsx": "react-jsx", "lib": ["ES2022", "DOM", "DOM.Iterable"], "moduleResolution": "bundler", + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, // Emit "noEmit": true, diff --git a/browser-extension/wxt.config.ts b/browser-extension/wxt.config.ts index 7a4699a..826fb93 100644 --- a/browser-extension/wxt.config.ts +++ b/browser-extension/wxt.config.ts @@ -1,6 +1,19 @@ import { defineConfig } from 'wxt' +import react from '@vitejs/plugin-react' +import path from 'path' export default defineConfig({ + vite: () => ({ + plugins: [react()], + css: { + postcss: path.resolve('./postcss.config.cjs') + }, + resolve: { + alias: { + '@': path.resolve('./src') + } + } + }), manifest: { description: 'Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8aa1ce..bf482a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,15 +10,39 @@ importers: browser-extension: dependencies: + '@types/react': + specifier: ^19.1.12 + version: 19.1.12 + '@types/react-dom': + specifier: ^19.1.9 + version: 19.1.9(@types/react@19.1.12) '@wxt-dev/webextension-polyfill': specifier: ^1.0.0 - version: 1.0.0(webextension-polyfill@0.12.0)(wxt@0.20.11(@types/node@22.18.1)(jiti@2.5.1)(rollup@4.50.1)(tsx@4.20.5)) + version: 1.0.0(webextension-polyfill@0.12.0)(wxt@0.20.11(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.50.1)(tsx@4.20.5)) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 highlight.js: specifier: ^11.11.1 version: 11.11.1 + lucide-react: + specifier: ^0.543.0 + version: 0.543.0(react@19.1.1) overtype: specifier: workspace:* version: link:../packages/overtype + react: + specifier: ^19.1.1 + version: 19.1.1 + react-dom: + specifier: ^19.1.1 + version: 19.1.1(react@19.1.1) + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 webextension-polyfill: specifier: ^0.12.0 version: 0.12.0 @@ -29,6 +53,9 @@ importers: '@playwright/test': specifier: ^1.46.0 version: 1.55.0 + '@tailwindcss/postcss': + specifier: ^4.1.13 + version: 4.1.13 '@testing-library/jest-dom': specifier: ^6.6.4 version: 6.8.0 @@ -41,18 +68,30 @@ importers: '@types/node': specifier: ^22.16.5 version: 22.18.1 + '@vitejs/plugin-react': + specifier: ^5.0.2 + version: 5.0.2(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) '@vitest/coverage-v8': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4) '@vitest/ui': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) express: specifier: ^4.19.2 version: 4.21.2 linkedom: specifier: ^0.18.12 version: 0.18.12 + postcss: + specifier: ^8.5.6 + version: 8.5.6 + tailwindcss: + specifier: ^4.1.13 + version: 4.1.13 tsx: specifier: ^4.19.1 version: 4.20.5 @@ -61,10 +100,10 @@ importers: version: 5.9.2 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(tsx@4.20.5) + version: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.20.5) wxt: specifier: ^0.20.7 - version: 0.20.11(@types/node@22.18.1)(jiti@2.5.1)(rollup@4.50.1)(tsx@4.20.5) + version: 0.20.11(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.50.1)(tsx@4.20.5) packages/overtype: dependencies: @@ -106,6 +145,10 @@ packages: rollup: optional: true + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -117,6 +160,40 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -125,15 +202,43 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.28.4': resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/runtime@7.28.2': resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.4': resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} @@ -542,6 +647,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -598,6 +707,9 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@rolldown/pluginutils@1.0.0-beta.34': + resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} + '@rollup/rollup-android-arm-eabi@4.50.1': resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} cpu: [arm] @@ -703,10 +815,110 @@ packages: cpu: [x64] os: [win32] + '@tailwindcss/node@4.1.13': + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + + '@tailwindcss/oxide-android-arm64@4.1.13': + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.13': + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.13': + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.13': + resolution: {integrity: sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==} + '@testing-library/jest-dom@6.8.0': resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -761,6 +973,14 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/react-dom@19.1.9': + resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.1.12': + resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==} + '@types/send@0.17.5': resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} @@ -770,6 +990,12 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@vitejs/plugin-react@5.0.2': + resolution: {integrity: sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/coverage-v8@3.2.4': resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} peerDependencies: @@ -916,6 +1142,13 @@ packages: atomically@2.0.3: resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -953,6 +1186,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -1002,6 +1240,9 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} + chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -1022,6 +1263,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-launcher@1.2.0: resolution: {integrity: sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==} engines: {node: '>=12.13.0'} @@ -1034,6 +1279,9 @@ packages: citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-boxes@3.0.0: resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} engines: {node: '>=10'} @@ -1070,6 +1318,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1120,6 +1372,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -1155,6 +1410,9 @@ packages: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -1248,6 +1506,10 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} @@ -1293,6 +1555,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.215: + resolution: {integrity: sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ==} + emoji-regex@10.5.0: resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} @@ -1313,6 +1578,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1475,6 +1744,9 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -1500,6 +1772,10 @@ packages: resolution: {integrity: sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==} hasBin: true + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1846,6 +2122,11 @@ packages: canvas: optional: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -1886,6 +2167,70 @@ packages: lighthouse-logger@2.0.2: resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -1944,10 +2289,18 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + lucide-react@0.543.0: + resolution: {integrity: sha512-fpVfuOQO0V3HBaOA1stIiP/A2fPCXHIleRZL16Mx3HmjTYwNSbimhnFBygs2CAfU1geexMX5ItUcWBGUaqw5CA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} @@ -2052,6 +2405,15 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -2095,6 +2457,9 @@ packages: node-notifier@10.0.1: resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==} + node-releases@2.0.20: + resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==} + normalize-package-data@3.0.3: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} @@ -2103,6 +2468,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -2285,6 +2654,9 @@ packages: resolution: {integrity: sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==} engines: {node: '>= 10.12'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -2366,6 +2738,19 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + read-pkg-up@8.0.0: resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} engines: {node: '>=12'} @@ -2465,12 +2850,19 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} secure-compare@3.0.1: resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -2666,6 +3058,20 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwindcss@4.1.13: + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + test-exclude@7.0.1: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} @@ -2808,6 +3214,12 @@ packages: resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} engines: {node: '>=18.12.0'} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + update-notifier@7.3.1: resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} engines: {node: '>=18'} @@ -3032,9 +3444,16 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -3082,6 +3501,8 @@ snapshots: optionalDependencies: rollup: 4.50.1 + '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -3101,16 +3522,109 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.28.4': {} + + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + '@babel/parser@7.28.4': dependencies: '@babel/types': 7.28.4 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/runtime@7.28.2': {} + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -3351,6 +3865,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@istanbuljs/schema@0.1.3': {} '@jridgewell/gen-mapping@0.3.13': @@ -3405,6 +3923,8 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@rolldown/pluginutils@1.0.0-beta.34': {} + '@rollup/rollup-android-arm-eabi@4.50.1': optional: true @@ -3468,6 +3988,78 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.50.1': optional: true + '@tailwindcss/node@4.1.13': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.13 + + '@tailwindcss/oxide-android-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide@4.1.13': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-x64': 4.1.13 + '@tailwindcss/oxide-freebsd-x64': 4.1.13 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + + '@tailwindcss/postcss@4.1.13': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.13 + '@tailwindcss/oxide': 4.1.13 + postcss: 8.5.6 + tailwindcss: 4.1.13 + '@testing-library/jest-dom@6.8.0': dependencies: '@adobe/css-tools': 4.4.4 @@ -3477,6 +4069,27 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.4 + '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -3534,6 +4147,14 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/react-dom@19.1.9(@types/react@19.1.12)': + dependencies: + '@types/react': 19.1.12 + + '@types/react@19.1.12': + dependencies: + csstype: 3.1.3 + '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 @@ -3550,6 +4171,18 @@ snapshots: '@types/node': 22.18.1 optional: true + '@vitejs/plugin-react@5.0.2(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.34 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-v8@3.2.4(vitest@3.2.4)': dependencies: '@ampproject/remapping': 2.3.0 @@ -3565,7 +4198,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(tsx@4.20.5) + vitest: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.20.5) transitivePeerDependencies: - supports-color @@ -3577,13 +4210,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5))': + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) '@vitest/pretty-format@3.2.4': dependencies: @@ -3614,7 +4247,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(tsx@4.20.5) + vitest: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.20.5) '@vitest/utils@3.2.4': dependencies: @@ -3643,10 +4276,10 @@ snapshots: async-mutex: 0.5.0 dequal: 2.0.3 - '@wxt-dev/webextension-polyfill@1.0.0(webextension-polyfill@0.12.0)(wxt@0.20.11(@types/node@22.18.1)(jiti@2.5.1)(rollup@4.50.1)(tsx@4.20.5))': + '@wxt-dev/webextension-polyfill@1.0.0(webextension-polyfill@0.12.0)(wxt@0.20.11(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.50.1)(tsx@4.20.5))': dependencies: webextension-polyfill: 0.12.0 - wxt: 0.20.11(@types/node@22.18.1)(jiti@2.5.1)(rollup@4.50.1)(tsx@4.20.5) + wxt: 0.20.11(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.50.1)(tsx@4.20.5) accepts@1.3.8: dependencies: @@ -3710,6 +4343,16 @@ snapshots: stubborn-fs: 1.2.5 when-exit: 2.1.4 + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.25.4 + caniuse-lite: 1.0.30001741 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -3769,6 +4412,13 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.25.4: + dependencies: + caniuse-lite: 1.0.30001741 + electron-to-chromium: 1.5.215 + node-releases: 2.0.20 + update-browserslist-db: 1.1.3(browserslist@4.25.4) + buffer-crc32@0.2.13: {} buffer-from@1.1.2: {} @@ -3824,6 +4474,8 @@ snapshots: camelcase@8.0.0: {} + caniuse-lite@1.0.30001741: {} + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -3845,6 +4497,8 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@3.0.0: {} + chrome-launcher@1.2.0: dependencies: '@types/node': 22.18.1 @@ -3860,6 +4514,10 @@ snapshots: dependencies: consola: 3.4.2 + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-boxes@3.0.0: {} cli-cursor@4.0.0: @@ -3900,6 +4558,8 @@ snapshots: clone@1.0.4: {} + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3947,6 +4607,8 @@ snapshots: content-type@1.0.5: {} + convert-source-map@2.0.0: {} + cookie-signature@1.0.6: {} cookie@0.7.1: {} @@ -3980,6 +4642,8 @@ snapshots: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 + csstype@3.1.3: {} + data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -4039,6 +4703,8 @@ snapshots: destroy@1.2.0: {} + detect-libc@2.0.4: {} + dom-accessibility-api@0.6.3: {} dom-serializer@2.0.0: @@ -4083,6 +4749,8 @@ snapshots: ee-first@1.1.1: {} + electron-to-chromium@1.5.215: {} + emoji-regex@10.5.0: {} emoji-regex@8.0.0: {} @@ -4097,6 +4765,11 @@ snapshots: dependencies: once: 1.4.0 + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.3 + entities@4.5.0: {} entities@6.0.1: {} @@ -4312,6 +4985,8 @@ snapshots: forwarded@0.2.0: {} + fraction.js@4.3.7: {} + fresh@0.5.2: {} fs-extra@11.3.1: @@ -4337,6 +5012,8 @@ snapshots: which: 1.2.4 winreg: 0.0.12 + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} get-east-asian-width@1.3.1: {} @@ -4678,6 +5355,8 @@ snapshots: - supports-color - utf-8-validate + jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} json-parse-even-better-errors@3.0.2: {} @@ -4718,6 +5397,51 @@ snapshots: transitivePeerDependencies: - supports-color + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + lines-and-columns@1.2.4: {} lines-and-columns@2.0.4: {} @@ -4779,10 +5503,18 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lru-cache@6.0.0: dependencies: yallist: 4.0.0 + lucide-react@0.543.0(react@19.1.1): + dependencies: + react: 19.1.1 + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4875,6 +5607,12 @@ snapshots: minipass@7.1.2: {} + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@3.0.1: {} + mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -4920,6 +5658,8 @@ snapshots: uuid: 8.3.2 which: 2.0.2 + node-releases@2.0.20: {} + normalize-package-data@3.0.3: dependencies: hosted-git-info: 4.1.0 @@ -4929,6 +5669,8 @@ snapshots: normalize-path@3.0.0: {} + normalize-range@0.1.2: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -5136,6 +5878,8 @@ snapshots: transitivePeerDependencies: - supports-color + postcss-value-parser@4.2.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -5232,6 +5976,15 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + + react-refresh@0.17.0: {} + + react@19.1.1: {} + read-pkg-up@8.0.0: dependencies: find-up: 5.0.0 @@ -5352,10 +6105,14 @@ snapshots: dependencies: xmlchars: 2.2.0 + scheduler@0.26.0: {} + scule@1.3.0: {} secure-compare@3.0.1: {} + semver@6.3.1: {} + semver@7.7.2: {} send@0.19.0: @@ -5568,6 +6325,21 @@ snapshots: symbol-tree@3.2.4: {} + tailwind-merge@3.3.1: {} + + tailwindcss@4.1.13: {} + + tapable@2.2.3: {} + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + test-exclude@7.0.1: dependencies: '@istanbuljs/schema': 0.1.3 @@ -5698,6 +6470,12 @@ snapshots: picomatch: 4.0.3 webpack-virtual-modules: 0.6.2 + update-browserslist-db@1.1.3(browserslist@4.25.4): + dependencies: + browserslist: 4.25.4 + escalade: 3.2.0 + picocolors: 1.1.1 + update-notifier@7.3.1: dependencies: boxen: 8.0.1 @@ -5726,13 +6504,13 @@ snapshots: vary@1.1.2: {} - vite-node@3.2.4(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5): + vite-node@3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) transitivePeerDependencies: - '@types/node' - jiti @@ -5747,7 +6525,7 @@ snapshots: - tsx - yaml - vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5): + vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) @@ -5759,13 +6537,14 @@ snapshots: '@types/node': 22.18.1 fsevents: 2.3.3 jiti: 2.5.1 + lightningcss: 1.30.1 tsx: 4.20.5 - vitest@3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(tsx@4.20.5): + vitest@3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.20.5): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -5783,8 +6562,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5) - vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.18.1 @@ -5913,7 +6692,7 @@ snapshots: dependencies: is-wsl: 3.1.0 - wxt@0.20.11(@types/node@22.18.1)(jiti@2.5.1)(rollup@4.50.1)(tsx@4.20.5): + wxt@0.20.11(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.50.1)(tsx@4.20.5): dependencies: '@1natsu/wait-element': 4.1.2 '@aklinker1/rollup-plugin-visualizer': 5.12.0(rollup@4.50.1) @@ -5957,8 +6736,8 @@ snapshots: publish-browser-extension: 3.0.2 scule: 1.3.0 unimport: 5.2.0 - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5) - vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(tsx@4.20.5) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) web-ext-run: 0.2.4 transitivePeerDependencies: - '@types/node' @@ -5991,8 +6770,12 @@ snapshots: y18n@5.0.8: {} + yallist@3.1.1: {} + yallist@4.0.0: {} + yallist@5.0.0: {} + yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} From 9377e7a761f614abd002592dc799d2c5dca92f51 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 11:53:50 -0700 Subject: [PATCH 04/23] `CommentEnhancer` now just has `tableRow(spot: Spot): ReactNode` instead of title/icon --- browser-extension/src/entrypoints/popup/main.tsx | 3 +-- browser-extension/src/lib/enhancer.ts | 5 ++--- .../src/lib/enhancers/github/githubIssueAddComment.tsx | 10 +--------- .../src/lib/enhancers/github/githubPRAddComment.tsx | 10 +--------- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/browser-extension/src/entrypoints/popup/main.tsx b/browser-extension/src/entrypoints/popup/main.tsx index fb2290b..da52da4 100644 --- a/browser-extension/src/entrypoints/popup/main.tsx +++ b/browser-extension/src/entrypoints/popup/main.tsx @@ -68,9 +68,8 @@ function SpotRow({ commentState, onClick }: SpotRowProps) { >
- {enhancer.tableIcon(commentState.spot)}
- {enhancer.tableTitle(commentState.spot)} + {enhancer.tableRow(commentState.spot)}
diff --git a/browser-extension/src/lib/enhancer.ts b/browser-extension/src/lib/enhancer.ts index 8d5f661..06b294f 100644 --- a/browser-extension/src/lib/enhancer.ts +++ b/browser-extension/src/lib/enhancer.ts @@ -41,7 +41,6 @@ export interface CommentEnhancer { * exactly once since pageload before this gets called. */ enhance(textarea: HTMLTextAreaElement, spot: Spot): OverTypeInstance - - tableIcon(spot: Spot): string - tableTitle(spot: Spot): ReactNode + /** Returns a ReactNode which will be displayed in the table row. */ + tableRow(spot: Spot): ReactNode } diff --git a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx index b17b7f7..206d189 100644 --- a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx +++ b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx @@ -55,7 +55,7 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer @@ -64,12 +64,4 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer ) } - - tableIcon(_: GitHubIssueAddCommentSpot): string { - return '🔄' // PR icon TODO: icon urls in /public - } - - buildUrl(spot: GitHubIssueAddCommentSpot): string { - return `https://${spot.domain}/${spot.slug}/issue/${spot.number}` - } } diff --git a/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx index a4ba1bc..1851fc7 100644 --- a/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx +++ b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx @@ -59,7 +59,7 @@ export class GitHubPRAddCommentEnhancer implements CommentEnhancer @@ -68,12 +68,4 @@ export class GitHubPRAddCommentEnhancer implements CommentEnhancer ) } - - tableIcon(_: GitHubPRAddCommentSpot): string { - return '🔄' // PR icon TODO: icon urls in /public - } - - buildUrl(spot: GitHubPRAddCommentSpot): string { - return `https://${spot.domain}/${spot.slug}/pull/${spot.number}` - } } From 113586cdc78df279dd525bcf302c0f976b42fca5 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 12:08:13 -0700 Subject: [PATCH 05/23] Take full advantage of tailwind v4. --- browser-extension/package.json | 4 +- browser-extension/postcss.config.cjs | 6 --- browser-extension/tailwind.config.cjs | 10 ----- browser-extension/wxt.config.ts | 6 +-- pnpm-lock.yaml | 60 ++++----------------------- 5 files changed, 11 insertions(+), 75 deletions(-) delete mode 100644 browser-extension/postcss.config.cjs delete mode 100644 browser-extension/tailwind.config.cjs diff --git a/browser-extension/package.json b/browser-extension/package.json index 92790c0..4f5b098 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@biomejs/biome": "^2.1.2", "@playwright/test": "^1.46.0", - "@tailwindcss/postcss": "^4.1.13", + "@tailwindcss/vite": "^4.1.13", "@testing-library/jest-dom": "^6.6.4", "@types/express": "^4.17.21", "@types/har-format": "^1.2.16", @@ -26,10 +26,8 @@ "@vitejs/plugin-react": "^5.0.2", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", - "autoprefixer": "^10.4.21", "express": "^4.19.2", "linkedom": "^0.18.12", - "postcss": "^8.5.6", "tailwindcss": "^4.1.13", "tsx": "^4.19.1", "typescript": "^5.8.3", diff --git a/browser-extension/postcss.config.cjs b/browser-extension/postcss.config.cjs deleted file mode 100644 index dc655aa..0000000 --- a/browser-extension/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - '@tailwindcss/postcss': {}, - autoprefixer: {}, - }, -} \ No newline at end of file diff --git a/browser-extension/tailwind.config.cjs b/browser-extension/tailwind.config.cjs deleted file mode 100644 index 08621b4..0000000 --- a/browser-extension/tailwind.config.cjs +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: {}, - }, - plugins: [], -} \ No newline at end of file diff --git a/browser-extension/wxt.config.ts b/browser-extension/wxt.config.ts index 826fb93..6c543af 100644 --- a/browser-extension/wxt.config.ts +++ b/browser-extension/wxt.config.ts @@ -1,13 +1,11 @@ import { defineConfig } from 'wxt' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' import path from 'path' export default defineConfig({ vite: () => ({ - plugins: [react()], - css: { - postcss: path.resolve('./postcss.config.cjs') - }, + plugins: [react(), tailwindcss()], resolve: { alias: { '@': path.resolve('./src') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf482a3..eae8263 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,9 +53,9 @@ importers: '@playwright/test': specifier: ^1.46.0 version: 1.55.0 - '@tailwindcss/postcss': + '@tailwindcss/vite': specifier: ^4.1.13 - version: 4.1.13 + version: 4.1.13(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) '@testing-library/jest-dom': specifier: ^6.6.4 version: 6.8.0 @@ -77,18 +77,12 @@ importers: '@vitest/ui': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4) - autoprefixer: - specifier: ^10.4.21 - version: 10.4.21(postcss@8.5.6) express: specifier: ^4.19.2 version: 4.21.2 linkedom: specifier: ^0.18.12 version: 0.18.12 - postcss: - specifier: ^8.5.6 - version: 8.5.6 tailwindcss: specifier: ^4.1.13 version: 4.1.13 @@ -145,10 +139,6 @@ packages: rollup: optional: true - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -900,8 +890,10 @@ packages: resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.13': - resolution: {integrity: sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==} + '@tailwindcss/vite@4.1.13': + resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 '@testing-library/jest-dom@6.8.0': resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} @@ -1142,13 +1134,6 @@ packages: atomically@2.0.3: resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} - autoprefixer@10.4.21: - resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1744,9 +1729,6 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - fraction.js@4.3.7: - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -2468,10 +2450,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -2654,9 +2632,6 @@ packages: resolution: {integrity: sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==} engines: {node: '>= 10.12'} - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -3501,8 +3476,6 @@ snapshots: optionalDependencies: rollup: 4.50.1 - '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -4052,13 +4025,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 - '@tailwindcss/postcss@4.1.13': + '@tailwindcss/vite@4.1.13(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': dependencies: - '@alloc/quick-lru': 5.2.0 '@tailwindcss/node': 4.1.13 '@tailwindcss/oxide': 4.1.13 - postcss: 8.5.6 tailwindcss: 4.1.13 + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) '@testing-library/jest-dom@6.8.0': dependencies: @@ -4343,16 +4315,6 @@ snapshots: stubborn-fs: 1.2.5 when-exit: 2.1.4 - autoprefixer@10.4.21(postcss@8.5.6): - dependencies: - browserslist: 4.25.4 - caniuse-lite: 1.0.30001741 - fraction.js: 4.3.7 - normalize-range: 0.1.2 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -4985,8 +4947,6 @@ snapshots: forwarded@0.2.0: {} - fraction.js@4.3.7: {} - fresh@0.5.2: {} fs-extra@11.3.1: @@ -5669,8 +5629,6 @@ snapshots: normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -5878,8 +5836,6 @@ snapshots: transitivePeerDependencies: - supports-color - postcss-value-parser@4.2.0: {} - postcss@8.5.6: dependencies: nanoid: 3.3.11 From 4ea95c03888acdb67a982b9758b1908700e555b0 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 13:35:28 -0700 Subject: [PATCH 06/23] Update README. --- browser-extension/README.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/browser-extension/README.md b/browser-extension/README.md index cb2641f..543713b 100644 --- a/browser-extension/README.md +++ b/browser-extension/README.md @@ -31,17 +31,26 @@ This is a [WXT](https://wxt.dev/)-based browser extension that - finds `textarea` components and decorates them with [overtype](https://overtype.dev/) and [highlight.js](https://highlightjs.org/) - stores unposted comment drafts, and makes them easy to find via the extension popup +### Tech Stack + +- **Framework**: [WXT](https://wxt.dev/) for browser extension development +- **UI**: React with TypeScript JSX +- **Styling**: Tailwind CSS v4 (with first-party Vite plugin) +- **Components**: shadcn/ui for table components +- **Editor Enhancement**: [Overtype](https://overtype.dev/) with syntax highlighting +- **Build**: Vite with React plugin + ### Entry points - `src/entrypoints/content.ts` - injected into every webpage - `src/entrypoints/background.ts` - service worker that manages state and handles messages -- `src/entrypoints/popup` - html/css/ts which opens when the extension's button gets clicked +- `src/entrypoints/popup` - React-based popup (html/css/tsx) with shadcn/ui table components ```mermaid graph TD Content[Content Script
content.ts] Background[Background Script
background.ts] - Popup[Popup Script
popup/main.ts] + Popup[Popup Script
popup/main.tsx] Content -->|ENHANCED/DESTROYED
CommentEvent| Background Popup -->|GET_OPEN_SPOTS
SWITCH_TO_TAB| Background @@ -62,11 +71,11 @@ graph TD ### Architecture -Every time a `textarea` shows up on a page, on initial load or later on, it gets passed to a list of `CommentEnhancer`s. Each one gets a turn to say "I can enhance this box!". They show that they can enhance it by returning a [`CommentSpot`, `Overtype`]. +Every time a `textarea` shows up on a page, on initial load or later on, it gets passed to a list of `CommentEnhancer`s. Each one gets a turn to say "I can enhance this box!". They show that they can enhance it by returning something non-null in the method `tryToEnhance(textarea: HTMLTextAreaElement): Spot | null`. Later on, that same `Spot` data will be used by the `tableRow(spot: Spot): ReactNode` method to create React components for rich formatting in the popup table. -Those values get bundled up with the `HTMLTextAreaElement` itself into an `EnhancedTextarea`, which gets added to the `TextareaRegistry`. At some interval, draft edits will get saved by the browser extension (TODO). +Those `Spot` values get bundled up with the `HTMLTextAreaElement` itself into an `EnhancedTextarea`, which gets added to the `TextareaRegistry`. At some interval, draft edits get saved by the browser extension. -When the `textarea` gets removed from the page, the `TextareaRegistry` is notified so that the `CommentSpot` can be marked as abandoned or submitted as appropriate (TODO). +When the `textarea` gets removed from the page, the `TextareaRegistry` is notified so that the `CommentSpot` can be marked as abandoned or submitted as appropriate. ## Testing From 49bc9b31f1de009a7049b4afd47b4c6af0a6bd05 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 13:43:58 -0700 Subject: [PATCH 07/23] Propery initialize shadcn/ui --- browser-extension/components.json | 21 ++++++ browser-extension/package.json | 1 + .../src/components/ui/button.tsx | 56 +++++++++++++++ .../src/entrypoints/background.ts | 6 +- .../src/entrypoints/popup/main.tsx | 8 +-- browser-extension/src/styles/globals.css | 69 +++++++++++++++++++ pnpm-lock.yaml | 34 +++++++++ 7 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 browser-extension/components.json create mode 100644 browser-extension/src/components/ui/button.tsx create mode 100644 browser-extension/src/styles/globals.css diff --git a/browser-extension/components.json b/browser-extension/components.json new file mode 100644 index 0000000..cd33c15 --- /dev/null +++ b/browser-extension/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/styles/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/browser-extension/package.json b/browser-extension/package.json index 4f5b098..5b33068 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -1,6 +1,7 @@ { "author": "DiffPlug", "dependencies": { + "@radix-ui/react-slot": "^1.2.3", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", "@wxt-dev/webextension-polyfill": "^1.0.0", diff --git a/browser-extension/src/components/ui/button.tsx b/browser-extension/src/components/ui/button.tsx new file mode 100644 index 0000000..36496a2 --- /dev/null +++ b/browser-extension/src/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/browser-extension/src/entrypoints/background.ts b/browser-extension/src/entrypoints/background.ts index 6cb8e13..3dcdf7d 100644 --- a/browser-extension/src/entrypoints/background.ts +++ b/browser-extension/src/entrypoints/background.ts @@ -1,12 +1,12 @@ -import type { CommentDraft, CommentEvent, CommentSpot } from '../lib/enhancer' -import type { GetOpenSpotsResponse, ToBackgroundMessage } from '../lib/messages' +import type { CommentDraft, CommentEvent, CommentSpot } from '@/lib/enhancer' +import type { GetOpenSpotsResponse, ToBackgroundMessage } from '@/lib/messages' import { CLOSE_MESSAGE_PORT, isContentToBackgroundMessage, isGetOpenSpotsMessage, isSwitchToTabMessage, KEEP_PORT_OPEN, -} from '../lib/messages' +} from '@/lib/messages' export interface Tab { tabId: number diff --git a/browser-extension/src/entrypoints/popup/main.tsx b/browser-extension/src/entrypoints/popup/main.tsx index da52da4..dff0a45 100644 --- a/browser-extension/src/entrypoints/popup/main.tsx +++ b/browser-extension/src/entrypoints/popup/main.tsx @@ -10,14 +10,14 @@ import { TableRow, } from '@/components/ui/table' import { cn } from '@/lib/utils' -import { logger } from '../../lib/logger' +import { logger } from '@/lib/logger' import type { GetOpenSpotsMessage, GetOpenSpotsResponse, SwitchToTabMessage, -} from '../../lib/messages' -import { EnhancerRegistry } from '../../lib/registries' -import type { CommentState } from '../background' +} from '@/lib/messages' +import { EnhancerRegistry } from '@/lib/registries' +import type { CommentState } from '@/entrypoints/background' const enhancers = new EnhancerRegistry() diff --git a/browser-extension/src/styles/globals.css b/browser-extension/src/styles/globals.css new file mode 100644 index 0000000..494f343 --- /dev/null +++ b/browser-extension/src/styles/globals.css @@ -0,0 +1,69 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eae8263..1671ad6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,9 @@ importers: browser-extension: dependencies: + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.1.12)(react@19.1.1) '@types/react': specifier: ^19.1.12 version: 19.1.12 @@ -697,6 +700,24 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rolldown/pluginutils@1.0.0-beta.34': resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} @@ -3896,6 +3917,19 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.12)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.12 + + '@radix-ui/react-slot@1.2.3(@types/react@19.1.12)(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.12 + '@rolldown/pluginutils@1.0.0-beta.34': {} '@rollup/rollup-android-arm-eabi@4.50.1': From 1274c11933acb2f6f577e04a6d63dfa79d3833a9 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 13:48:35 -0700 Subject: [PATCH 08/23] biome:fix --- .../src/components/ui/button.tsx | 59 ++++++++----------- browser-extension/src/entrypoints/content.ts | 10 ++-- .../src/entrypoints/popup/main.tsx | 10 +--- .../github/githubIssueAddComment.tsx | 4 +- .../enhancers/github/githubPRAddComment.tsx | 4 +- browser-extension/src/styles/globals.css | 2 +- 6 files changed, 39 insertions(+), 50 deletions(-) diff --git a/browser-extension/src/components/ui/button.tsx b/browser-extension/src/components/ui/button.tsx index 36496a2..80f6691 100644 --- a/browser-extension/src/components/ui/button.tsx +++ b/browser-extension/src/components/ui/button.tsx @@ -1,36 +1,33 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from '@radix-ui/react-slot' +import { cva, type VariantProps } from 'class-variance-authority' +import * as React from 'react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', { + defaultVariants: { + size: 'default', + variant: 'default', + }, variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", + default: 'h-10 px-4 py-2', + icon: 'h-10 w-10', + lg: 'h-11 rounded-md px-8', + sm: 'h-9 rounded-md px-3', + }, + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', }, }, - defaultVariants: { - variant: "default", - size: "default", - }, - } + }, ) export interface ButtonProps @@ -41,16 +38,12 @@ export interface ButtonProps const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : 'button' return ( - + ) - } + }, ) -Button.displayName = "Button" +Button.displayName = 'Button' export { Button, buttonVariants } diff --git a/browser-extension/src/entrypoints/content.ts b/browser-extension/src/entrypoints/content.ts index 83de338..c005929 100644 --- a/browser-extension/src/entrypoints/content.ts +++ b/browser-extension/src/entrypoints/content.ts @@ -1,8 +1,8 @@ -import { CONFIG, type ModeType } from '../lib/config' -import type { CommentEvent, CommentSpot } from '../lib/enhancer' -import { logger } from '../lib/logger' -import { EnhancerRegistry, TextareaRegistry } from '../lib/registries' -import { githubPrNewCommentContentScript } from '../playgrounds/github-playground' +import { CONFIG, type ModeType } from '@/lib/config' +import type { CommentEvent, CommentSpot } from '@/lib/enhancer' +import { logger } from '@/lib/logger' +import { EnhancerRegistry, TextareaRegistry } from '@/lib/registries' +import { githubPrNewCommentContentScript } from '@/playgrounds/github-playground' const enhancers = new EnhancerRegistry() const enhancedTextareas = new TextareaRegistry() diff --git a/browser-extension/src/entrypoints/popup/main.tsx b/browser-extension/src/entrypoints/popup/main.tsx index dff0a45..6ddbb2b 100644 --- a/browser-extension/src/entrypoints/popup/main.tsx +++ b/browser-extension/src/entrypoints/popup/main.tsx @@ -9,15 +9,11 @@ import { TableHeader, TableRow, } from '@/components/ui/table' -import { cn } from '@/lib/utils' +import type { CommentState } from '@/entrypoints/background' import { logger } from '@/lib/logger' -import type { - GetOpenSpotsMessage, - GetOpenSpotsResponse, - SwitchToTabMessage, -} from '@/lib/messages' +import type { GetOpenSpotsMessage, GetOpenSpotsResponse, SwitchToTabMessage } from '@/lib/messages' import { EnhancerRegistry } from '@/lib/registries' -import type { CommentState } from '@/entrypoints/background' +import { cn } from '@/lib/utils' const enhancers = new EnhancerRegistry() diff --git a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx index 206d189..84d4cf4 100644 --- a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx +++ b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx @@ -1,7 +1,7 @@ import OverType, { type OverTypeInstance } from 'overtype' import type React from 'react' -import type { CommentEnhancer, CommentSpot } from '../../enhancer' -import { logger } from '../../logger' +import type { CommentEnhancer, CommentSpot } from '@/lib/enhancer' +import { logger } from '@/lib/logger' import { modifyDOM } from '../modifyDOM' import { githubHighlighter } from './githubHighlighter' diff --git a/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx index 1851fc7..d228196 100644 --- a/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx +++ b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx @@ -1,7 +1,7 @@ import OverType, { type OverTypeInstance } from 'overtype' import type React from 'react' -import type { CommentEnhancer, CommentSpot } from '../../enhancer' -import { logger } from '../../logger' +import type { CommentEnhancer, CommentSpot } from '@/lib/enhancer' +import { logger } from '@/lib/logger' import { modifyDOM } from '../modifyDOM' import { githubHighlighter } from './githubHighlighter' diff --git a/browser-extension/src/styles/globals.css b/browser-extension/src/styles/globals.css index 494f343..3094824 100644 --- a/browser-extension/src/styles/globals.css +++ b/browser-extension/src/styles/globals.css @@ -66,4 +66,4 @@ body { @apply bg-background text-foreground; } -} \ No newline at end of file +} From 2a72e6f75ede2d77dac7d9c00dd02b837b4344a1 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 14:35:54 -0700 Subject: [PATCH 09/23] Add a playground for the table with hotreload. --- browser-extension/package.json | 1 + .../github/githubIssueAddComment.tsx | 2 +- .../enhancers/github/githubPRAddComment.tsx | 2 +- .../tests/lib/enhancers/github.test.ts | 37 +++++++++- .../tests/playground/TablePlayground.tsx | 74 +++++++++++++++++++ browser-extension/tests/playground/index.html | 13 ++++ browser-extension/tests/playground/main.tsx | 28 +++++++ .../tests/playground/mockData.tsx | 43 +++++++++++ browser-extension/tests/playground/style.css | 16 ++++ browser-extension/vite.playground.config.ts | 26 +++++++ 10 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 browser-extension/tests/playground/TablePlayground.tsx create mode 100644 browser-extension/tests/playground/index.html create mode 100644 browser-extension/tests/playground/main.tsx create mode 100644 browser-extension/tests/playground/mockData.tsx create mode 100644 browser-extension/tests/playground/style.css create mode 100644 browser-extension/vite.playground.config.ts diff --git a/browser-extension/package.json b/browser-extension/package.json index 5b33068..4d7012a 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -59,6 +59,7 @@ "dev:firefox": "wxt -b firefox", "postinstall": "wxt prepare", "test": "vitest run", + "playground": "vite --config vite.playground.config.ts", "har:record": "tsx tests/har-record.ts", "har:view": "tsx tests/har-view.ts" }, diff --git a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx index 84d4cf4..b35f8f9 100644 --- a/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx +++ b/browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx @@ -5,7 +5,7 @@ import { logger } from '@/lib/logger' import { modifyDOM } from '../modifyDOM' import { githubHighlighter } from './githubHighlighter' -interface GitHubIssueAddCommentSpot extends CommentSpot { +export interface GitHubIssueAddCommentSpot extends CommentSpot { type: 'GH_ISSUE_ADD_COMMENT' domain: string slug: string // owner/repo diff --git a/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx index d228196..0b70cb4 100644 --- a/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx +++ b/browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx @@ -5,7 +5,7 @@ import { logger } from '@/lib/logger' import { modifyDOM } from '../modifyDOM' import { githubHighlighter } from './githubHighlighter' -interface GitHubPRAddCommentSpot extends CommentSpot { +export interface GitHubPRAddCommentSpot extends CommentSpot { type: 'GH_PR_ADD_COMMENT' // Override to narrow from string to specific union domain: string slug: string // owner/repo diff --git a/browser-extension/tests/lib/enhancers/github.test.ts b/browser-extension/tests/lib/enhancers/github.test.ts index 3952e81..c119651 100644 --- a/browser-extension/tests/lib/enhancers/github.test.ts +++ b/browser-extension/tests/lib/enhancers/github.test.ts @@ -11,7 +11,8 @@ describe('github', () => { const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(2) expect(enhancers.tryToEnhance(textareas[0]!)).toBeNull() - expect(enhancers.tryToEnhance(textareas[1]!)?.spot).toMatchInlineSnapshot(` + const enhancedTextarea = enhancers.tryToEnhance(textareas[1]!) + expect(enhancedTextarea?.spot).toMatchInlineSnapshot(` { "domain": "github.com", "number": 517, @@ -20,12 +21,28 @@ describe('github', () => { "unique_key": "github.com:diffplug/selfie:517", } `) + expect(enhancedTextarea?.enhancer.tableRow(enhancedTextarea.spot)).toMatchInlineSnapshot(` + + + diffplug/selfie + + + PR # + 517 + + + `) }) usingHar('gh_issue').it('should create the correct spot object', async () => { const enhancers = new EnhancerRegistry() const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(1) - expect(enhancers.tryToEnhance(textareas[0]!)?.spot).toMatchInlineSnapshot(` + const enhancedTextarea = enhancers.tryToEnhance(textareas[0]!) + expect(enhancedTextarea?.spot).toMatchInlineSnapshot(` { "domain": "github.com", "number": 523, @@ -34,5 +51,21 @@ describe('github', () => { "unique_key": "github.com:diffplug/selfie:523", } `) + // Test the tableRow method + expect(enhancedTextarea?.enhancer.tableRow(enhancedTextarea.spot)).toMatchInlineSnapshot(` + + + diffplug/selfie + + + Issue # + 523 + + + `) }) }) diff --git a/browser-extension/tests/playground/TablePlayground.tsx b/browser-extension/tests/playground/TablePlayground.tsx new file mode 100644 index 0000000..167e297 --- /dev/null +++ b/browser-extension/tests/playground/TablePlayground.tsx @@ -0,0 +1,74 @@ +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' +import type { CommentState } from '@/entrypoints/background' +import { cn } from '@/lib/utils' +import { enhancerRegistry, sampleSpots } from './mockData' + +interface SpotRowProps { + commentState: CommentState + onClick: () => void +} + +function SpotRow({ commentState, onClick }: SpotRowProps) { + const enhancer = enhancerRegistry.enhancerFor(commentState.spot) + + if (!enhancer) { + return ( + + +
Unknown spot type: {commentState.spot.type}
+
+
+ ) + } + + return ( + + {enhancer.tableRow(commentState.spot)} + + ) +} + +export function TablePlayground() { + const handleSpotClick = (spot: CommentState) => { + alert(`Clicked: ${spot.spot.type}\nTab: ${spot.tab.tabId}`) + } + + return ( +
+
+

Comment Spots

+

Click on any row to simulate tab switching

+
+ + + + + Spot Details + + + + {sampleSpots.map((spot) => ( + handleSpotClick(spot)} + /> + ))} + +
+
+ ) +} diff --git a/browser-extension/tests/playground/index.html b/browser-extension/tests/playground/index.html new file mode 100644 index 0000000..09a40b6 --- /dev/null +++ b/browser-extension/tests/playground/index.html @@ -0,0 +1,13 @@ + + + + + + + Table Playground + + +
+ + + \ No newline at end of file diff --git a/browser-extension/tests/playground/main.tsx b/browser-extension/tests/playground/main.tsx new file mode 100644 index 0000000..dc0bb62 --- /dev/null +++ b/browser-extension/tests/playground/main.tsx @@ -0,0 +1,28 @@ +import { createRoot } from 'react-dom/client' +import './style.css' +import { TablePlayground } from './TablePlayground' + +const root = createRoot(document.getElementById('root')!) +root.render( +
+
+
+

Table Playground

+

+ Testing table rendering with real enhancers and sample data. +

+
+ + + +
+

Development Notes

+
    +
  • • Hot reload is active - changes to components update instantly
  • +
  • • Uses real enhancers from the browser extension
  • +
  • • Click rows to test interaction behavior
  • +
+
+
+
, +) diff --git a/browser-extension/tests/playground/mockData.tsx b/browser-extension/tests/playground/mockData.tsx new file mode 100644 index 0000000..ca67fec --- /dev/null +++ b/browser-extension/tests/playground/mockData.tsx @@ -0,0 +1,43 @@ +import type { CommentState } from '@/entrypoints/background' +import type { CommentSpot } from '@/lib/enhancer' +import type { GitHubIssueAddCommentSpot } from '@/lib/enhancers/github/githubIssueAddComment' +import type { GitHubPRAddCommentSpot } from '@/lib/enhancers/github/githubPRAddComment' +import { EnhancerRegistry } from '@/lib/registries' + +const gh_pr: GitHubPRAddCommentSpot = { + domain: 'github.com', + number: 517, + slug: 'diffplug/selfie', + type: 'GH_PR_ADD_COMMENT', + unique_key: 'github.com:diffplug/selfie:517', +} +const gh_issue: GitHubIssueAddCommentSpot = { + domain: 'github.com', + number: 523, + slug: 'diffplug/selfie', + type: 'GH_ISSUE_ADD_COMMENT', + unique_key: 'github.com:diffplug/selfie:523', +} + +const spots: CommentSpot[] = [gh_pr, gh_issue] + +export const sampleSpots: CommentState[] = spots.map((spot) => { + const state: CommentState = { + drafts: [ + [ + 0, + { + body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + }, + ], + ], + spot, + tab: { + tabId: 123, + windowId: 456, + }, + } + return state +}) + +export const enhancerRegistry = new EnhancerRegistry() diff --git a/browser-extension/tests/playground/style.css b/browser-extension/tests/playground/style.css new file mode 100644 index 0000000..c62bebb --- /dev/null +++ b/browser-extension/tests/playground/style.css @@ -0,0 +1,16 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + margin: 0; + padding: 2rem; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + background: #f8fafc; + min-height: 100vh; +} + +#root { + max-width: 1200px; + margin: 0 auto; +} diff --git a/browser-extension/vite.playground.config.ts b/browser-extension/vite.playground.config.ts new file mode 100644 index 0000000..37888e5 --- /dev/null +++ b/browser-extension/vite.playground.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' +import path from 'path' + +export default defineConfig({ + plugins: [ + react(), + tailwindcss() as any + ], + resolve: { + alias: { + '@': path.resolve('./src') + } + }, + root: 'tests/playground', + server: { + port: 3002, + open: true, + host: true + }, + build: { + outDir: '../../dist-playground', + emptyOutDir: true + } +}) \ No newline at end of file From 7ba5e88ed5091c5559895eeb21e4502e9ee2e660 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 15:50:44 -0700 Subject: [PATCH 10/23] Improve commonality between prod and test. --- browser-extension/src/components/SpotRow.tsx | 40 +++++++++ .../src/components/SpotTable.tsx | 78 ++++++++++++++++++ .../src/entrypoints/popup/main.tsx | 77 ++++------------- .../tests/playground/TablePlayground.tsx | 82 ++++--------------- .../tests/playground/mockData.tsx | 3 - 5 files changed, 149 insertions(+), 131 deletions(-) create mode 100644 browser-extension/src/components/SpotRow.tsx create mode 100644 browser-extension/src/components/SpotTable.tsx diff --git a/browser-extension/src/components/SpotRow.tsx b/browser-extension/src/components/SpotRow.tsx new file mode 100644 index 0000000..a394901 --- /dev/null +++ b/browser-extension/src/components/SpotRow.tsx @@ -0,0 +1,40 @@ +import { TableCell, TableRow } from '@/components/ui/table' +import type { CommentState } from '@/entrypoints/background' +import type { EnhancerRegistry } from '@/lib/registries' +import { cn } from '@/lib/utils' + +interface SpotRowProps { + commentState: CommentState + enhancerRegistry: EnhancerRegistry + onClick: () => void + className?: string + cellClassName?: string + errorClassName?: string +} + +export function SpotRow({ + commentState, + enhancerRegistry, + onClick, + className, + cellClassName = 'p-3', + errorClassName = 'text-red-500', +}: SpotRowProps) { + const enhancer = enhancerRegistry.enhancerFor(commentState.spot) + + if (!enhancer) { + return ( + + +
Unknown spot type: {commentState.spot.type}
+
+
+ ) + } + + return ( + + {enhancer.tableRow(commentState.spot)} + + ) +} diff --git a/browser-extension/src/components/SpotTable.tsx b/browser-extension/src/components/SpotTable.tsx new file mode 100644 index 0000000..33e26e7 --- /dev/null +++ b/browser-extension/src/components/SpotTable.tsx @@ -0,0 +1,78 @@ +import { Table, TableBody, TableHead, TableHeader, TableRow } from '@/components/ui/table' +import type { CommentState } from '@/entrypoints/background' +import type { EnhancerRegistry } from '@/lib/registries' +import { SpotRow } from './SpotRow' + +interface SpotTableProps { + spots: CommentState[] + enhancerRegistry: EnhancerRegistry + onSpotClick: (spot: CommentState) => void + title?: string + description?: string + headerText?: string + className?: string + headerClassName?: string + rowClassName?: string + cellClassName?: string + emptyStateMessage?: string + showHeader?: boolean +} + +export function SpotTable({ + spots, + enhancerRegistry, + onSpotClick, + title, + description, + headerText = 'Comment Spots', + className, + headerClassName = 'p-3 font-medium text-muted-foreground', + rowClassName, + cellClassName, + emptyStateMessage = 'No comment spots available', + showHeader = true, +}: SpotTableProps) { + if (spots.length === 0) { + return
{emptyStateMessage}
+ } + + const tableContent = ( + + {showHeader && ( + + + {headerText} + + + )} + + {spots.map((spot) => ( + onSpotClick(spot)} + className={rowClassName || ''} + cellClassName={cellClassName || 'p-3'} + /> + ))} + +
+ ) + + if (title || description) { + return ( +
+ {(title || description) && ( +
+ {title &&

{title}

} + {description &&

{description}

} +
+ )} + {tableContent} +
+ ) + } + + return
{tableContent}
+} diff --git a/browser-extension/src/entrypoints/popup/main.tsx b/browser-extension/src/entrypoints/popup/main.tsx index 6ddbb2b..fa1cea2 100644 --- a/browser-extension/src/entrypoints/popup/main.tsx +++ b/browser-extension/src/entrypoints/popup/main.tsx @@ -1,21 +1,11 @@ import './style.css' import React from 'react' import { createRoot } from 'react-dom/client' -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table' +import { SpotTable } from '@/components/SpotTable' import type { CommentState } from '@/entrypoints/background' import { logger } from '@/lib/logger' import type { GetOpenSpotsMessage, GetOpenSpotsResponse, SwitchToTabMessage } from '@/lib/messages' import { EnhancerRegistry } from '@/lib/registries' -import { cn } from '@/lib/utils' - -const enhancers = new EnhancerRegistry() async function getOpenSpots(): Promise { logger.debug('Sending message to background script...') @@ -40,38 +30,7 @@ function switchToTab(tabId: number, windowId: number): void { window.close() } -interface SpotRowProps { - commentState: CommentState - onClick: () => void -} - -function SpotRow({ commentState, onClick }: SpotRowProps) { - const enhancer = enhancers.enhancerFor(commentState.spot) - - if (!enhancer) { - logger.error('No enhancer found for:', commentState.spot) - logger.error('Only have enhancers for:', enhancers.byType) - return null - } - - return ( - - -
-
- {enhancer.tableRow(commentState.spot)} -
-
-
-
- ) -} +const enhancers = new EnhancerRegistry() function PopupApp() { const [spots, setSpots] = React.useState([]) @@ -96,10 +55,8 @@ function PopupApp() { return
Loading...
} - if (spots.length === 0) { - return ( -
No open comment spots
- ) + const handleSpotClick = (spot: CommentState) => { + switchToTab(spot.tab.tabId, spot.tab.windowId) } return ( @@ -107,22 +64,16 @@ function PopupApp() {

Open Comment Spots

- - - - Comment Spots - - - - {spots.map((spot) => ( - switchToTab(spot.tab.tabId, spot.tab.windowId)} - /> - ))} - -
+
) diff --git a/browser-extension/tests/playground/TablePlayground.tsx b/browser-extension/tests/playground/TablePlayground.tsx index 167e297..089110a 100644 --- a/browser-extension/tests/playground/TablePlayground.tsx +++ b/browser-extension/tests/playground/TablePlayground.tsx @@ -1,74 +1,26 @@ -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table' +import { SpotTable } from '@/components/SpotTable' import type { CommentState } from '@/entrypoints/background' -import { cn } from '@/lib/utils' -import { enhancerRegistry, sampleSpots } from './mockData' - -interface SpotRowProps { - commentState: CommentState - onClick: () => void -} - -function SpotRow({ commentState, onClick }: SpotRowProps) { - const enhancer = enhancerRegistry.enhancerFor(commentState.spot) - - if (!enhancer) { - return ( - - -
Unknown spot type: {commentState.spot.type}
-
-
- ) - } - - return ( - - {enhancer.tableRow(commentState.spot)} - - ) -} +import { EnhancerRegistry } from '@/lib/registries' +import { sampleSpots } from './mockData' export function TablePlayground() { const handleSpotClick = (spot: CommentState) => { alert(`Clicked: ${spot.spot.type}\nTab: ${spot.tab.tabId}`) } - + const enhancers = new EnhancerRegistry() return ( -
-
-

Comment Spots

-

Click on any row to simulate tab switching

-
- - - - - Spot Details - - - - {sampleSpots.map((spot) => ( - handleSpotClick(spot)} - /> - ))} - -
-
+ ) } diff --git a/browser-extension/tests/playground/mockData.tsx b/browser-extension/tests/playground/mockData.tsx index ca67fec..57fd727 100644 --- a/browser-extension/tests/playground/mockData.tsx +++ b/browser-extension/tests/playground/mockData.tsx @@ -2,7 +2,6 @@ import type { CommentState } from '@/entrypoints/background' import type { CommentSpot } from '@/lib/enhancer' import type { GitHubIssueAddCommentSpot } from '@/lib/enhancers/github/githubIssueAddComment' import type { GitHubPRAddCommentSpot } from '@/lib/enhancers/github/githubPRAddComment' -import { EnhancerRegistry } from '@/lib/registries' const gh_pr: GitHubPRAddCommentSpot = { domain: 'github.com', @@ -39,5 +38,3 @@ export const sampleSpots: CommentState[] = spots.map((spot) => { } return state }) - -export const enhancerRegistry = new EnhancerRegistry() From da006a39e2efe6d72fccdef0556e7fbfb77fe8a2 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 15:57:42 -0700 Subject: [PATCH 11/23] Renames for easier command-palette navigation. --- browser-extension/src/entrypoints/popup/index.html | 2 +- .../src/entrypoints/popup/{main.tsx => popup.tsx} | 0 .../{TablePlayground.tsx => PopupPlayground.tsx} | 4 ++-- .../tests/playground/{main.tsx => playground.tsx} | 12 +++++++----- .../playground/{mockData.tsx => playgroundData.tsx} | 0 5 files changed, 10 insertions(+), 8 deletions(-) rename browser-extension/src/entrypoints/popup/{main.tsx => popup.tsx} (100%) rename browser-extension/tests/playground/{TablePlayground.tsx => PopupPlayground.tsx} (91%) rename browser-extension/tests/playground/{main.tsx => playground.tsx} (71%) rename browser-extension/tests/playground/{mockData.tsx => playgroundData.tsx} (100%) diff --git a/browser-extension/src/entrypoints/popup/index.html b/browser-extension/src/entrypoints/popup/index.html index 8a09020..66aa778 100644 --- a/browser-extension/src/entrypoints/popup/index.html +++ b/browser-extension/src/entrypoints/popup/index.html @@ -8,6 +8,6 @@
- + \ No newline at end of file diff --git a/browser-extension/src/entrypoints/popup/main.tsx b/browser-extension/src/entrypoints/popup/popup.tsx similarity index 100% rename from browser-extension/src/entrypoints/popup/main.tsx rename to browser-extension/src/entrypoints/popup/popup.tsx diff --git a/browser-extension/tests/playground/TablePlayground.tsx b/browser-extension/tests/playground/PopupPlayground.tsx similarity index 91% rename from browser-extension/tests/playground/TablePlayground.tsx rename to browser-extension/tests/playground/PopupPlayground.tsx index 089110a..4b5bdc0 100644 --- a/browser-extension/tests/playground/TablePlayground.tsx +++ b/browser-extension/tests/playground/PopupPlayground.tsx @@ -1,9 +1,9 @@ import { SpotTable } from '@/components/SpotTable' import type { CommentState } from '@/entrypoints/background' import { EnhancerRegistry } from '@/lib/registries' -import { sampleSpots } from './mockData' +import { sampleSpots } from './playgroundData' -export function TablePlayground() { +export function PopupPlayground() { const handleSpotClick = (spot: CommentState) => { alert(`Clicked: ${spot.spot.type}\nTab: ${spot.tab.tabId}`) } diff --git a/browser-extension/tests/playground/main.tsx b/browser-extension/tests/playground/playground.tsx similarity index 71% rename from browser-extension/tests/playground/main.tsx rename to browser-extension/tests/playground/playground.tsx index dc0bb62..7a97299 100644 --- a/browser-extension/tests/playground/main.tsx +++ b/browser-extension/tests/playground/playground.tsx @@ -1,6 +1,6 @@ import { createRoot } from 'react-dom/client' import './style.css' -import { TablePlayground } from './TablePlayground' +import { PopupPlayground } from './PopupPlayground' const root = createRoot(document.getElementById('root')!) root.render( @@ -13,14 +13,16 @@ root.render(

- +

Development Notes

    -
  • • Hot reload is active - changes to components update instantly
  • -
  • • Uses real enhancers from the browser extension
  • -
  • • Click rows to test interaction behavior
  • +
  • Hot reload is active - changes to components update instantly
  • +
  • Uses real enhancers from the browser extension
  • +
  • + Sample data comes from playgroundData.tsx +
diff --git a/browser-extension/tests/playground/mockData.tsx b/browser-extension/tests/playground/playgroundData.tsx similarity index 100% rename from browser-extension/tests/playground/mockData.tsx rename to browser-extension/tests/playground/playgroundData.tsx From 4ed332c5e268a17768f5d2cedde1657ac1e2f03f Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:09:43 -0700 Subject: [PATCH 12/23] We ned postcss at devtime. --- browser-extension/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/browser-extension/package.json b/browser-extension/package.json index 4d7012a..00bb9a5 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -29,6 +29,7 @@ "@vitest/ui": "^3.2.4", "express": "^4.19.2", "linkedom": "^0.18.12", + "postcss": "^8.5.6", "tailwindcss": "^4.1.13", "tsx": "^4.19.1", "typescript": "^5.8.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1671ad6..50f0e46 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: linkedom: specifier: ^0.18.12 version: 0.18.12 + postcss: + specifier: ^8.5.6 + version: 8.5.6 tailwindcss: specifier: ^4.1.13 version: 4.1.13 From 44fdbb555c7a4adc634e871bd73e7859002a3ed2 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:11:47 -0700 Subject: [PATCH 13/23] playground is alive --- .../src/entrypoints/popup/style.css | 11 ++----- browser-extension/src/styles/popup-frame.css | 9 ++++++ .../tests/playground/PopupPlayground.tsx | 31 +++++++++++-------- browser-extension/tests/playground/index.html | 2 +- .../tests/playground/playground.tsx | 21 ++++++++----- browser-extension/tests/playground/style.css | 20 +++++++++--- 6 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 browser-extension/src/styles/popup-frame.css diff --git a/browser-extension/src/entrypoints/popup/style.css b/browser-extension/src/entrypoints/popup/style.css index b773d69..8e9d480 100644 --- a/browser-extension/src/entrypoints/popup/style.css +++ b/browser-extension/src/entrypoints/popup/style.css @@ -1,12 +1,5 @@ +@import url("../../styles/popup-frame.css"); + @tailwind base; @tailwind components; @tailwind utilities; - -body { - width: 300px; - padding: 15px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - font-size: 14px; - line-height: 1.4; - margin: 0; -} diff --git a/browser-extension/src/styles/popup-frame.css b/browser-extension/src/styles/popup-frame.css new file mode 100644 index 0000000..b03d1a4 --- /dev/null +++ b/browser-extension/src/styles/popup-frame.css @@ -0,0 +1,9 @@ +/* Popup window frame styles */ +body { + width: 300px; + padding: 15px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + font-size: 14px; + line-height: 1.4; + margin: 0; +} diff --git a/browser-extension/tests/playground/PopupPlayground.tsx b/browser-extension/tests/playground/PopupPlayground.tsx index 4b5bdc0..8a188f7 100644 --- a/browser-extension/tests/playground/PopupPlayground.tsx +++ b/browser-extension/tests/playground/PopupPlayground.tsx @@ -7,20 +7,25 @@ export function PopupPlayground() { const handleSpotClick = (spot: CommentState) => { alert(`Clicked: ${spot.spot.type}\nTab: ${spot.tab.tabId}`) } + const enhancers = new EnhancerRegistry() + return ( - +
+

Open Comment Spots

+ +
+ +
+
) } diff --git a/browser-extension/tests/playground/index.html b/browser-extension/tests/playground/index.html index 09a40b6..0138cf2 100644 --- a/browser-extension/tests/playground/index.html +++ b/browser-extension/tests/playground/index.html @@ -8,6 +8,6 @@
- + \ No newline at end of file diff --git a/browser-extension/tests/playground/playground.tsx b/browser-extension/tests/playground/playground.tsx index 7a97299..317cfb2 100644 --- a/browser-extension/tests/playground/playground.tsx +++ b/browser-extension/tests/playground/playground.tsx @@ -1,28 +1,33 @@ import { createRoot } from 'react-dom/client' +import '@/entrypoints/popup/style.css' import './style.css' import { PopupPlayground } from './PopupPlayground' const root = createRoot(document.getElementById('root')!) root.render(
-
+
-

Table Playground

+

Popup Simulator

- Testing table rendering with real enhancers and sample data. + This shows exactly how the table appears in the browser popup (300px width).

- +
+ +
-
+

Development Notes

    -
  • Hot reload is active - changes to components update instantly
  • -
  • Uses real enhancers from the browser extension
  • - Sample data comes from playgroundData.tsx + The popup frame above matches the exact 300px width of the browser extension popup +
  • +
  • + Any changes to popup/style.css will automatically update here
  • +
  • Hot reload is active for instant updates
diff --git a/browser-extension/tests/playground/style.css b/browser-extension/tests/playground/style.css index c62bebb..025d849 100644 --- a/browser-extension/tests/playground/style.css +++ b/browser-extension/tests/playground/style.css @@ -1,16 +1,28 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +/* Playground-specific styles - popup styles are imported via popup/style.css */ +/* Override body styles for playground layout */ body { margin: 0; padding: 2rem; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f8fafc; min-height: 100vh; + width: auto; /* Override popup's fixed width for playground */ } #root { max-width: 1200px; margin: 0 auto; } + +/* Popup simulator frame */ +.popup-frame { + width: 300px; + padding: 15px; + font-size: 14px; + line-height: 1.4; + background: white; + border: 1px solid #e2e8f0; + border-radius: 8px; + box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1); + margin: 0 auto; +} From 9e9744796ee23280a8ba9567b63e62765fe8b00c Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:34:18 -0700 Subject: [PATCH 14/23] Get the playground to match the popup exactly. --- browser-extension/tests/playground/playground.tsx | 2 +- browser-extension/tests/playground/style.css | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/browser-extension/tests/playground/playground.tsx b/browser-extension/tests/playground/playground.tsx index 317cfb2..cac9281 100644 --- a/browser-extension/tests/playground/playground.tsx +++ b/browser-extension/tests/playground/playground.tsx @@ -6,7 +6,7 @@ import { PopupPlayground } from './PopupPlayground' const root = createRoot(document.getElementById('root')!) root.render(
-
+

Popup Simulator

diff --git a/browser-extension/tests/playground/style.css b/browser-extension/tests/playground/style.css index 025d849..b1ada89 100644 --- a/browser-extension/tests/playground/style.css +++ b/browser-extension/tests/playground/style.css @@ -11,7 +11,7 @@ body { #root { max-width: 1200px; - margin: 0 auto; + margin: 0; } /* Popup simulator frame */ @@ -24,5 +24,6 @@ body { border: 1px solid #e2e8f0; border-radius: 8px; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1); - margin: 0 auto; + margin: 0; + text-align: left; } From db9d46f752cff8332ad18edf5bcca04eb507bcc2 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:34:27 -0700 Subject: [PATCH 15/23] Improve docs. --- browser-extension/README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/browser-extension/README.md b/browser-extension/README.md index 543713b..9324f41 100644 --- a/browser-extension/README.md +++ b/browser-extension/README.md @@ -42,15 +42,15 @@ This is a [WXT](https://wxt.dev/)-based browser extension that ### Entry points -- `src/entrypoints/content.ts` - injected into every webpage -- `src/entrypoints/background.ts` - service worker that manages state and handles messages -- `src/entrypoints/popup` - React-based popup (html/css/tsx) with shadcn/ui table components +- [`src/entrypoints/content.ts`](src/entrypoints/content.ts) - injected into every webpage +- [`src/entrypoints/background.ts`](src/entrypoints/background.ts) - service worker that manages state and handles messages +- [`src/entrypoints/popup/popup.tsx`](src/entrypoints/popup/popup.tsx) - popup (html/css/tsx) with shadcn/ui table components ```mermaid graph TD Content[Content Script
content.ts] Background[Background Script
background.ts] - Popup[Popup Script
popup/main.tsx] + Popup[Popup Script
popup/popup.tsx] Content -->|ENHANCED/DESTROYED
CommentEvent| Background Popup -->|GET_OPEN_SPOTS
SWITCH_TO_TAB| Background @@ -69,8 +69,6 @@ graph TD class TextArea,UI ui ``` -### Architecture - Every time a `textarea` shows up on a page, on initial load or later on, it gets passed to a list of `CommentEnhancer`s. Each one gets a turn to say "I can enhance this box!". They show that they can enhance it by returning something non-null in the method `tryToEnhance(textarea: HTMLTextAreaElement): Spot | null`. Later on, that same `Spot` data will be used by the `tableRow(spot: Spot): ReactNode` method to create React components for rich formatting in the popup table. Those `Spot` values get bundled up with the `HTMLTextAreaElement` itself into an `EnhancedTextarea`, which gets added to the `TextareaRegistry`. At some interval, draft edits get saved by the browser extension. @@ -79,12 +77,12 @@ When the `textarea` gets removed from the page, the `TextareaRegistry` is notifi ## Testing -In `tests/har` there are various `.har` files. These are complete recordings of a single page load. - -- `pnpm run har:view` and you can see the recordings, with or without our browser extension. +- `npm run playground` gives you a test environment where you can tinker with the popup with various test data, supports hot reload +- `npm run har:view` gives you recordings of various web pages which you can see with and without enhancement by the browser extension ### Recording new HAR files +- the har recordings live in `tests/har`, they are complete recordings of the network requests of a single page load - you can add or change URLs in `tests/har-index.ts` - `npx playwright codegen https://github.com/login --save-storage=playwright/.auth/gh.json` will store new auth tokens - login manually, then close the browser From adadfd83d7ca013abcdb8c003d4b7c7566ef199a Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:35:52 -0700 Subject: [PATCH 16/23] Change width to 311px. --- browser-extension/src/styles/popup-frame.css | 2 +- browser-extension/tests/playground/playground.tsx | 4 ++-- browser-extension/tests/playground/style.css | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/browser-extension/src/styles/popup-frame.css b/browser-extension/src/styles/popup-frame.css index b03d1a4..971d5fa 100644 --- a/browser-extension/src/styles/popup-frame.css +++ b/browser-extension/src/styles/popup-frame.css @@ -1,6 +1,6 @@ /* Popup window frame styles */ body { - width: 300px; + width: 311px; padding: 15px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; diff --git a/browser-extension/tests/playground/playground.tsx b/browser-extension/tests/playground/playground.tsx index cac9281..a6f84ae 100644 --- a/browser-extension/tests/playground/playground.tsx +++ b/browser-extension/tests/playground/playground.tsx @@ -10,7 +10,7 @@ root.render(

Popup Simulator

- This shows exactly how the table appears in the browser popup (300px width). + This shows exactly how the table appears in the browser popup (311px width).

@@ -22,7 +22,7 @@ root.render(

Development Notes

  • - The popup frame above matches the exact 300px width of the browser extension popup + The popup frame above matches the exact 311px width of the browser extension popup
  • Any changes to popup/style.css will automatically update here diff --git a/browser-extension/tests/playground/style.css b/browser-extension/tests/playground/style.css index b1ada89..3538dae 100644 --- a/browser-extension/tests/playground/style.css +++ b/browser-extension/tests/playground/style.css @@ -16,7 +16,7 @@ body { /* Popup simulator frame */ .popup-frame { - width: 300px; + width: 311px; padding: 15px; font-size: 14px; line-height: 1.4; From 2ca0e680407298880703d1b9108aa0c080126ccc Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:40:28 -0700 Subject: [PATCH 17/23] Set the width in only one place. --- browser-extension/src/styles/popup-frame.css | 6 +++++- browser-extension/tests/playground/playground.tsx | 10 +--------- browser-extension/tests/playground/style.css | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/browser-extension/src/styles/popup-frame.css b/browser-extension/src/styles/popup-frame.css index 971d5fa..fd54bc9 100644 --- a/browser-extension/src/styles/popup-frame.css +++ b/browser-extension/src/styles/popup-frame.css @@ -1,6 +1,10 @@ /* Popup window frame styles */ +:root { + --popup-width: 311px; +} + body { - width: 311px; + width: var(--popup-width); padding: 15px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; diff --git a/browser-extension/tests/playground/playground.tsx b/browser-extension/tests/playground/playground.tsx index a6f84ae..2ee3653 100644 --- a/browser-extension/tests/playground/playground.tsx +++ b/browser-extension/tests/playground/playground.tsx @@ -9,9 +9,6 @@ root.render(

    Popup Simulator

    -

    - This shows exactly how the table appears in the browser popup (311px width). -

    @@ -21,12 +18,7 @@ root.render(

    Development Notes

      -
    • - The popup frame above matches the exact 311px width of the browser extension popup -
    • -
    • - Any changes to popup/style.css will automatically update here -
    • +
    • The popup frame above matches the exact browser extension popup.
    • Hot reload is active for instant updates
    diff --git a/browser-extension/tests/playground/style.css b/browser-extension/tests/playground/style.css index 3538dae..c56cada 100644 --- a/browser-extension/tests/playground/style.css +++ b/browser-extension/tests/playground/style.css @@ -16,7 +16,7 @@ body { /* Popup simulator frame */ .popup-frame { - width: 311px; + width: var(--popup-width); padding: 15px; font-size: 14px; line-height: 1.4; From 0e17d9d641eed0f260f5035d170fde948ec6fb82 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 17:41:36 -0700 Subject: [PATCH 18/23] Make it wider. --- browser-extension/src/styles/popup-frame.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser-extension/src/styles/popup-frame.css b/browser-extension/src/styles/popup-frame.css index fd54bc9..49cd2b8 100644 --- a/browser-extension/src/styles/popup-frame.css +++ b/browser-extension/src/styles/popup-frame.css @@ -1,6 +1,6 @@ /* Popup window frame styles */ :root { - --popup-width: 311px; + --popup-width: 600px; } body { From c04643c24404dfa7b404143bf3db0eb272717998 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 20:06:56 -0700 Subject: [PATCH 19/23] We were missing vite, which we were using through an undeclared transitive on wxt i think. --- browser-extension/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/browser-extension/package.json b/browser-extension/package.json index 00bb9a5..5516936 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -33,6 +33,7 @@ "tailwindcss": "^4.1.13", "tsx": "^4.19.1", "typescript": "^5.8.3", + "vite": "^7.1.5", "vitest": "^3.2.4", "wxt": "^0.20.7" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50f0e46..ea4a3ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: typescript: specifier: ^5.8.3 version: 5.9.2 + vite: + specifier: ^7.1.5 + version: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) vitest: specifier: ^3.2.4 version: 3.2.4(@types/node@22.18.1)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.20.5) From 10805c1c6783e57c26cebaa3141613afbe65fc48 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 22:07:16 -0700 Subject: [PATCH 20/23] Unnecessary docs. --- browser-extension/README.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/browser-extension/README.md b/browser-extension/README.md index 9324f41..79275aa 100644 --- a/browser-extension/README.md +++ b/browser-extension/README.md @@ -31,15 +31,6 @@ This is a [WXT](https://wxt.dev/)-based browser extension that - finds `textarea` components and decorates them with [overtype](https://overtype.dev/) and [highlight.js](https://highlightjs.org/) - stores unposted comment drafts, and makes them easy to find via the extension popup -### Tech Stack - -- **Framework**: [WXT](https://wxt.dev/) for browser extension development -- **UI**: React with TypeScript JSX -- **Styling**: Tailwind CSS v4 (with first-party Vite plugin) -- **Components**: shadcn/ui for table components -- **Editor Enhancement**: [Overtype](https://overtype.dev/) with syntax highlighting -- **Build**: Vite with React plugin - ### Entry points - [`src/entrypoints/content.ts`](src/entrypoints/content.ts) - injected into every webpage From 0381d9b737f761900f8e2b4b1d19d67e967f2e72 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 22:08:25 -0700 Subject: [PATCH 21/23] Add the octicons. --- browser-extension/package.json | 1 + pnpm-lock.yaml | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/browser-extension/package.json b/browser-extension/package.json index 5516936..e2eabb0 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -1,6 +1,7 @@ { "author": "DiffPlug", "dependencies": { + "@primer/octicons-react": "^19.18.0", "@radix-ui/react-slot": "^1.2.3", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea4a3ae..55dc316 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,9 @@ importers: browser-extension: dependencies: + '@primer/octicons-react': + specifier: ^19.18.0 + version: 19.18.0(react@19.1.1) '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.12)(react@19.1.1) @@ -706,6 +709,12 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@primer/octicons-react@19.18.0': + resolution: {integrity: sha512-nLFlLmWfz3McbTiOUKVO+iwB15ALYQC9rHeP8K3qM1pyJ8svGaPjGR72BQSEM8ThyQUUodq/Re1n94tO5NNhzQ==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.3' + '@radix-ui/react-compose-refs@1.1.2': resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: @@ -3923,6 +3932,10 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@primer/octicons-react@19.18.0(react@19.1.1)': + dependencies: + react: 19.1.1 + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.12)(react@19.1.1)': dependencies: react: 19.1.1 From fbcfe43bde038aa00034572ef67f1035a6638343 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 23:09:08 -0700 Subject: [PATCH 22/23] Add some missing .gitignores --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 011e869..4ce6898 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ dist/ Thumbs.db # playright +.playwright-mcp/ +browser-extension/dist-playground/ browser-extension/playwright-report/ browser-extension/playwright/ -browser-extension/test-results/ \ No newline at end of file +browser-extension/test-results/ From 597cf79453304884b1a7691d8863aaab64886892 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 10 Sep 2025 23:28:56 -0700 Subject: [PATCH 23/23] Simplify. --- browser-extension/src/styles/globals.css | 70 +----------------------- 1 file changed, 1 insertion(+), 69 deletions(-) diff --git a/browser-extension/src/styles/globals.css b/browser-extension/src/styles/globals.css index 3094824..f1d8c73 100644 --- a/browser-extension/src/styles/globals.css +++ b/browser-extension/src/styles/globals.css @@ -1,69 +1 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem; - } - - .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 212.7 26.8% 83.9%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} +@import "tailwindcss";