Skip to content

Commit 348af96

Browse files
authored
First cut at a data model (#6)
2 parents 1e02cb6 + 2feab18 commit 348af96

File tree

20 files changed

+462
-88
lines changed

20 files changed

+462
-88
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
on:
2+
pull_request:
3+
push:
4+
branches: [main, release]
5+
workflow_dispatch:
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.ref }}
8+
cancel-in-progress: true
9+
jobs:
10+
lint-test-and-build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v5
14+
- uses: actions/setup-node@v5
15+
with:
16+
node-version-file: '.nvmrc'
17+
cache: 'npm'
18+
cache-dependency-path: browser-extension/package-lock.json
19+
- run: npm ci
20+
working-directory: browser-extension
21+
- run: npm run biome
22+
working-directory: browser-extension
23+
- run: npm test
24+
working-directory: browser-extension
25+
- run: npm run compile
26+
working-directory: browser-extension

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# <img align="left" src="browser-extension/public/icons/icon-48.png"> Gitcasso
22

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

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

1414
Special thanks to:
15-
- [overtype](https://github.com/panphora/overtype) for the trick which makes syntax highlighting possible
16-
- [shiki](https://github.com/shikijs/shiki) for the broad library of syntax highlighters
15+
- [overtype](https://overtype.dev/) for doing `textarea` syntax highlighting of `md`
16+
- [highlight.js](https://highlightjs.org/) for the broad library of syntax highlighters
1717
- [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

browser-extension/README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
- `npm run biome` - runs `biome check` (lint & formatting)
1818
- `npm run biome:fix` - fixes most of what `biome check` finds
1919
- `npm run compile` - typechecking
20-
- `npm run test` - vitest
20+
- `npm test` - vitest
2121

2222
### Deployment
2323
- `npm run build` - build for mv3 for most browsers
@@ -27,10 +27,18 @@
2727

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

30-
- finds `textarea` components and decorates them with [overtype](https://overtype.dev/) and [shiki](https://github.com/shikijs/shiki).
30+
- finds `textarea` components and decorates them with [overtype](https://overtype.dev/) and [highlight.js](https://highlightjs.org/)
3131
- stores unposted comment drafts, and makes them easy to find via the extension popup
3232

3333
### Entry points
3434

35-
- src/entrypoints/content.ts - injected into every webpage
36-
- src/entrypoints/popup - html/css/ts which opens when the extension's button gets clicked
35+
- `src/entrypoints/content.ts` - injected into every webpage
36+
- `src/entrypoints/popup` - html/css/ts which opens when the extension's button gets clicked
37+
38+
### Architecture
39+
40+
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`].
41+
42+
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).
43+
44+
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).

browser-extension/biome.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"linter": {
4242
"rules": {
4343
"complexity": {
44-
"noExcessiveCognitiveComplexity": "warn"
44+
"noExcessiveCognitiveComplexity": "off"
4545
},
4646
"correctness": {
4747
"noUnusedVariables": "error",
@@ -65,7 +65,7 @@
6565
"allow": ["assert", "error", "info", "warn"]
6666
}
6767
},
68-
"noExplicitAny": "error",
68+
"noExplicitAny": "off",
6969
"noVar": "error"
7070
}
7171
}

browser-extension/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"@wxt-dev/webextension-polyfill": "^1.0.0",
55
"webextension-polyfill": "^0.12.0"
66
},
7-
"description": "Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly places).",
7+
"description": "Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).",
88
"devDependencies": {
99
"@biomejs/biome": "^2.1.2",
1010
"@testing-library/jest-dom": "^6.6.4",
@@ -41,5 +41,5 @@
4141
"zip:firefox": "wxt zip -b firefox"
4242
},
4343
"type": "module",
44-
"version": "1.0.0"
44+
"version": "0.0.1"
4545
}
Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
import { CONFIG } from './content/config'
2-
import { logger } from './content/logger'
3-
import { injectStyles } from './content/styles'
1+
import { CONFIG } from '../lib/config'
2+
import { logger } from '../lib/logger'
3+
import { EnhancerRegistry, TextareaRegistry } from '../lib/registries'
4+
5+
const enhancers = new EnhancerRegistry()
6+
const enhancedTextareas = new TextareaRegistry()
47

58
export default defineContentScript({
69
main() {
710
const textAreasOnPageLoad = document.querySelectorAll<HTMLTextAreaElement>(`textarea`)
811
for (const textarea of textAreasOnPageLoad) {
9-
initializeMaybe(textarea)
12+
enhanceMaybe(textarea)
1013
}
1114
const observer = new MutationObserver(handleMutations)
1215
observer.observe(document.body, {
1316
childList: true,
1417
subtree: true,
1518
})
16-
logger.debug('Extension loaded')
19+
logger.debug('Extension loaded with', enhancers.getEnhancerCount, 'handlers')
1720
},
1821
matches: ['<all_urls>'],
1922
runAt: 'document_end',
@@ -24,20 +27,72 @@ function handleMutations(mutations: MutationRecord[]): void {
2427
for (const node of mutation.addedNodes) {
2528
if (node.nodeType === Node.ELEMENT_NODE) {
2629
const element = node as Element
27-
if (element.tagName === 'textarea') {
28-
initializeMaybe(element as HTMLTextAreaElement)
30+
if (element.tagName === 'TEXTAREA') {
31+
enhanceMaybe(element as HTMLTextAreaElement)
32+
} else {
33+
// Also check for textareas within added subtrees
34+
const textareas = element.querySelectorAll?.('textarea')
35+
if (textareas) {
36+
for (const textarea of textareas) {
37+
enhanceMaybe(textarea)
38+
}
39+
}
40+
}
41+
}
42+
}
43+
44+
for (const node of mutation.removedNodes) {
45+
if (node.nodeType === Node.ELEMENT_NODE) {
46+
const element = node as Element
47+
if (element.tagName === 'TEXTAREA') {
48+
enhancedTextareas.unregisterDueToModification(element as HTMLTextAreaElement)
49+
} else {
50+
// Also check for textareas within removed subtrees
51+
const textareas = element.querySelectorAll?.('textarea')
52+
if (textareas) {
53+
for (const textarea of textareas) {
54+
enhancedTextareas.unregisterDueToModification(textarea)
55+
}
56+
}
2957
}
3058
}
3159
}
3260
}
3361
}
3462

35-
function initializeMaybe(textarea: HTMLTextAreaElement) {
36-
if (!textarea.classList.contains(CONFIG.ADDED_OVERTYPE_CLASS)) {
37-
logger.debug('activating textarea {}', textarea)
38-
injectStyles()
39-
textarea.classList.add(CONFIG.ADDED_OVERTYPE_CLASS)
63+
function enhanceMaybe(textarea: HTMLTextAreaElement) {
64+
if (enhancedTextareas.get(textarea)) {
65+
logger.debug('textarea already registered {}', textarea)
66+
return
67+
}
68+
69+
logger.debug('activating textarea {}', textarea)
70+
injectStyles()
71+
72+
const enhancedTextarea = enhancers.tryToEnhance(textarea)
73+
if (enhancedTextarea) {
74+
logger.debug(
75+
'Identified textarea:',
76+
enhancedTextarea.spot.type,
77+
enhancedTextarea.spot.unique_key,
78+
)
79+
enhancedTextareas.register(enhancedTextarea)
4080
} else {
41-
logger.debug('already activated textarea {}', textarea)
81+
logger.debug('No handler found for textarea')
82+
}
83+
}
84+
85+
const STYLES = `
86+
.${CONFIG.ADDED_OVERTYPE_CLASS} {
87+
background: cyan !important;
88+
}
89+
`
90+
91+
function injectStyles(): void {
92+
if (!document.getElementById('gitcasso-styles')) {
93+
const style = document.createElement('style')
94+
style.textContent = STYLES
95+
style.id = 'gitcasso-styles'
96+
document.head.appendChild(style)
4297
}
4398
}

browser-extension/src/entrypoints/content/styles.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

browser-extension/src/entrypoints/popup/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<div id="app">
1010
<div class="header">
1111
<div class="logo">Gitcasso Markdown Assistant</div>
12-
<div class="subtitle">Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly places).</div>
12+
<div class="subtitle">Syntax highlighting and autosave for comments on GitHub (and other other markdown-friendly websites).</div>
1313
</div>
1414
<div id="scan-results">
1515
<p>Loading drafts from local storage...</p>
File renamed without changes.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { OverType } from '../overtype/mock-overtype'
2+
3+
/**
4+
* stores enough info about the location of a draft to:
5+
* - display it in a table
6+
* - reopen the draft in-context
7+
*/
8+
export interface CommentSpot {
9+
unique_key: string
10+
type: string
11+
}
12+
13+
/** wraps the textareas of a given platform with Gitcasso's enhancements */
14+
export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
15+
/** guarantees to only return a type within this list */
16+
forSpotTypes(): string[]
17+
/**
18+
* whenever a new `textarea` is added to any webpage, this method is called.
19+
* if we return non-null, then we become the handler for that text area.
20+
*/
21+
tryToEnhance(textarea: HTMLTextAreaElement): [OverType, Spot] | null
22+
23+
tableIcon(spot: Spot): string
24+
tableTitle(spot: Spot): string
25+
buildUrl(spot: Spot): string
26+
}

0 commit comments

Comments
 (0)