feat: simple extension setup

This commit is contained in:
2026-05-31 21:37:04 +02:00
parent d6f170ac2f
commit 98f0fd10b0
12 changed files with 522 additions and 0 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
dist/

21
manifest.json Normal file
View File

@@ -0,0 +1,21 @@
{
"manifest_version": 3,
"name": "Wolvesville Assassins Convention Tweaks",
"version": "1.0.0",
"description": "Tweaks for Wolvesville's Assassins Convention",
"action": {
"default_popup": "src/popup.html",
"default_title": "Assassins Convention Tweaks"
},
"content_scripts": [
{
"matches": [
"https://www.wolvesville.com/*",
"https://wolvesville.com/*"
],
"js": ["dist/content.js"],
"run_at": "document_idle"
}
],
"permissions": ["storage"]
}

15
package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "wov-assassins-convention-tweaks",
"version": "1.0.0",
"description": "Tweaks for WoV Assassins Convention",
"scripts": {
"build": "esbuild src/content.ts src/popup.ts --bundle --outdir=dist --platform=browser --target=chrome120",
"watch": "esbuild src/content.ts src/popup.ts --bundle --outdir=dist --platform=browser --target=chrome120 --watch",
"dev": "pnpm watch"
},
"devDependencies": {
"@types/chrome": "^0.0.268",
"esbuild": "^0.24.0",
"typescript": "^5.6.0"
}
}

313
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,313 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@types/chrome':
specifier: ^0.0.268
version: 0.0.268
esbuild:
specifier: ^0.24.0
version: 0.24.2
typescript:
specifier: ^5.6.0
version: 5.9.3
packages:
'@esbuild/aix-ppc64@0.24.2':
resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.24.2':
resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.24.2':
resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.24.2':
resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.24.2':
resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.24.2':
resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.24.2':
resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.24.2':
resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.24.2':
resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.24.2':
resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.24.2':
resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.24.2':
resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.24.2':
resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.24.2':
resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.24.2':
resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.24.2':
resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.24.2':
resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.24.2':
resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.24.2':
resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.24.2':
resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.24.2':
resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.24.2':
resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.24.2':
resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.24.2':
resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.24.2':
resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@types/chrome@0.0.268':
resolution: {integrity: sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA==}
'@types/filesystem@0.0.36':
resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
'@types/filewriter@0.0.33':
resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
'@types/har-format@1.2.16':
resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
esbuild@0.24.2:
resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
engines: {node: '>=18'}
hasBin: true
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
snapshots:
'@esbuild/aix-ppc64@0.24.2':
optional: true
'@esbuild/android-arm64@0.24.2':
optional: true
'@esbuild/android-arm@0.24.2':
optional: true
'@esbuild/android-x64@0.24.2':
optional: true
'@esbuild/darwin-arm64@0.24.2':
optional: true
'@esbuild/darwin-x64@0.24.2':
optional: true
'@esbuild/freebsd-arm64@0.24.2':
optional: true
'@esbuild/freebsd-x64@0.24.2':
optional: true
'@esbuild/linux-arm64@0.24.2':
optional: true
'@esbuild/linux-arm@0.24.2':
optional: true
'@esbuild/linux-ia32@0.24.2':
optional: true
'@esbuild/linux-loong64@0.24.2':
optional: true
'@esbuild/linux-mips64el@0.24.2':
optional: true
'@esbuild/linux-ppc64@0.24.2':
optional: true
'@esbuild/linux-riscv64@0.24.2':
optional: true
'@esbuild/linux-s390x@0.24.2':
optional: true
'@esbuild/linux-x64@0.24.2':
optional: true
'@esbuild/netbsd-arm64@0.24.2':
optional: true
'@esbuild/netbsd-x64@0.24.2':
optional: true
'@esbuild/openbsd-arm64@0.24.2':
optional: true
'@esbuild/openbsd-x64@0.24.2':
optional: true
'@esbuild/sunos-x64@0.24.2':
optional: true
'@esbuild/win32-arm64@0.24.2':
optional: true
'@esbuild/win32-ia32@0.24.2':
optional: true
'@esbuild/win32-x64@0.24.2':
optional: true
'@types/chrome@0.0.268':
dependencies:
'@types/filesystem': 0.0.36
'@types/har-format': 1.2.16
'@types/filesystem@0.0.36':
dependencies:
'@types/filewriter': 0.0.33
'@types/filewriter@0.0.33': {}
'@types/har-format@1.2.16': {}
esbuild@0.24.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.24.2
'@esbuild/android-arm': 0.24.2
'@esbuild/android-arm64': 0.24.2
'@esbuild/android-x64': 0.24.2
'@esbuild/darwin-arm64': 0.24.2
'@esbuild/darwin-x64': 0.24.2
'@esbuild/freebsd-arm64': 0.24.2
'@esbuild/freebsd-x64': 0.24.2
'@esbuild/linux-arm': 0.24.2
'@esbuild/linux-arm64': 0.24.2
'@esbuild/linux-ia32': 0.24.2
'@esbuild/linux-loong64': 0.24.2
'@esbuild/linux-mips64el': 0.24.2
'@esbuild/linux-ppc64': 0.24.2
'@esbuild/linux-riscv64': 0.24.2
'@esbuild/linux-s390x': 0.24.2
'@esbuild/linux-x64': 0.24.2
'@esbuild/netbsd-arm64': 0.24.2
'@esbuild/netbsd-x64': 0.24.2
'@esbuild/openbsd-arm64': 0.24.2
'@esbuild/openbsd-x64': 0.24.2
'@esbuild/sunos-x64': 0.24.2
'@esbuild/win32-arm64': 0.24.2
'@esbuild/win32-ia32': 0.24.2
'@esbuild/win32-x64': 0.24.2
typescript@5.9.3: {}

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
onlyBuiltDependencies:
- esbuild

46
src/content.ts Normal file
View File

@@ -0,0 +1,46 @@
import { State, STORAGE_KEY } from "./types";
const KEY_BINDINGS: Record<string, string> = {
// TODO
};
let enabled = false;
const clickButton = (selector: string): void => {
const el = document.querySelector<HTMLElement>(selector);
el?.click();
};
const onKeyDown = (event: KeyboardEvent): void => {
if (!enabled) return;
const target = event.target as HTMLElement;
if (
target.tagName === "INPUT" ||
target.tagName === "TEXTAREA" ||
target.isContentEditable
) return;
const selector = KEY_BINDINGS[event.code];
if (selector) {
event.preventDefault();
clickButton(selector);
}
};
const init = async (): Promise<void> => {
const result = await chrome.storage.local.get(STORAGE_KEY);
const state = result[STORAGE_KEY] as State | undefined;
enabled = state?.enabled ?? true;
document.addEventListener("keydown", onKeyDown);
chrome.storage.onChanged.addListener((changes) => {
if (STORAGE_KEY in changes) {
const next = changes[STORAGE_KEY].newValue as State;
enabled = next.enabled;
}
});
};
init();

49
src/popup.css Normal file
View File

@@ -0,0 +1,49 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
width: 320px;
padding: 16px;
font-family: system-ui, -apple-system, sans-serif;
background: #0f1117;
color: #e2e8f0;
}
h1 {
font-size: 0.95rem;
font-weight: 700;
letter-spacing: 0.02em;
margin-bottom: 6px;
}
p {
font-size: 0.75rem;
color: #94a3b8;
line-height: 1.4;
margin-bottom: 16px;
}
#toggle {
width: 100%;
padding: 9px;
border: none;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 700;
cursor: pointer;
letter-spacing: 0.05em;
transition: background 0.15s;
}
#toggle.on {
background: #22c55e;
color: #fff;
}
#toggle.off {
background: #3f3f46;
color: #94a3b8;
}

13
src/popup.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WoV Tweaks</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<h1>Assassins Convention Tweaks</h1>
<button id="toggle"></button>
<script src="../dist/popup.js"></script>
</body>
</html>

34
src/popup.ts Normal file
View File

@@ -0,0 +1,34 @@
import { State, STORAGE_KEY } from "./types";
const DEFAULT_STATE: State = {
enabled: true,
};
const getState = async (): Promise<State> => {
const result = await chrome.storage.local.get(STORAGE_KEY);
return (result[STORAGE_KEY] as State | undefined) ?? DEFAULT_STATE;
};
const setState = async (state: State): Promise<void> => {
await chrome.storage.local.set({ [STORAGE_KEY]: state });
};
const render = (btn: HTMLButtonElement, enabled: boolean): void => {
btn.textContent = enabled ? "ON" : "OFF";
btn.className = enabled ? "on" : "off";
};
const init = async (): Promise<void> => {
const btn = document.getElementById("toggle") as HTMLButtonElement;
const state = await getState();
render(btn, state.enabled);
btn.addEventListener("click", async () => {
const current = await getState();
const next = { enabled: !current.enabled };
await setState(next);
render(btn, next.enabled);
});
};
document.addEventListener("DOMContentLoaded", init);

5
src/types.ts Normal file
View File

@@ -0,0 +1,5 @@
export type State = {
enabled: boolean;
};
export const STORAGE_KEY = "wov_assassins_convention_tweaks_state";

13
tsconfig.json Normal file
View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"types": ["chrome"]
},
"include": ["src"]
}