Skip to content
Merged
57 changes: 57 additions & 0 deletions .claude/commands/corpus-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
argument-hint: [corpus_slug]
description: uses Playwright MCP and the `corpus:view` to parse page elements
---

- using Playwright MCP, navigate to `http://localhost:3001/corpus/$1/gitcasso`
- the page will have a div with id `gitcasso-comment-spots`, wait 500ms for it to settle
- inside the `gitcasso-comment-spots` div you will see something like this:

```json
{
"url": "https://github.com/diffplug/selfie/issues/523",
"allTextAreas": [
{
"textarea": "id='feedback' name='feedback' className='form-control width-full mb-2'",
"spot": "NO_SPOT"
},
{
"textarea": "id=':rn:' name='' className='prc-Textarea-TextArea-13q4j overtype-input'",
"spot": {
"domain": "github.com",
"number": 523,
"slug": "diffplug/selfie",
"title": "TODO_TITLE",
"type": "GH_ISSUE_ADD_COMMENT",
"unique_key": "github.com:diffplug/selfie:523"
}
}
]
}
```

- this output means that this page is simulating the url `https://github.com/diffplug/selfie/issues/523`
- every textarea on the page is represented
- `NO_SPOT` means that the spot was not enhanced
- `type: GH_ISSUE_ADD_COMMENT` means that it was enhanced by whichever implementation of `CommentEnhancer` returns the spot type `GH_ISSUE_ADD_COMMENT`
- if you search for that string in `src/lib/enhancers` you will find the correct one
- the `tryToEnhance` method returned a `CommentSpot`, and that whole data is splatted out above

If you make a change to the code of the enhancer, you can click the button with id `gitcasso-rebuild-btn`. It will trigger a rebuild of the browser extension, and then refresh the page. You'll be able to see the effects of your change in the `gitcasso-comment-spots` div described above.

## Common extraction workflow

If you see `"title": "TODO_TITLE"` or similar hardcoded `TODO` values in the JSON output, this indicates the enhancer needs some kind of extraction implemented:

1. **Find the enhancer**: Search for the `type` value (e.g., `GH_ISSUE_ADD_COMMENT`) in `src/lib/enhancers/`
2. **Implement extraction**: Replace hardcoded title with DOM extraction:
```javascript
const title = document.querySelector('main h1')!.textContent.replace(/\s*#\d+$/, '').trim()
```
4. **Test with rebuild**: Click the 🔄 button to rebuild and verify the title appears correctly in the JSON

## Extraction code style

- Don't hedge your bets and write lots of fallback code or strings of `?.`. Have a specific piece of data you want to get, use non-null `!` assertions where necessary to be clear about getting.
- If a field is empty, represent it with an empty string. Don't use placeholders when extracting data.
- The pages we are scraping are going to change over time, and it's easier to fix broken ones if we know exactly what used to work. If the code has lots of branching paths, it's harder to tell what it was doing.
6 changes: 1 addition & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,5 @@ dist/
.DS_Store
Thumbs.db

# playright
# playwright
.playwright-mcp/
browser-extension/dist-playground/
browser-extension/playwright-report/
browser-extension/playwright/
browser-extension/test-results/
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Please refer to `CONTRIBUTING.md` and `README.md`.
Refer to `CONTRIBUTING.md` for the project's architecture and useful commands.

Whenever you complete a task, if you wish some info had been provided to you ahead of time instead of figuring it out from scratch, you have permission to edit this `CLAUDE.md` to add any helpful context.
9 changes: 6 additions & 3 deletions src/lib/enhancers/github/githubIssueAddComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer<GitHubIssu
const slug = `${owner}/${repo}`
const number = parseInt(numberStr!, 10)
const unique_key = `github.com:${slug}:${number}`
const title = 'TODO_TITLE'
const title = document
.querySelector('main h1')!
.textContent.replace(/\s*#\d+$/, '')
.trim()
return {
domain: location.host,
number,
Expand Down Expand Up @@ -77,7 +80,7 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer<GitHubIssu
)
}

tableTitle(_spot: GitHubIssueAddCommentSpot): string {
return 'TITLE_TODO'
tableTitle(spot: GitHubIssueAddCommentSpot): string {
return spot.title
}
}
13 changes: 10 additions & 3 deletions src/lib/enhancers/github/githubIssueNewComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface GitHubIssueNewCommentSpot extends CommentSpot {
type: 'GH_ISSUE_NEW_COMMENT'
domain: string
slug: string // owner/repo
title: string
}

export class GitHubIssueNewCommentEnhancer implements CommentEnhancer<GitHubIssueNewCommentSpot> {
Expand All @@ -17,9 +18,12 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer<GitHubIssu
}

tryToEnhance(
_textarea: HTMLTextAreaElement,
textarea: HTMLTextAreaElement,
location: StrippedLocation,
): GitHubIssueNewCommentSpot | null {
if (textarea.id === 'feedback') {
return null
}
if (location.host !== 'github.com') {
return null
}
Expand All @@ -34,9 +38,12 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer<GitHubIssu
const [, owner, repo] = match
const slug = `${owner}/${repo}`
const unique_key = `github.com:${slug}:new`
const titleInput = document.querySelector('input[placeholder="Title"]') as HTMLInputElement
const title = titleInput?.value || ''
return {
domain: location.host,
slug,
title,
type: 'GH_ISSUE_NEW_COMMENT',
unique_key,
}
Expand All @@ -62,8 +69,8 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer<GitHubIssu
)
}

tableTitle(_spot: GitHubIssueNewCommentSpot): string {
return 'New Issue'
tableTitle(spot: GitHubIssueNewCommentSpot): string {
return spot.title || 'New Issue'
}

buildUrl(spot: GitHubIssueNewCommentSpot): string {
Expand Down
9 changes: 6 additions & 3 deletions src/lib/enhancers/github/githubPRAddComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export class GitHubPRAddCommentEnhancer implements CommentEnhancer<GitHubPRAddCo
const slug = `${owner}/${repo}`
const number = parseInt(numberStr!, 10)
const unique_key = `github.com:${slug}:${number}`
const title = 'TODO_TITLE'
const title = document
.querySelector('main h1')!
.textContent.replace(/\s*#\d+$/, '')
.trim()
return {
domain: location.host,
number,
Expand Down Expand Up @@ -70,7 +73,7 @@ export class GitHubPRAddCommentEnhancer implements CommentEnhancer<GitHubPRAddCo
)
}

tableTitle(_spot: GitHubPRAddCommentSpot): string {
return 'TITLE_TODO'
tableTitle(spot: GitHubPRAddCommentSpot): string {
return spot.title
}
}
23 changes: 16 additions & 7 deletions src/lib/enhancers/github/githubPRNewComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { prepareGitHubHighlighter } from './githubHighlighter'
interface GitHubPRNewCommentSpot extends CommentSpot {
type: 'GH_PR_NEW_COMMENT'
domain: string
slug: string // owner/repo/base-branch/compare-branch
slug: string // owner/repo
title: string
head: string // `user:repo:branch` where changes are implemented
base: string // branch you want changes pulled into
}

export class GitHubPRNewCommentEnhancer implements CommentEnhancer<GitHubPRNewCommentSpot> {
Expand Down Expand Up @@ -38,13 +41,19 @@ export class GitHubPRNewCommentEnhancer implements CommentEnhancer<GitHubPRNewCo

if (!match) return null
const [, owner, repo, baseBranch, compareBranch] = match
const slug = baseBranch
? `${owner}/${repo}/${baseBranch}...${compareBranch}`
: `${owner}/${repo}/${compareBranch}`
const unique_key = `github.com:${slug}`
const slug = `${owner}/${repo}`
const base = baseBranch || 'main'
const head = compareBranch!
const unique_key = `github.com:${slug}:${base}...${head}`
const titleInput = document.querySelector('input[placeholder="Title"]') as HTMLInputElement
const title = titleInput!.value

return {
base,
domain: location.host,
head,
slug,
title,
type: 'GH_PR_NEW_COMMENT',
unique_key,
}
Expand All @@ -70,8 +79,8 @@ export class GitHubPRNewCommentEnhancer implements CommentEnhancer<GitHubPRNewCo
)
}

tableTitle(_spot: GitHubPRNewCommentSpot): string {
return 'TITLE_TODO'
tableTitle(spot: GitHubPRNewCommentSpot): string {
return spot.title || 'New Pull Request'
}

buildUrl(spot: GitHubPRNewCommentSpot): string {
Expand Down
25 changes: 11 additions & 14 deletions tests/corpus-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,28 +477,28 @@ function createCommentSpotDisplayScript(urlParts: ReturnType<typeof getUrlParts>

function updateCommentSpotDisplay() {
const textareas = document.querySelectorAll('textarea');
const spotsFound = [];
const allTextAreas = [];

for (const textarea of textareas) {
const forValue = 'id=' + textarea.id + ' name=' + textarea.name + ' className=' + textarea.className;
const forValue = "id='" + textarea.id + "' name='" + textarea.name + "' className='" + textarea.className + "'";
const enhancedItem = window.gitcassoTextareaRegistry ? window.gitcassoTextareaRegistry.get(textarea) : undefined;
if (enhancedItem) {
spotsFound.push({
for: forValue,
allTextAreas.push({
textarea: forValue,
spot: enhancedItem.spot,
title: enhancedItem.enhancer.tableTitle(enhancedItem.spot),
});
} else {
spotsFound.push({
for: forValue,
allTextAreas.push({
textarea: forValue,
spot: 'NO_SPOT',
});
}
}

console.log('Enhanced textareas:', spotsFound.filter(s => s.spot !== 'NO_SPOT').length);
console.log('All textareas on page:', textareas.length);
commentSpotDisplay.innerHTML = '<div style="' + styles.header + '"><pre>${urlParts.href}\\n' + JSON.stringify(spotsFound, null, 2) + '</pre></div>';
const harness = {
url: '${urlParts.href}',
allTextAreas: allTextAreas
}
commentSpotDisplay.innerHTML = '<div style="' + styles.header + '"><pre>' + JSON.stringify(harness, null, 1) + '</pre></div>';
}

// Initial update
Expand All @@ -508,9 +508,6 @@ function createCommentSpotDisplayScript(urlParts: ReturnType<typeof getUrlParts>
setTimeout(updateCommentSpotDisplay, 400);
setTimeout(updateCommentSpotDisplay, 800);

// Update display periodically
setInterval(updateCommentSpotDisplay, 2000);

document.body.appendChild(commentSpotDisplay);
`
}
Expand Down
5 changes: 5 additions & 0 deletions tests/corpus/_corpus-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export const CORPUS: Record<string, CorpusEntry> = {
type: 'html',
url: 'https://github.com/diffplug/gitcasso/issues/56',
},
gh_issue_new_populated: {
description: 'a new issue wiht some fields filled out',
type: 'html',
url: 'https://github.com/diffplug/gitcasso/issues/new',
},
gh_issue_populated_comment: {
description: 'comment text box has some text',
type: 'html',
Expand Down
1,586 changes: 1,586 additions & 0 deletions tests/corpus/gh_issue_new_populated.html

Large diffs are not rendered by default.

Loading