Skip to content
Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8b4ee16
Add github instructions
edmcman Dec 28, 2025
8f8c4aa
Basic testing
edmcman Dec 30, 2025
8680249
upgrade
edmcman Dec 30, 2025
cfde45d
minor
edmcman Dec 30, 2025
7660175
gitignore
edmcman Dec 30, 2025
656adfb
ci
edmcman Dec 30, 2025
7e7f5d4
dummy
edmcman Dec 30, 2025
5cd49bd
CI on all branches
edmcman Dec 30, 2025
e4fb116
fix old action version
edmcman Dec 30, 2025
5a043b5
install browsers
edmcman Dec 30, 2025
8f8610a
fix tests
edmcman Dec 30, 2025
03bdddb
Add snapshots of initial tutorial
edmcman Dec 30, 2025
a1df474
wip
edmcman Dec 30, 2025
8d1897c
wip
edmcman Dec 30, 2025
e4f3639
Add devcontainer
edmcman Dec 30, 2025
be6c72d
prettify
edmcman Dec 30, 2025
433c9e7
devcontainer
edmcman Dec 30, 2025
624b882
don't format test-results
edmcman Dec 30, 2025
186440b
update snapshots
edmcman Dec 30, 2025
c26c868
wip
edmcman Dec 30, 2025
96c4200
rename test
edmcman Dec 30, 2025
e52e00d
update
edmcman Dec 30, 2025
c97dc0a
Trying to improve waiting
edmcman Dec 30, 2025
7cc934d
prettier
edmcman Dec 30, 2025
8cfddcc
CI deps
edmcman Dec 30, 2025
e95ad93
fix dev build
edmcman Dec 30, 2025
5e4a93c
logging
edmcman Dec 30, 2025
87883f5
diff ratio
edmcman Dec 30, 2025
38357f1
slow down typing slightly
edmcman Dec 30, 2025
eb08376
retry
edmcman Dec 30, 2025
d2976d6
Try to add another timeout to increase reliability
edmcman Dec 30, 2025
2786301
upload test results
edmcman Dec 30, 2025
6df6b5e
use up-to-date upload-artifacts
edmcman Dec 30, 2025
6a62f9f
Slightly modify CI gh-pages deployment
edmcman Dec 30, 2025
2c9d616
Try CI refactor again
edmcman Dec 30, 2025
099e77f
rename
edmcman Dec 30, 2025
9d79264
Revert compiled-dev change
edmcman Dec 31, 2025
0b65580
Document the new tests
edmcman Dec 31, 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
34 changes: 34 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-18-bookworm",
"features": {
"ghcr.io/devcontainers-extra/features/apt-packages:1": {
"clean_ppas": true,
"preserve_apt_list": true,
"packages": "xvfb,xauth",
"ppas": "ppa:deadsnakes/ppa"
},
"ghcr.io/devcontainers/features/git-lfs:1": {
"autoPull": true,
"version": "latest"
}
},

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install && sudo npx playwright install-deps && npx playwright install",

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
45 changes: 45 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copilot instructions (LinkHints)

## Big picture
- This is a WebExtension built from TypeScript (+ some Preact UIs). Source lives in `src/`.
- Rollup builds everything into `compiled/` (Sucrase strips TS/JSX). `tsc` is used for type-checking only.
- Build/entrypoint mapping is centralized in `project.config.ts` and consumed by `rollup.config.js`.

## Key modules
- `src/background/`: the “hub” (state machine + orchestration). See `src/background/Program.ts`.
- `src/worker/`: content script loaded in every frame; captures keypresses and finds/report elements.
- `src/renderer/`: top-frame content script; renders hints/underlines (shadow DOM container).
- `src/options/` and `src/popup/`: Preact UIs.
- `src/shared/`: shared types/helpers; message schema in `src/shared/messages.ts`.
- `docs/` → `compiled-docs/`: website sources.

## Program + messaging model (core convention)
- Each subsystem has a `Program` class in `src/*/Program.ts(x)` and a tiny bootstrap in `src/*/main.ts(x)`.
- Subsystems communicate via `browser.runtime` messages; **background is the router**.
- Message types are discriminated unions in `src/shared/messages.ts` (`ToBackground`, `FromBackground`, plus nested `ToWorker`, `FromWorker`, etc.).
- Each `Program` typically defines a local `wrapMessage(...)` helper to wrap its inner message into `ToBackground`.

## Shared utilities you should use
- Listener lifecycle + error logging: `addListener`, `addEventListener`, and `Resets` in `src/shared/main.ts`.
- Logging: `log(...)` in `src/shared/main.ts` (programs update `log.level` via StateSync).
- Build-time globals are injected by Rollup and typed in `@types/globals.d.ts` (`BROWSER`, `PROD`, `META_*`, `COLOR_*`, `DEFAULT_*`). Don’t try to “import config” for these.

## Generated outputs (don’t edit)
- Do not edit `compiled/`, `compiled-docs/`, or `dist-*`.
- Template generators called by Rollup:
- `src/manifest.ts` → `compiled/manifest.json`
- `src/html.tsx` → minimal HTML shells in `compiled/`
- `src/icons.tsx` (+ `src/icons/`) → icons; update PNGs via `npm run png-icons`
- `src/css.ts` → injects colors from `project.config.ts` into CSS

## Developer workflows (exact commands)
- Install: `npm ci`
- Type-check/lint/format check/build: `npm test`
- Build once (writes `compiled/`): `npm run compile`
- Watch build: `npm run watch`
- Run extension (auto-reloads on `compiled/` changes): `npm run firefox` / `npm run chrome`
- Shortcut to run watch + both browsers: `npm start`

## Change guidance (repo-specific)
- If you add/change a cross-component action, update `src/shared/messages.ts` and both sender/receiver `Program.ts` switch handling.
- Keep message payloads JSON-serializable; prefer discriminated unions over ad-hoc objects.
44 changes: 41 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: CI

on:
push:
branches:
- "main"
pull_request:

permissions:
Expand Down Expand Up @@ -47,11 +45,51 @@ jobs:

- name: Upload artifact
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v3
with:
path: compiled-docs

- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
id: deployment
uses: actions/deploy-pages@v1

test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 18.x

- name: Cache node_modules
id: cache-node_modules
uses: actions/cache@v3
with:
path: node_modules
key: node_modules-${{ hashFiles('package.json', 'package-lock.json') }}

- name: Cache Playwright browsers
uses: actions/cache@v3
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

- name: npm ci
if: steps.cache-node_modules.outputs.cache-hit != 'true'
run: npm ci

- name: Install xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb

- name: Install playwright dependencies
run: sudo npx playwright install-deps

- name: Install Playwright browsers
run: npx playwright install

- name: Run Playwright tests
run: xvfb-run -a npm run test:playwright
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
.eslintcache
custom.config.cjs
/custom.config.cjs
/node_modules/
/compiled/
/compiled*/
/compiled-docs/
/dist*/
/test-results
/.tool-versions
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
*.custom.js
/compiled/
/compiled-docs/
/test-results/
/tests/*-snapshots/
*.json
/rollup.config-*.js
107 changes: 107 additions & 0 deletions package-lock.json

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

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"build:all": "npm run build:chrome && npm run build:firefox",
"png-icons": "sucrase-node scripts/png-icons.ts",
"web-ext": "sucrase-node node_modules/.bin/web-ext",
"test:playwright": "npm run compile && playwright test --project=firefox",
"test": "run-pty --auto-exit % npm run build:all % eslint . --report-unused-disable-directives % prettier --check . % tsc"
},
"dependencies": {
Expand All @@ -23,6 +24,7 @@
"webextension-polyfill": "0.10.0"
},
"devDependencies": {
"@playwright/test": "^1.57.0",
"@rollup/plugin-commonjs": "24.0.0",
"@rollup/plugin-node-resolve": "15.0.1",
"@rollup/plugin-replace": "5.0.2",
Expand All @@ -41,6 +43,8 @@
"js-tokens": "8.0.0",
"minifycss": "1.0.1",
"optional-require": "1.1.8",
"playwright": "^1.57.0",
"playwright-webextext": "^0.0.4",
"preact-render-to-string": "5.2.6",
"prettier": "2.8.3",
"readdirp": "3.6.0",
Expand Down
22 changes: 22 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { PlaywrightTestConfig } from "@playwright/test";

const config: PlaywrightTestConfig = {
testDir: "tests",
timeout: 60_000,
expect: {
timeout: 20_000,
},
reporter: "list",
projects: [
{
name: "firefox",
use: {
browserName: "firefox",
// extensions need to be loaded in headed mode
headless: false,
},
},
],
};

export default config;
2 changes: 1 addition & 1 deletion project.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default {
prod: currentBrowser !== undefined,
browser: currentBrowser,
src: "src",
compiled: "compiled",
compiled: currentBrowser !== undefined ? "compiled" : "compiled-dev",
dist: currentBrowser === undefined ? "dist" : `dist-${currentBrowser}`,
webextIgnoreFiles: [
"icons/*.!(svg)",
Expand Down
5 changes: 4 additions & 1 deletion src/renderer/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ export default class RendererProgram {
// into this shadow root, which is a small optimization. (The override of
// `.attachShadow` in injected.ts does not apply to code running in the
// extension context, only in the page context).
const shadowRoot = container.attachShadow({ mode: "closed" });
// In development, use "open" for testing accessibility via playwright.
const shadowRoot = container.attachShadow({
mode: PROD ? "closed" : "open",
});

const root = document.createElement("div");
root.className = ROOT_CLASS;
Expand Down
Loading