Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
85ec3b9
wip2
Sep 3, 2025
3883abe
First cut.
Sep 3, 2025
3e676e3
Progress
Sep 3, 2025
038710c
Idiomatic typescript.
Sep 3, 2025
f66e7a9
`identifyContextOf`
Sep 3, 2025
0161e0b
Remove unnecessary methods.
Sep 3, 2025
4ca0261
Remove unnecessary base class.
Sep 3, 2025
88564d9
Move `type` from `TextareaInfo` into `CommentContext`.
Sep 3, 2025
d35d4ae
Fixup unnecessary splitting.
Sep 4, 2025
c53b60d
More refactor cleanup.
Sep 4, 2025
58ed64d
Type narrowing.
Sep 4, 2025
5a5d3a8
Wow it all compiles (!!!)
Sep 4, 2025
aa073ef
Add a `TextAreaRegistry` for tracking the textareas themselves at run…
Sep 4, 2025
3ab68d7
No more marker classes, just our registry.
Sep 4, 2025
0f354d9
Unregistering a textarea works too.
Sep 4, 2025
180b258
very small fixup - don't do the query if the thing is itself a textarea
Sep 4, 2025
63f13ee
biome:fix
Sep 4, 2025
7d845f5
allow explicit any
Sep 4, 2025
e484541
Rename
Sep 4, 2025
33a2aba
More renaming and moving.
Sep 4, 2025
b3f7730
Rename refactor.
Sep 4, 2025
bdd4662
Update README with progress so far.
Sep 4, 2025
4d0ad6a
Add a mock `Overtype` while we wait to merge.
Sep 4, 2025
faef782
The enhancers now create the `Overtype` (as they should).
Sep 4, 2025
220b835
Add CI.
Sep 4, 2025
0805bda
A bit of consistency.
Sep 4, 2025
728dbc0
Rename.
Sep 4, 2025
bfe0176
Use WXT's testing harness, don't reinvent the wheel.
Sep 4, 2025
ed34d42
First cut at a `content.test.ts`
Sep 4, 2025
dfee834
Move the test to a better place.
Sep 4, 2025
cfdea3f
Run tests in CI.
Sep 4, 2025
fe8ae09
Be more efficient with CI resources.
Sep 4, 2025
2561ef7
biome the test
Sep 4, 2025
afe4b0d
Minor tagline and version fixes.
Sep 4, 2025
5875110
Remove `RedditEnhancer`, we'll get there eventually...
Sep 4, 2025
fe74e99
Use typescript features a little better.
Sep 4, 2025
a7420f4
Simplify `github-handler` down to only the things which are likely to…
Sep 4, 2025
2fb9278
Fixup and simplify tests.
Sep 4, 2025
3f15b24
highlight.js not highlightjs
Sep 4, 2025
e7cb0de
Cleanup via renames.
Sep 4, 2025
7ad747d
More renames.
Sep 4, 2025
b9c730e
Minor cleanup.
Sep 4, 2025
8b6f80b
More cleanup.
Sep 4, 2025
dbdb756
More rename.
Sep 4, 2025
9466025
Minor rename.
Sep 4, 2025
22c826a
Remove unnecessary comments.
Sep 4, 2025
48fb4fa
datamodel -> logic, common -> util
Sep 4, 2025
2feab18
logic -> lib
Sep 4, 2025
348af96
First cut at a data model (#6)
nedtwigg Sep 4, 2025
8209ec5
Merge branch 'main' into feat/overtype-hard
Sep 4, 2025
f92f6cb
Exclude `src/overtype` from biome
nedtwigg Sep 4, 2025
8fa7f19
Add the POC script as `github-playground`
nedtwigg Sep 4, 2025
e735186
fixup package-lock.json
nedtwigg Sep 4, 2025
854d10c
Exclude the playgrounds from biome
nedtwigg Sep 4, 2025
4184941
Wire up the playground.
nedtwigg Sep 4, 2025
a124a16
Merge branch 'feat/overtype-hard' into feat/friendly-playground
nedtwigg Sep 4, 2025
a68d742
Pull latest overtype-hard changes into the github-playground.
nedtwigg Sep 4, 2025
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
26 changes: 26 additions & 0 deletions .github/workflows/browser-extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
on:
pull_request:
push:
branches: [main, release]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache-dependency-path: browser-extension/package-lock.json
- run: npm ci
working-directory: browser-extension
- run: npm run biome
working-directory: browser-extension
- run: npm test
working-directory: browser-extension
- run: npm run compile
working-directory: browser-extension
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# <img align="left" src="browser-extension/public/icons/icon-48.png"> Gitcasso

*Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly places).*
*Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).*

- "Syntax highlighting is the lie that enables us to see the truth."
- "The meaning of life is to find your lost comment drafts. The purpose of life is to post them."
Expand All @@ -12,6 +12,6 @@ TODO: screenshot of comment draft storage and restoration
If there's something you'd like to add or fix, see [CONTRIBUTING.md](CONTRIBUTING.md).

Special thanks to:
- [overtype](https://github.com/panphora/overtype) for the trick which makes syntax highlighting possible
- [shiki](https://github.com/shikijs/shiki) for the broad library of syntax highlighters
- [overtype](https://overtype.dev/) for doing `textarea` syntax highlighting of `md`
- [highlight.js](https://highlightjs.org/) for the broad library of syntax highlighters
- [Yukai Huang](https://github.com/Yukaii) for [the PRs](https://github.com/panphora/overtype/issues?q=is%3Apr+author%3AYukaii) which made the two work together
16 changes: 12 additions & 4 deletions browser-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- `npm run biome` - runs `biome check` (lint & formatting)
- `npm run biome:fix` - fixes most of what `biome check` finds
- `npm run compile` - typechecking
- `npm run test` - vitest
- `npm test` - vitest

### Deployment
- `npm run build` - build for mv3 for most browsers
Expand All @@ -27,10 +27,18 @@

This is a [WXT](https://wxt.dev/)-based browser extension that

- finds `textarea` components and decorates them with [overtype](https://overtype.dev/) and [shiki](https://github.com/shikijs/shiki).
- 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

### Entry points

- src/entrypoints/content.ts - injected into every webpage
- src/entrypoints/popup - html/css/ts which opens when the extension's button gets clicked
- `src/entrypoints/content.ts` - injected into every webpage
- `src/entrypoints/popup` - html/css/ts which opens when the extension's button gets clicked

### 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`].

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).

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).
6 changes: 3 additions & 3 deletions browser-extension/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"files": {
"ignoreUnknown": false,
"includes": [".*", "src/**", "tests/**"]
"includes": [".*", "src/**", "tests/**", "!src/overtype", "!src/playgrounds"]
},
"formatter": {
"enabled": true,
Expand Down Expand Up @@ -41,7 +41,7 @@
"linter": {
"rules": {
"complexity": {
"noExcessiveCognitiveComplexity": "warn"
"noExcessiveCognitiveComplexity": "off"
},
"correctness": {
"noUnusedVariables": "error",
Expand All @@ -65,7 +65,7 @@
"allow": ["assert", "error", "info", "warn"]
}
},
"noExplicitAny": "error",
"noExplicitAny": "off",
"noVar": "error"
}
}
Expand Down
4 changes: 2 additions & 2 deletions browser-extension/package-lock.json

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

4 changes: 2 additions & 2 deletions browser-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"overtype": "^1.2.3",
"webextension-polyfill": "^0.12.0"
},
"description": "Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly places).",
"description": "Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).",
"devDependencies": {
"@biomejs/biome": "^2.1.2",
"@testing-library/jest-dom": "^6.6.4",
Expand Down Expand Up @@ -43,5 +43,5 @@
"zip:firefox": "wxt zip -b firefox"
},
"type": "module",
"version": "1.0.0"
"version": "0.0.1"
}
136 changes: 93 additions & 43 deletions browser-extension/src/entrypoints/content.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,103 @@
import hljs from "highlight.js";
import OverType from "../overtype/overtype";
import { CONFIG } from '../lib/config'
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()

export default defineContentScript({
main() {
if (window.location.hostname !== "github.com") {
return;
if (CONFIG.MODE === 'PLAYGROUNDS_PR') {
githubPrNewCommentContentScript()
return
}
OverType.setCodeHighlighter(hljsHighlighter);
const ghCommentBox = document.getElementById(
"new_comment_field"
) as HTMLTextAreaElement | null;
if (ghCommentBox) {
const overtypeContainer = modifyDOM(ghCommentBox);
new OverType(overtypeContainer, {
placeholder: "Add your comment here...",
autoResize: true,
minHeight: "102px",
padding: "var(--base-size-8)",
});
const textAreasOnPageLoad = document.querySelectorAll<HTMLTextAreaElement>(`textarea`)
for (const textarea of textAreasOnPageLoad) {
enhanceMaybe(textarea)
}
const observer = new MutationObserver(handleMutations)
observer.observe(document.body, {
childList: true,
subtree: true,
})
logger.debug('Extension loaded with', enhancers.getEnhancerCount, 'handlers')
},
matches: ["<all_urls>"],
runAt: "document_end",
});

function 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")!;
}
matches: ['<all_urls>'],
runAt: 'document_end',
})

function handleMutations(mutations: MutationRecord[]): void {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element
if (element.tagName === 'TEXTAREA') {
enhanceMaybe(element as HTMLTextAreaElement)
} else {
// Also check for textareas within added subtrees
const textareas = element.querySelectorAll?.('textarea')
if (textareas) {
for (const textarea of textareas) {
enhanceMaybe(textarea)
}
}
}
}
}

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;
for (const node of mutation.removedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element
if (element.tagName === 'TEXTAREA') {
enhancedTextareas.unregisterDueToModification(element as HTMLTextAreaElement)
} else {
// Also check for textareas within removed subtrees
const textareas = element.querySelectorAll?.('textarea')
if (textareas) {
for (const textarea of textareas) {
enhancedTextareas.unregisterDueToModification(textarea)
}
}
}
}
}
} catch (error) {
console.warn("highlight.js highlighting failed:", error);
return code;
}
}

function enhanceMaybe(textarea: HTMLTextAreaElement) {
if (enhancedTextareas.get(textarea)) {
logger.debug('textarea already registered {}', textarea)
return
}

logger.debug('activating textarea {}', textarea)
injectStyles()

const enhancedTextarea = enhancers.tryToEnhance(textarea)
if (enhancedTextarea) {
logger.debug(
'Identified textarea:',
enhancedTextarea.spot.type,
enhancedTextarea.spot.unique_key,
)
enhancedTextareas.register(enhancedTextarea)
} else {
logger.debug('No handler found for textarea')
}
}

const STYLES = `
.${CONFIG.ADDED_OVERTYPE_CLASS} {
background: cyan !important;
}
`

function injectStyles(): void {
if (!document.getElementById('gitcasso-styles')) {
const style = document.createElement('style')
style.textContent = STYLES
style.id = 'gitcasso-styles'
document.head.appendChild(style)
}
}
9 changes: 0 additions & 9 deletions browser-extension/src/entrypoints/content/config.ts

This file was deleted.

16 changes: 0 additions & 16 deletions browser-extension/src/entrypoints/content/styles.ts

This file was deleted.

2 changes: 1 addition & 1 deletion browser-extension/src/entrypoints/popup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<div id="app">
<div class="header">
<div class="logo">Gitcasso Markdown Assistant</div>
<div class="subtitle">Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly places).</div>
<div class="subtitle">Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).</div>
</div>
<div id="scan-results">
<p>Loading drafts from local storage...</p>
Expand Down
10 changes: 10 additions & 0 deletions browser-extension/src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const MODES = ['PROD', 'PLAYGROUNDS_PR'] as const

export type ModeType = (typeof MODES)[number]

export const CONFIG = {
ADDED_OVERTYPE_CLASS: 'gitcasso-overtype',
DEBUG: true, // enabled debug logging
EXTENSION_NAME: 'gitcasso', // decorates logs
MODE: 'PLAYGROUNDS_PR' satisfies ModeType,
} as const
26 changes: 26 additions & 0 deletions browser-extension/src/lib/enhancer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { OverType } from '../overtype/mock-overtype'

/**
* stores enough info about the location of a draft to:
* - display it in a table
* - reopen the draft in-context
*/
export interface CommentSpot {
unique_key: string
type: string
}

/** wraps the textareas of a given platform with Gitcasso's enhancements */
export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
/** guarantees to only return a type within this list */
forSpotTypes(): string[]
/**
* 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

tableIcon(spot: Spot): string
tableTitle(spot: Spot): string
buildUrl(spot: Spot): string
}
Loading