Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion browser-extension/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"recommended": true,
"style": {
"noDefaultExport": "off",
"noNonNullAssertion": "warn",
"noNonNullAssertion": "off",
"noParameterAssign": {
"options": {
"propertyAssignment": "deny"
Expand Down
7 changes: 7 additions & 0 deletions browser-extension/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions browser-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"dependencies": {
"@wxt-dev/webextension-polyfill": "^1.0.0",
"highlight.js": "^11.11.1",
"markdown-actions": "^1.1.2",
"webextension-polyfill": "^0.12.0"
},
"description": "Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).",
Expand Down
4 changes: 2 additions & 2 deletions browser-extension/src/entrypoints/content.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CONFIG } from '../lib/config'
import { CONFIG, type ModeType } from '../lib/config'
import { logger } from '../lib/logger'
import { EnhancerRegistry, TextareaRegistry } from '../lib/registries'
import { githubPrNewCommentContentScript } from '../playgrounds/github-playground'
Expand All @@ -8,7 +8,7 @@ const enhancedTextareas = new TextareaRegistry()

export default defineContentScript({
main() {
if (CONFIG.MODE === 'PLAYGROUNDS_PR') {
if ((CONFIG.MODE as ModeType) === 'PLAYGROUNDS_PR') {
githubPrNewCommentContentScript()
return
}
Expand Down
2 changes: 1 addition & 1 deletion browser-extension/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export const CONFIG = {
ADDED_OVERTYPE_CLASS: 'gitcasso-overtype',
DEBUG: true, // enabled debug logging
EXTENSION_NAME: 'gitcasso', // decorates logs
MODE: 'PLAYGROUNDS_PR' satisfies ModeType,
MODE: 'PROD' satisfies ModeType,
} as const
4 changes: 2 additions & 2 deletions browser-extension/src/lib/enhancer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OverType } from '../overtype/mock-overtype'
import type { OverTypeInstance } from '../overtype/overtype'

/**
* stores enough info about the location of a draft to:
Expand All @@ -18,7 +18,7 @@ export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
* whenever a new `textarea` is added to any webpage, this method is called.
* if we return non-null, then we become the handler for that text area.
*/
tryToEnhance(textarea: HTMLTextAreaElement): [OverType, Spot] | null
tryToEnhance(textarea: HTMLTextAreaElement): [OverTypeInstance, Spot] | null

tableIcon(spot: Spot): string
tableTitle(spot: Spot): string
Expand Down
130 changes: 66 additions & 64 deletions browser-extension/src/lib/enhancers/github.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { OverType } from '../../overtype/mock-overtype'
import hljs from 'highlight.js'
import { logger } from '../../lib/logger'
import OverType, { type OverTypeInstance } from '../../overtype/overtype'
import type { CommentEnhancer, CommentSpot } from '../enhancer'

const GITHUB_SPOT_TYPES = [
'GH_PR_ADD_COMMENT',
/* TODO
'GH_ISSUE_NEW',
'GH_PR_NEW',
'GH_ISSUE_ADD_COMMENT',
'GH_PR_ADD_COMMENT',
/* TODO
'GH_ISSUE_EDIT_COMMENT',
'GH_PR_EDIT_COMMENT',
'GH_PR_CODE_COMMENT',
Expand All @@ -15,95 +17,95 @@ const GITHUB_SPOT_TYPES = [

export type GitHubSpotType = (typeof GITHUB_SPOT_TYPES)[number]

export interface GitHubSpot extends CommentSpot {
export interface GitHubAddCommentSpot extends CommentSpot {
type: GitHubSpotType // Override to narrow from string to specific union
domain: string
slug: string // owner/repo
number?: number | undefined // issue/PR number, undefined for new issues and PRs
number: number // issue/PR number, undefined for new issues and PRs
}

export class GitHubEnhancer implements CommentEnhancer<GitHubSpot> {
export class GitHubAddCommentEnhancer implements CommentEnhancer<GitHubAddCommentSpot> {
forSpotTypes(): string[] {
return [...GITHUB_SPOT_TYPES]
}

tryToEnhance(textarea: HTMLTextAreaElement): [OverType, GitHubSpot] | null {
// Only handle GitHub domains
if (!window.location.hostname.includes('github')) {
tryToEnhance(textarea: HTMLTextAreaElement): [OverTypeInstance, GitHubAddCommentSpot] | null {
// Only handle github.com domains TODO: identify GitHub Enterprise somehow
if (window.location.hostname !== 'github.com') {
return null
}

const pathname = window.location.pathname

// Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456
const match = pathname.match(/^\/([^/]+)\/([^/]+)(?:\/(issues|pull)\/(\d+))?/)
if (!match) return null
logger.debug(`${this.constructor.name} examing url`, window.location.pathname)

const [, owner, repo, urlType, numberStr] = match
const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/pull\/(\d+))/)
logger.debug(`${this.constructor.name} found match`, window.location.pathname)
if (!match) return null
const [, owner, repo, numberStr] = match
const slug = `${owner}/${repo}`
const number = numberStr ? parseInt(numberStr, 10) : undefined

// Determine comment type
let type: GitHubSpotType

if (pathname.includes('/issues/new')) {
type = 'GH_ISSUE_NEW'
} else if (pathname.includes('/compare/') || pathname.endsWith('/compare')) {
type = 'GH_PR_NEW'
} else if (urlType && number) {
if (urlType === 'issues') {
type = 'GH_ISSUE_ADD_COMMENT'
} else {
type = 'GH_PR_ADD_COMMENT'
}
} else {
return null
}
const number = parseInt(numberStr!, 10)

// Generate unique key based on context
let unique_key = `github:${slug}`
if (number) {
unique_key += `:${urlType}:${number}`
} else {
unique_key += ':new'
}
const unique_key = `github.com:${slug}:${number}`

const spot: GitHubSpot = {
domain: window.location.hostname,
const spot: GitHubAddCommentSpot = {
domain: 'github.com',
number,
slug,
type,
type: 'GH_PR_ADD_COMMENT',
unique_key,
}
const overtype = new OverType(textarea)
return [overtype, spot]
return [this.createOvertypeFor(textarea), spot]
}

private createOvertypeFor(ghCommentBox: HTMLTextAreaElement): OverTypeInstance {
OverType.setCodeHighlighter(hljsHighlighter)
const overtypeContainer = this.modifyDOM(ghCommentBox)
return new OverType(overtypeContainer, {
autoResize: true,
minHeight: '102px',
padding: 'var(--base-size-8)',
placeholder: 'Add your comment here...',
})[0]!
}

private modifyDOM(overtypeInput: HTMLTextAreaElement): HTMLElement {
overtypeInput.classList.add('overtype-input')
const overtypePreview = document.createElement('div')
overtypePreview.classList.add('overtype-preview')
overtypeInput.insertAdjacentElement('afterend', overtypePreview)
const overtypeWrapper = overtypeInput.parentElement!.closest('div')!
overtypeWrapper.classList.add('overtype-wrapper')
overtypeInput.placeholder = 'Add your comment here...'
const overtypeContainer = overtypeWrapper.parentElement!.closest('div')!
overtypeContainer.classList.add('overtype-container')
return overtypeContainer.parentElement!.closest('div')!
}

tableTitle(spot: GitHubSpot): string {
tableTitle(spot: GitHubAddCommentSpot): string {
const { slug, number } = spot
if (number) {
return `Comment on ${slug} #${number}`
}
return `New ${window.location.pathname.includes('/issues/') ? 'issue' : 'PR'} in ${slug}`
return `${slug} PR #${number}`
}

tableIcon(spot: GitHubSpot): string {
switch (spot.type) {
case 'GH_ISSUE_NEW':
case 'GH_ISSUE_ADD_COMMENT':
return '🐛' // Issue icon
case 'GH_PR_NEW':
case 'GH_PR_ADD_COMMENT':
return '🔄' // PR icon
}
tableIcon(_: GitHubAddCommentSpot): string {
return '🔄' // PR icon TODO: icon urls in /public
}

buildUrl(spot: GitHubAddCommentSpot): string {
return `https://${spot.domain}/${spot.slug}/pull/${spot.number}`
}
}

buildUrl(spot: GitHubSpot): string {
const baseUrl = `https://${spot.domain}/${spot.slug}`
if (spot.number) {
const type = spot.type.indexOf('ISSUE') ? 'issues' : 'pull'
return `${baseUrl}/${type}/${spot.number}`
function hljsHighlighter(code: string, language: string) {
try {
if (language && hljs.getLanguage(language)) {
const result = hljs.highlight(code, { language })
return result.value
} else {
const result = hljs.highlightAuto(code)
return result.value
}
return baseUrl
} catch (error) {
console.warn('highlight.js highlighting failed:', error)
return code
}
}
16 changes: 8 additions & 8 deletions browser-extension/src/lib/registries.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import type { OverType } from '../overtype/mock-overtype'
import type { OverTypeInstance } from '../overtype/overtype'
import type { CommentEnhancer, CommentSpot } from './enhancer'
import { GitHubEnhancer } from './enhancers/github'
import { GitHubAddCommentEnhancer } from './enhancers/github'

export interface EnhancedTextarea<T extends CommentSpot = CommentSpot> {
textarea: HTMLTextAreaElement
spot: T
handler: CommentEnhancer<T>
overtype: OverType
enhancer: CommentEnhancer<T>
overtype: OverTypeInstance
}

export class EnhancerRegistry {
private enhancers = new Set<CommentEnhancer<any>>()

constructor() {
// Register all available handlers
this.register(new GitHubEnhancer())
this.register(new GitHubAddCommentEnhancer())
}

private register<T extends CommentSpot>(handler: CommentEnhancer<T>): void {
this.enhancers.add(handler)
}

tryToEnhance(textarea: HTMLTextAreaElement): EnhancedTextarea<any> | null {
for (const handler of this.enhancers) {
for (const enhancer of this.enhancers) {
try {
const result = handler.tryToEnhance(textarea)
const result = enhancer.tryToEnhance(textarea)
if (result) {
const [overtype, spot] = result
return { handler, overtype, spot, textarea }
return { enhancer, overtype, spot, textarea }
}
} catch (error) {
console.warn('Handler failed to identify textarea:', error)
Expand Down
44 changes: 0 additions & 44 deletions browser-extension/src/overtype/mock-overtype.ts

This file was deleted.

This file was deleted.

Loading