From f38d51c3b9235b71397b60cc76c345e0866ce1eb Mon Sep 17 00:00:00 2001 From: Todd Riley Date: Wed, 10 Sep 2025 12:02:03 -0400 Subject: [PATCH 1/6] Biome format config files too. --- browser-extension/biome.json | 2 +- browser-extension/playwright.config.ts | 5 ++--- browser-extension/vitest.config.ts | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/browser-extension/biome.json b/browser-extension/biome.json index d4b34d1..33cd57f 100644 --- a/browser-extension/biome.json +++ b/browser-extension/biome.json @@ -10,7 +10,7 @@ }, "files": { "ignoreUnknown": false, - "includes": [".*", "src/**", "tests/**", "!src/overtype", "!src/playgrounds"] + "includes": [".*", "*.config.ts", "src/**", "tests/**", "!src/overtype"] }, "formatter": { "enabled": true, diff --git a/browser-extension/playwright.config.ts b/browser-extension/playwright.config.ts index 173e3b5..ea2fa0f 100644 --- a/browser-extension/playwright.config.ts +++ b/browser-extension/playwright.config.ts @@ -2,12 +2,11 @@ import { defineConfig } from '@playwright/test' export default defineConfig({ + reporter: [['html', { open: 'never' }]], testDir: 'tests/e2e', use: { screenshot: 'only-on-failure', - video: 'retain-on-failure', trace: 'retain-on-failure', + video: 'retain-on-failure', }, - reporter: [['html', { open: 'never' }]], }) - diff --git a/browser-extension/vitest.config.ts b/browser-extension/vitest.config.ts index 98e411b..d545330 100644 --- a/browser-extension/vitest.config.ts +++ b/browser-extension/vitest.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ plugins: [WxtVitest()], test: { environment: 'node', - pool: 'threads', globals: true, + pool: 'threads', }, }) From f6dfe039ff854d5e79a3c7076330d793a175a7c0 Mon Sep 17 00:00:00 2001 From: Todd Riley Date: Wed, 10 Sep 2025 12:03:12 -0400 Subject: [PATCH 2/6] Add Issue New --- browser-extension/src/lib/config.ts | 6 +- .../enhancers/github/githubIssueNewComment.ts | 67 +++++++++++++++++++ .../github/githubIssuePRNewComment.ts | 67 +++++++++++++++++++ browser-extension/src/lib/logger.ts | 27 ++++++-- browser-extension/src/lib/registries.ts | 2 + 5 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts create mode 100644 browser-extension/src/lib/enhancers/github/githubIssuePRNewComment.ts diff --git a/browser-extension/src/lib/config.ts b/browser-extension/src/lib/config.ts index eb8b231..73f1820 100644 --- a/browser-extension/src/lib/config.ts +++ b/browser-extension/src/lib/config.ts @@ -2,9 +2,13 @@ const MODES = ['PROD', 'PLAYGROUNDS_PR'] as const export type ModeType = (typeof MODES)[number] +const LOG_LEVELS = ['DEBUG', 'INFO', 'WARN', 'ERROR'] as const + +export type LogLevel = (typeof LOG_LEVELS)[number] + export const CONFIG = { ADDED_OVERTYPE_CLASS: 'gitcasso-overtype', - DEBUG: true, // enabled debug logging EXTENSION_NAME: 'gitcasso', // decorates logs + LOG_LEVEL: 'INFO' satisfies LogLevel, MODE: 'PROD' satisfies ModeType, } as const diff --git a/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts b/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts new file mode 100644 index 0000000..25846c1 --- /dev/null +++ b/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts @@ -0,0 +1,67 @@ +import OverType, { type OverTypeInstance } from 'overtype' +import type { CommentEnhancer, CommentSpot } from '../../enhancer' +import { logger } from '../../logger' +import { modifyDOM } from '../modifyDOM' +import { githubHighlighter } from './githubHighlighter' + +interface GitHubIssueAddCommentSpot extends CommentSpot { + type: 'GH_ISSUE_NEW_COMMENT' + domain: string + slug: string // owner/repo +} + +export class GitHubIssueNewCommentEnhancer implements CommentEnhancer { + forSpotTypes(): string[] { + return ['GH_ISSUE_NEW_COMMENT'] + } + + tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueAddCommentSpot | null { + if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') { + return null + } + + // Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456 + logger.info(`${this.constructor.name} examing url`, window.location.pathname) + + const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/) + logger.info(`${this.constructor.name} found match`, window.location.pathname) + + if (!match) return null + const [, owner, repo] = match + const slug = `${owner}/${repo}` + const unique_key = `github.com:${slug}:new` + return { + domain: 'github.com', + slug, + type: 'GH_ISSUE_NEW_COMMENT', + unique_key, + } + } + + prepareForFirstEnhancement(): void { + OverType.setCodeHighlighter(githubHighlighter) + } + + enhance(textArea: HTMLTextAreaElement, _spot: GitHubIssueAddCommentSpot): OverTypeInstance { + const overtypeContainer = modifyDOM(textArea) + return new OverType(overtypeContainer, { + autoResize: true, + minHeight: '400px', + padding: 'var(--base-size-16)', + placeholder: 'Type your description here...', + })[0]! + } + + tableTitle(spot: GitHubIssueAddCommentSpot): string { + const { slug } = spot + return `${slug} New Issue` + } + + tableIcon(_: GitHubIssueAddCommentSpot): string { + return '🔄' // PR icon TODO: icon urls in /public + } + + buildUrl(spot: GitHubIssueAddCommentSpot): string { + return `https://${spot.domain}/${spot.slug}/issue/new` + } +} diff --git a/browser-extension/src/lib/enhancers/github/githubIssuePRNewComment.ts b/browser-extension/src/lib/enhancers/github/githubIssuePRNewComment.ts new file mode 100644 index 0000000..25846c1 --- /dev/null +++ b/browser-extension/src/lib/enhancers/github/githubIssuePRNewComment.ts @@ -0,0 +1,67 @@ +import OverType, { type OverTypeInstance } from 'overtype' +import type { CommentEnhancer, CommentSpot } from '../../enhancer' +import { logger } from '../../logger' +import { modifyDOM } from '../modifyDOM' +import { githubHighlighter } from './githubHighlighter' + +interface GitHubIssueAddCommentSpot extends CommentSpot { + type: 'GH_ISSUE_NEW_COMMENT' + domain: string + slug: string // owner/repo +} + +export class GitHubIssueNewCommentEnhancer implements CommentEnhancer { + forSpotTypes(): string[] { + return ['GH_ISSUE_NEW_COMMENT'] + } + + tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueAddCommentSpot | null { + if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') { + return null + } + + // Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456 + logger.info(`${this.constructor.name} examing url`, window.location.pathname) + + const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/) + logger.info(`${this.constructor.name} found match`, window.location.pathname) + + if (!match) return null + const [, owner, repo] = match + const slug = `${owner}/${repo}` + const unique_key = `github.com:${slug}:new` + return { + domain: 'github.com', + slug, + type: 'GH_ISSUE_NEW_COMMENT', + unique_key, + } + } + + prepareForFirstEnhancement(): void { + OverType.setCodeHighlighter(githubHighlighter) + } + + enhance(textArea: HTMLTextAreaElement, _spot: GitHubIssueAddCommentSpot): OverTypeInstance { + const overtypeContainer = modifyDOM(textArea) + return new OverType(overtypeContainer, { + autoResize: true, + minHeight: '400px', + padding: 'var(--base-size-16)', + placeholder: 'Type your description here...', + })[0]! + } + + tableTitle(spot: GitHubIssueAddCommentSpot): string { + const { slug } = spot + return `${slug} New Issue` + } + + tableIcon(_: GitHubIssueAddCommentSpot): string { + return '🔄' // PR icon TODO: icon urls in /public + } + + buildUrl(spot: GitHubIssueAddCommentSpot): string { + return `https://${spot.domain}/${spot.slug}/issue/new` + } +} diff --git a/browser-extension/src/lib/logger.ts b/browser-extension/src/lib/logger.ts index d11faa3..e6111a5 100644 --- a/browser-extension/src/lib/logger.ts +++ b/browser-extension/src/lib/logger.ts @@ -1,4 +1,4 @@ -import { CONFIG } from './config' +import { CONFIG, type LogLevel } from './config' /** * Simple logging utilities for the extension @@ -9,12 +9,25 @@ const prefix = `[${CONFIG.EXTENSION_NAME}]` // No-op function for disabled logging const noop = () => {} +// Log level hierarchy - index represents priority +const LOG_LEVEL_PRIORITY: Record = { + DEBUG: 0, + ERROR: 3, + INFO: 1, + WARN: 2, +} + +// Helper function to check if a log level is enabled +const shouldLog = (level: LogLevel): boolean => { + return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[CONFIG.LOG_LEVEL] +} + // Export simple logging functions export const logger = { - debug: CONFIG.DEBUG ? console.log.bind(console, prefix) : noop, - error: console.error.bind(console, prefix), - info: CONFIG.DEBUG ? console.log.bind(console, prefix) : noop, - time: CONFIG.DEBUG ? console.time.bind(console) : noop, - timeEnd: CONFIG.DEBUG ? console.timeEnd.bind(console) : noop, - warn: console.warn.bind(console, prefix), + debug: shouldLog('DEBUG') ? console.log.bind(console, prefix) : noop, + error: shouldLog('ERROR') ? console.error.bind(console, prefix) : noop, + info: shouldLog('INFO') ? console.log.bind(console, prefix) : noop, + time: shouldLog('INFO') ? console.time.bind(console) : noop, + timeEnd: shouldLog('INFO') ? console.timeEnd.bind(console) : noop, + warn: shouldLog('WARN') ? console.warn.bind(console, prefix) : noop, } diff --git a/browser-extension/src/lib/registries.ts b/browser-extension/src/lib/registries.ts index b6d8ad9..237032b 100644 --- a/browser-extension/src/lib/registries.ts +++ b/browser-extension/src/lib/registries.ts @@ -1,6 +1,7 @@ import type { OverTypeInstance } from 'overtype' import type { CommentEnhancer, CommentSpot } from './enhancer' import { GitHubIssueAddCommentEnhancer } from './enhancers/github/githubIssueAddComment' +import { GitHubIssueNewCommentEnhancer } from './enhancers/github/githubIssueNewComment' import { GitHubPRAddCommentEnhancer } from './enhancers/github/githubPRAddComment' export interface EnhancedTextarea { @@ -19,6 +20,7 @@ export class EnhancerRegistry { // Register all available handlers this.register(new GitHubIssueAddCommentEnhancer()) this.register(new GitHubPRAddCommentEnhancer()) + this.register(new GitHubIssueNewCommentEnhancer()) } private register(enhancer: CommentEnhancer): void { From 597f6e51f59c3c07f6bc9f7c701816b14dd7df97 Mon Sep 17 00:00:00 2001 From: Todd Riley Date: Wed, 10 Sep 2025 14:28:47 -0400 Subject: [PATCH 3/6] PR new --- .../enhancers/github/githubIssueNewComment.ts | 18 ++++---- ...ePRNewComment.ts => githubPRNewComment.ts} | 41 +++++++++++-------- browser-extension/src/lib/logger.ts | 4 ++ browser-extension/src/lib/registries.ts | 4 +- 4 files changed, 39 insertions(+), 28 deletions(-) rename browser-extension/src/lib/enhancers/github/{githubIssuePRNewComment.ts => githubPRNewComment.ts} (50%) diff --git a/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts b/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts index 25846c1..01c0333 100644 --- a/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts +++ b/browser-extension/src/lib/enhancers/github/githubIssueNewComment.ts @@ -4,27 +4,27 @@ import { logger } from '../../logger' import { modifyDOM } from '../modifyDOM' import { githubHighlighter } from './githubHighlighter' -interface GitHubIssueAddCommentSpot extends CommentSpot { +interface GitHubIssueNewCommentSpot extends CommentSpot { type: 'GH_ISSUE_NEW_COMMENT' domain: string slug: string // owner/repo } -export class GitHubIssueNewCommentEnhancer implements CommentEnhancer { +export class GitHubIssueNewCommentEnhancer implements CommentEnhancer { forSpotTypes(): string[] { return ['GH_ISSUE_NEW_COMMENT'] } - tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueAddCommentSpot | null { + tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueNewCommentSpot | null { if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') { return null } // Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456 - logger.info(`${this.constructor.name} examing url`, window.location.pathname) + logger.debug(`${this.constructor.name} examing url`, window.location.pathname) const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/) - logger.info(`${this.constructor.name} found match`, window.location.pathname) + logger.debug(`${this.constructor.name} found match`, window.location.pathname) if (!match) return null const [, owner, repo] = match @@ -42,7 +42,7 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer { +export class GitHubPRNewCommentEnhancer implements CommentEnhancer { forSpotTypes(): string[] { - return ['GH_ISSUE_NEW_COMMENT'] + return ['GH_PR_NEW_COMMENT'] } - tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueAddCommentSpot | null { + tryToEnhance(_textarea: HTMLTextAreaElement): GitHubPRNewCommentSpot | null { if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') { return null } - // Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456 + // /owner/repo/compare/feature/more-enhancers?expand=1 + // or /owner/repo/compare/feat/issue-static-and-dynamic...feature/more-enhancers?expand=1 logger.info(`${this.constructor.name} examing url`, window.location.pathname) - const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/) - logger.info(`${this.constructor.name} found match`, window.location.pathname) + const match = window.location.pathname.match( + /^\/([^/]+)\/([^/]+)\/compare\/(?:([^.?]+)\.\.\.)?([^?]+)/, + ) + logger.info(`${this.constructor.name} found match`, window.location.pathname, match) if (!match) return null - const [, owner, repo] = match - const slug = `${owner}/${repo}` - const unique_key = `github.com:${slug}:new` + const [, owner, repo, baseBranch, compareBranch] = match + const slug = baseBranch + ? `${owner}/${repo}/${baseBranch}...${compareBranch}` + : `${owner}/${repo}/${compareBranch}` + const unique_key = `github.com:${slug}` return { domain: 'github.com', slug, - type: 'GH_ISSUE_NEW_COMMENT', + type: 'GH_PR_NEW_COMMENT', unique_key, } } @@ -42,26 +47,26 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer = { // Helper function to check if a log level is enabled const shouldLog = (level: LogLevel): boolean => { + // Don't log anything in production mode + if (CONFIG.MODE === 'PROD') { + return false + } return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[CONFIG.LOG_LEVEL] } diff --git a/browser-extension/src/lib/registries.ts b/browser-extension/src/lib/registries.ts index 237032b..7ca0208 100644 --- a/browser-extension/src/lib/registries.ts +++ b/browser-extension/src/lib/registries.ts @@ -3,6 +3,7 @@ import type { CommentEnhancer, CommentSpot } from './enhancer' import { GitHubIssueAddCommentEnhancer } from './enhancers/github/githubIssueAddComment' import { GitHubIssueNewCommentEnhancer } from './enhancers/github/githubIssueNewComment' import { GitHubPRAddCommentEnhancer } from './enhancers/github/githubPRAddComment' +import { GitHubPRNewCommentEnhancer } from './enhancers/github/githubPRNewComment' export interface EnhancedTextarea { textarea: HTMLTextAreaElement @@ -19,8 +20,9 @@ export class EnhancerRegistry { constructor() { // Register all available handlers this.register(new GitHubIssueAddCommentEnhancer()) - this.register(new GitHubPRAddCommentEnhancer()) this.register(new GitHubIssueNewCommentEnhancer()) + this.register(new GitHubPRAddCommentEnhancer()) + this.register(new GitHubPRNewCommentEnhancer()) } private register(enhancer: CommentEnhancer): void { From 97948e7a800f0904f31eb81574a3ef45bfe5fc14 Mon Sep 17 00:00:00 2001 From: Todd Riley Date: Wed, 10 Sep 2025 14:58:50 -0400 Subject: [PATCH 4/6] tests --- .../tests/lib/enhancers/github.test.ts | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/browser-extension/tests/lib/enhancers/github.test.ts b/browser-extension/tests/lib/enhancers/github.test.ts index 3952e81..22b7908 100644 --- a/browser-extension/tests/lib/enhancers/github.test.ts +++ b/browser-extension/tests/lib/enhancers/github.test.ts @@ -6,7 +6,7 @@ expect import { EnhancerRegistry } from '../../../src/lib/registries' describe('github', () => { - usingHar('gh_pr').it('should create the correct spot object', async () => { + usingHar('gh_pr').it('should create the correct spot object for gh_pr', async () => { const enhancers = new EnhancerRegistry() const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(2) @@ -21,7 +21,20 @@ describe('github', () => { } `) }) - usingHar('gh_issue').it('should create the correct spot object', async () => { + usingHar('gh_new_pr').it('should create the correct spot object for gh_new_pr', async () => { + const enhancers = new EnhancerRegistry() + const textareas = document.querySelectorAll('textarea') + expect(textareas.length).toBe(2) + expect(enhancers.tryToEnhance(textareas[0]!)?.spot).toMatchInlineSnapshot(` + { + "domain": "github.com", + "slug": "diffplug/selfie/main...cavia-porcellus:selfie:main", + "type": "GH_PR_NEW_COMMENT", + "unique_key": "github.com:diffplug/selfie/main...cavia-porcellus:selfie:main", + } + `) + }) + usingHar('gh_issue').it('should create the correct spot object for gh_issue', async () => { const enhancers = new EnhancerRegistry() const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(1) @@ -35,4 +48,19 @@ describe('github', () => { } `) }) + usingHar( + 'gh_new_issue', + ).it('should create the correct spot object for gh_new_issue', async () => { + const enhancers = new EnhancerRegistry() + const textareas = document.querySelectorAll('textarea') + expect(textareas.length).toBe(1) + expect(enhancers.tryToEnhance(textareas[0]!)?.spot).toMatchInlineSnapshot(` + { + "domain": "github.com", + "slug": "diffplug/selfie", + "type": "GH_ISSUE_NEW_COMMENT", + "unique_key": "github.com:diffplug/selfie:new", + } + `) + }) }) From cd0031acb162409ac90d8a55698974b04a477f7b Mon Sep 17 00:00:00 2001 From: Todd Riley Date: Wed, 10 Sep 2025 15:01:52 -0400 Subject: [PATCH 5/6] Fix test output. --- browser-extension/tests/har-fixture.ts | 2 +- browser-extension/tests/lib/enhancers/github.test.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/browser-extension/tests/har-fixture.ts b/browser-extension/tests/har-fixture.ts index 6464657..687040d 100644 --- a/browser-extension/tests/har-fixture.ts +++ b/browser-extension/tests/har-fixture.ts @@ -33,7 +33,7 @@ export { expect } export function usingHar(harKey: keyof typeof PAGES) { return { it: (name: string, fn: () => void | Promise) => { - return baseTest(name, async () => { + return baseTest(`${harKey}:${name}`, async () => { // Setup HAR DOM before test await setupHarDOM(harKey) diff --git a/browser-extension/tests/lib/enhancers/github.test.ts b/browser-extension/tests/lib/enhancers/github.test.ts index 22b7908..1b56dea 100644 --- a/browser-extension/tests/lib/enhancers/github.test.ts +++ b/browser-extension/tests/lib/enhancers/github.test.ts @@ -6,7 +6,7 @@ expect import { EnhancerRegistry } from '../../../src/lib/registries' describe('github', () => { - usingHar('gh_pr').it('should create the correct spot object for gh_pr', async () => { + usingHar('gh_pr').it('should create the correct spot object', async () => { const enhancers = new EnhancerRegistry() const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(2) @@ -21,7 +21,7 @@ describe('github', () => { } `) }) - usingHar('gh_new_pr').it('should create the correct spot object for gh_new_pr', async () => { + usingHar('gh_new_pr').it('should create the correct spot object', async () => { const enhancers = new EnhancerRegistry() const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(2) @@ -34,7 +34,7 @@ describe('github', () => { } `) }) - usingHar('gh_issue').it('should create the correct spot object for gh_issue', async () => { + 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) @@ -48,9 +48,7 @@ describe('github', () => { } `) }) - usingHar( - 'gh_new_issue', - ).it('should create the correct spot object for gh_new_issue', async () => { + usingHar('gh_new_issue').it('should create the correct spot object', async () => { const enhancers = new EnhancerRegistry() const textareas = document.querySelectorAll('textarea') expect(textareas.length).toBe(1) From e68c74410fa0e9277f60277ba6a8a263911a3e9f Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 11 Sep 2025 12:08:56 -0700 Subject: [PATCH 6/6] Update browser-extension/src/lib/logger.ts --- browser-extension/src/lib/logger.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/browser-extension/src/lib/logger.ts b/browser-extension/src/lib/logger.ts index 1859b77..e6111a5 100644 --- a/browser-extension/src/lib/logger.ts +++ b/browser-extension/src/lib/logger.ts @@ -19,10 +19,6 @@ const LOG_LEVEL_PRIORITY: Record = { // Helper function to check if a log level is enabled const shouldLog = (level: LogLevel): boolean => { - // Don't log anything in production mode - if (CONFIG.MODE === 'PROD') { - return false - } return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[CONFIG.LOG_LEVEL] }