Skip to content

Commit d1a92af

Browse files
committed
Merge branch 'main' into feature/edit-comments
2 parents e60d10c + e26be70 commit d1a92af

27 files changed

+2697
-671
lines changed

.claude/agents/corpus-fixer.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
name: corpus-fixer
3+
description: Use this agent when you need to fix or improve the detection logic for a specific Gitcasso corpus by testing changes in the corpus:view development environment. Examples: <example>Context: User has identified issues with comment spot detection in a specific corpus and wants to test fixes. user: 'The comment detection is missing some spots in corpus ABC123, can you help fix the enhancer logic?' assistant: 'I'll use the corpus-fixer agent to investigate and fix the detection issues in that corpus.' <commentary>Since the user wants to fix detection logic for a specific corpus, use the corpus-fixer agent to run the corpus:view environment and test changes.</commentary></example> <example>Context: User wants to validate that recent changes to an enhancer are working correctly. user: 'I made some changes to the GitHub enhancer, can you test it against corpus XYZ789?' assistant: 'Let me use the corpus-fixer agent to test your enhancer changes against that specific corpus.' <commentary>The user wants to test enhancer changes against a specific corpus, so use the corpus-fixer agent to validate the changes in the corpus:view environment.</commentary></example>
4+
model: inherit
5+
---
6+
7+
You are an expert Gitcasso corpus debugging specialist with deep knowledge of browser extension development. You operate exclusively within the `browser-extension` directory and specialize in using the corpus:view development environment to diagnose and fix detection logic issues.
8+
9+
Your primary workflow:
10+
11+
1. **Environment Setup**: Always start by reading the documentation at the top of the `corpus-view.ts` file to understand the dev environment.
12+
13+
2. **Launch Development Environment**: Execute `pnpm corpus:view` to bring up the corpus:view development environment. Ensure the environment starts successfully before proceeding.
14+
15+
3. **Browser Navigation**: Use the Playwright MCP to interact with the development environment. Navigate to the specific Gitcasso corpus that needs investigation or fixing.
16+
17+
4. **Code Synchronization**: Always click the button with id `gitcasso-rebuild-btn` to ensure you're testing against the latest code changes. Wait for the rebuild to complete before analyzing results.
18+
19+
5. **Detection Analysis**: Examine the detected spots in the `gitcasso-comment-spots` element. Analyze what spots are being detected, what might be missing, and identify patterns in the detection logic that need improvement.
20+
21+
6. **Enhancer Modification**: Based on your analysis, make targeted changes to the specific enhancer's detection logic. Focus on:
22+
- Improving selector accuracy
23+
- Handling edge cases in the DOM structure
24+
- Optimizing detection algorithms for the specific site pattern
25+
- Ensuring compatibility with dynamic content loading
26+
27+
7. **Iterative Testing**: After making changes, rebuild and test again to validate improvements. Continue this cycle until the detection logic works correctly for the target corpus.
28+
29+
8. **Documentation**: Clearly explain what issues you found, what changes you made, and why those changes improve the detection logic.
30+
31+
Key principles:
32+
- Always work incrementally - make small, targeted changes and test frequently
33+
- Focus on the specific corpus mentioned by the user unless told otherwise
34+
- Pay attention to browser console errors and network issues that might affect detection
35+
- Consider how your changes might impact other sites or corpus entries
36+
- Be methodical in your debugging approach - document what you try and what results you observe
37+
- Understand that corpus can be either HAR files (for initial page loads) or HTML snapshots (for post-interaction states)
38+
39+
You have expertise in CSS selectors, DOM manipulation, JavaScript debugging, and understanding how different websites structure their comment systems. Use this knowledge to create robust, reliable detection logic that works across various edge cases.

.claude/commands/finish-wc.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- run `git status` to see the changes in the working copy
2+
- **figure out the intent of the change**
3+
- complete whatever tasks are necessary to complete this change
4+
- make sure that `pnpm precommit` succeeds when you are done
5+
- don't fix `precommit` by reverting the changes, it is more important to complete the initial change

browser-extension/README.md

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,42 @@ When the `textarea` gets removed from the page, the `TextareaRegistry` is notifi
6969
## Testing
7070

7171
- `npm run playground` gives you a test environment where you can tinker with the popup with various test data, supports hot reload
72-
- `npm run har:view` gives you recordings of various web pages which you can see with and without enhancement by the browser extension
73-
74-
### Recording new HAR files
75-
76-
- the har recordings live in `tests/har`, they are complete recordings of the network requests of a single page load
77-
- you can add or change URLs in `tests/har-index.ts`
78-
- `npx playwright codegen https://github.com/login --save-storage=playwright/.auth/gh.json` will store new auth tokens
79-
- login manually, then close the browser
80-
- ***these cookies are very sensitive! we only run this script using a test account that has no permissions or memberships to anything, recommend you do the same!***
81-
- `pnpm run har:record` this records new snapshots using those auth tokens (it needs args, run it with no args for docs)
82-
- DO NOT COMMIT AND PUSH NEW OR CHANGED `har` files!
83-
- we try to sanitize these (see `har-record.ts` for details) but there may be important PII in them
84-
- if you need new HAR files for something, let us know and we will generate them ourselves using a dummy account
85-
- IF YOUR PR CHANGES OR ADDS HAR FILES WE WILL CLOSE IT. Ask for HAR files and we'll be happy to generate clean ones you can test against.
72+
- `npm run corpus:view` gives you recordings of various web pages which you can see with and without enhancement by the browser extension
73+
74+
### Test Corpus
75+
76+
We maintain a corpus of test pages in two formats for testing the browser extension:
77+
78+
#### HAR Corpus (Automated)
79+
80+
- For testing initial page loads and network requests
81+
- HAR recordings live in `tests/corpus/har/`, complete recordings of the network requests of a single page load
82+
- You can add or change URLs in `tests/corpus/_corpus-index.ts`
83+
- **Recording new HAR files:**
84+
- `npx playwright codegen https://github.com/login --save-storage=playwright/.auth/gh.json` will store new auth tokens
85+
- login manually, then close the browser
86+
- ***these cookies are very sensitive! we only run this script using a test account that has no permissions or memberships to anything, recommend you do the same!***
87+
- `pnpm run corpus:record:har` records new HAR files using those auth tokens (it needs args, run it with no args for docs)
88+
- DO NOT COMMIT AND PUSH NEW OR CHANGED HAR files!
89+
- we try to sanitize these (see `har-record.ts` for details) but there may be important PII in them
90+
- if you need new HAR files for something, let us know and we will generate them ourselves using a dummy account
91+
- IF YOUR PR CHANGES OR ADDS HAR FILES WE WILL CLOSE IT. Ask for HAR files and we'll be happy to generate clean ones you can test against.
92+
93+
#### HTML Corpus (Manual)
94+
95+
- For testing post-interaction states (e.g., expanded textareas, modal dialogs, dynamic content)
96+
- HTML snapshots live in `tests/corpus/html/`, manually captured using SingleFile browser extension
97+
- All assets are inlined in a single HTML file by SingleFile
98+
- **Creating new HTML corpus files:**
99+
1. Navigate to the desired page state (click buttons, expand textareas, etc.)
100+
2. Use SingleFile browser extension to save the complete page
101+
3. Save the `.html` file to `tests/corpus/html/` with a descriptive name
102+
4. Add an entry to `tests/corpus/_corpus-index.ts` with `type: 'html'` and a description of the captured state
103+
104+
#### Viewing Corpus Files
105+
106+
- Run `pnpm run corpus:view` to start the test server at http://localhost:3001
107+
- Select any corpus file to view in two modes:
108+
- **Clean**: Original page without extension
109+
- **Gitcasso**: Page with extension injected for testing
110+
- Both HAR and HTML corpus types are supported

browser-extension/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@
6262
"test": "vitest run",
6363
"playground": "vite --config vite.playground.config.ts",
6464
"playground:build": "vite build --config vite.playground.config.ts",
65-
"har:record": "tsx tests/har-record.ts",
66-
"har:view": "tsx tests/har-view.ts"
65+
"corpus:record:har": "tsx tests/har-record.ts",
66+
"corpus:view": "tsx tests/corpus-view.ts"
6767
},
6868
"type": "module",
6969
"version": "0.0.1"

browser-extension/src/entrypoints/content.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
import { CONFIG } from '../lib/config'
2-
import type { CommentEvent, CommentSpot } from '../lib/enhancer'
2+
import type { CommentEvent, CommentSpot, StrippedLocation } from '../lib/enhancer'
33
import { logger } from '../lib/logger'
44
import { EnhancerRegistry, TextareaRegistry } from '../lib/registries'
55

66
const enhancers = new EnhancerRegistry()
77
const enhancedTextareas = new TextareaRegistry()
88

9+
// Expose for debugging in har:view
10+
;(window as any).gitcassoTextareaRegistry = enhancedTextareas
11+
12+
function detectLocation(): StrippedLocation {
13+
if ((window as any).gitcassoMockLocation) {
14+
return (window as any).gitcassoMockLocation
15+
}
16+
const result = {
17+
host: window.location.host,
18+
pathname: window.location.pathname,
19+
}
20+
logger.debug('[gitcasso] detectLocation called, returning:', result)
21+
return result
22+
}
23+
924
function sendEventToBackground(type: 'ENHANCED' | 'DESTROYED', spot: CommentSpot): void {
1025
const message: CommentEvent = {
1126
spot,
@@ -23,6 +38,7 @@ enhancedTextareas.setEventHandlers(
2338

2439
export default defineContentScript({
2540
main() {
41+
logger.debug('Main was called')
2642
const textAreasOnPageLoad = document.querySelectorAll<HTMLTextAreaElement>(`textarea`)
2743
for (const textarea of textAreasOnPageLoad) {
2844
enhanceMaybe(textarea)
@@ -32,7 +48,7 @@ export default defineContentScript({
3248
childList: true,
3349
subtree: true,
3450
})
35-
logger.debug('Extension loaded with', enhancers.getEnhancerCount, 'handlers')
51+
logger.debug('Extension loaded with', enhancers.getEnhancerCount(), 'handlers')
3652
},
3753
matches: ['<all_urls>'],
3854
runAt: 'document_end',
@@ -77,24 +93,29 @@ function handleMutations(mutations: MutationRecord[]): void {
7793
}
7894

7995
function enhanceMaybe(textarea: HTMLTextAreaElement) {
96+
logger.debug('[gitcasso] enhanceMaybe called for textarea:', textarea.id, textarea.className)
8097
if (enhancedTextareas.get(textarea)) {
8198
logger.debug('textarea already registered {}', textarea)
8299
return
83100
}
84101

85-
logger.debug('activating textarea {}', textarea)
86102
injectStyles()
87-
88-
const enhancedTextarea = enhancers.tryToEnhance(textarea)
89-
if (enhancedTextarea) {
90-
logger.debug(
91-
'Identified textarea:',
92-
enhancedTextarea.spot.type,
93-
enhancedTextarea.spot.unique_key,
94-
)
95-
enhancedTextareas.register(enhancedTextarea)
96-
} else {
97-
logger.debug('No handler found for textarea')
103+
try {
104+
const location = detectLocation()
105+
logger.debug('[gitcasso] Calling tryToEnhance with location:', location)
106+
const enhancedTextarea = enhancers.tryToEnhance(textarea, location)
107+
if (enhancedTextarea) {
108+
logger.debug(
109+
'Identified textarea:',
110+
enhancedTextarea.spot.type,
111+
enhancedTextarea.spot.unique_key,
112+
)
113+
enhancedTextareas.register(enhancedTextarea)
114+
} else {
115+
logger.debug('No handler found for textarea')
116+
}
117+
} catch (e) {
118+
logger.error(e)
98119
}
99120
}
100121

browser-extension/src/lib/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ export type LogLevel = (typeof LOG_LEVELS)[number]
99
export const CONFIG = {
1010
ADDED_OVERTYPE_CLASS: 'gitcasso-overtype',
1111
EXTENSION_NAME: 'gitcasso', // decorates logs
12-
LOG_LEVEL: 'INFO' satisfies LogLevel,
12+
LOG_LEVEL: 'DEBUG' satisfies LogLevel,
1313
MODE: 'PROD' satisfies ModeType,
1414
} as const

browser-extension/src/lib/enhancer.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ export interface CommentEvent {
1919
draft?: string
2020
}
2121

22+
/**
23+
* Minimal location information that enhancers need for routing decisions.
24+
* Avoids dependency on global window/location objects for better testability.
25+
*/
26+
export interface StrippedLocation {
27+
host: string
28+
pathname: string
29+
}
30+
2231
/** Wraps the textareas of a given platform with Gitcasso's enhancements. */
2332
export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
2433
/** Guarantees to only return a type within this list. */
@@ -27,7 +36,7 @@ export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
2736
* Whenever a new `textarea` is added to any webpage, this method is called.
2837
* If we return non-null, then we become the handler for that text area.
2938
*/
30-
tryToEnhance(textarea: HTMLTextAreaElement): Spot | null
39+
tryToEnhance(textarea: HTMLTextAreaElement, location: StrippedLocation): Spot | null
3140
/** This gets called the first time that `tryToEnhance` returns non-null. */
3241
prepareForFirstEnhancement(): void
3342
/**

browser-extension/src/lib/enhancers/CommentEnhancerMissing.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { OverTypeInstance } from 'overtype'
22
import type { ReactNode } from 'react'
3-
import type { CommentEnhancer, CommentSpot } from '../enhancer'
3+
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '../enhancer'
44

55
/** Used when an entry is in the table which we don't recognize. */
66
export class CommentEnhancerMissing implements CommentEnhancer {
@@ -40,7 +40,7 @@ export class CommentEnhancerMissing implements CommentEnhancer {
4040
forSpotTypes(): string[] {
4141
throw new Error('Method not implemented.')
4242
}
43-
tryToEnhance(_textarea: HTMLTextAreaElement): CommentSpot | null {
43+
tryToEnhance(_textarea: HTMLTextAreaElement, _location: StrippedLocation): CommentSpot | null {
4444
throw new Error('Method not implemented.')
4545
}
4646
prepareForFirstEnhancement(): void {

browser-extension/src/lib/enhancers/github/githubIssueAddComment.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { IssueOpenedIcon } from '@primer/octicons-react'
22
import OverType, { type OverTypeInstance } from 'overtype'
33
import type React from 'react'
4-
import type { CommentEnhancer, CommentSpot } from '@/lib/enhancer'
4+
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '@/lib/enhancer'
55
import { logger } from '@/lib/logger'
66
import { modifyDOM } from '../modifyDOM'
77
import { commonGithubOptions } from './ghOptions'
@@ -20,24 +20,31 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer<GitHubIssu
2020
return ['GH_ISSUE_ADD_COMMENT']
2121
}
2222

23-
tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueAddCommentSpot | null {
24-
if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') {
23+
tryToEnhance(
24+
textarea: HTMLTextAreaElement,
25+
location: StrippedLocation,
26+
): GitHubIssueAddCommentSpot | null {
27+
if (textarea.id === 'feedback') {
28+
return null
29+
}
30+
if (location.host !== 'github.com') {
2531
return null
2632
}
2733

2834
// Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456
29-
logger.debug(`${this.constructor.name} examing url`, window.location.pathname)
35+
logger.debug(`${this.constructor.name} examing url`, location.pathname)
3036

31-
const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/(\d+))/)
32-
logger.debug(`${this.constructor.name} found match`, window.location.pathname)
37+
const match = location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/(\d+))/)
38+
logger.debug(`${this.constructor.name} found match`, location.pathname)
3339
if (!match) return null
40+
3441
const [, owner, repo, numberStr] = match
3542
const slug = `${owner}/${repo}`
3643
const number = parseInt(numberStr!, 10)
3744
const unique_key = `github.com:${slug}:${number}`
3845
const title = 'TODO_TITLE'
3946
return {
40-
domain: 'github.com',
47+
domain: location.host,
4148
number,
4249
slug,
4350
title,

browser-extension/src/lib/enhancers/github/githubIssueNewComment.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import OverType, { type OverTypeInstance } from 'overtype'
2-
import type { CommentEnhancer, CommentSpot } from '../../enhancer'
2+
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '../../enhancer'
33
import { logger } from '../../logger'
44
import { modifyDOM } from '../modifyDOM'
55
import { commonGithubOptions } from './ghOptions'
@@ -16,23 +16,26 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer<GitHubIssu
1616
return ['GH_ISSUE_NEW_COMMENT']
1717
}
1818

19-
tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueNewCommentSpot | null {
20-
if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') {
19+
tryToEnhance(
20+
_textarea: HTMLTextAreaElement,
21+
location: StrippedLocation,
22+
): GitHubIssueNewCommentSpot | null {
23+
if (location.host !== 'github.com') {
2124
return null
2225
}
2326

2427
// Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456
25-
logger.debug(`${this.constructor.name} examing url`, window.location.pathname)
28+
logger.debug(`${this.constructor.name} examing url`, location.pathname)
2629

27-
const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/)
28-
logger.debug(`${this.constructor.name} found match`, window.location.pathname)
30+
const match = location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/)
31+
logger.debug(`${this.constructor.name} found match`, location.pathname)
2932

3033
if (!match) return null
3134
const [, owner, repo] = match
3235
const slug = `${owner}/${repo}`
3336
const unique_key = `github.com:${slug}:new`
3437
return {
35-
domain: 'github.com',
38+
domain: location.host,
3639
slug,
3740
type: 'GH_ISSUE_NEW_COMMENT',
3841
unique_key,

0 commit comments

Comments
 (0)