initial commit

This commit is contained in:
2026-05-30 18:51:10 +00:00
commit 95f5fdd3ea
19 changed files with 3050 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
FROM mcr.microsoft.com/devcontainers/javascript-node:22
# Install Chrome (stable)
RUN wget -q -O /tmp/chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
&& apt-get update && apt-get install -y /tmp/chrome.deb \
&& rm /tmp/chrome.deb \
&& apt-get clean
# Install Chrome DevTools CLI for headless testing
RUN npm install -g chrome-devtools-cli
WORKDIR /workspace
+9
View File
@@ -0,0 +1,9 @@
{
"features": {
"ghcr.io/devcontainers/features/sshd:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers/features/sshd@sha256:f5251b8e4325f68f7280973c6cd65daff414449c66f240621502d4e8e74eb7ee",
"integrity": "sha256:f5251b8e4325f68f7280973c6cd65daff414449c66f240621502d4e8e74eb7ee"
}
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "Goon Plugin Dev",
"image": "mcr.microsoft.com/devcontainers/javascript-node:22",
"features": {
"ghcr.io/devcontainers/features/sshd:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"fmvilas.popup-previewer.ChromeExtensionPopupPreviewer",
"maslansnik.chromium-ext-debugger",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bierner.markdown-preview-github-styles"
]
}
},
"forwardPorts": [5173],
"postCreateCommand": "npm install",
"remoteEnv": {
"CHROME_PATH": "/usr/bin/google-chrome"
}
}
+13
View File
@@ -0,0 +1,13 @@
# Optional: use docker-compose if you need a custom Chrome setup.
# With podman, substitute `docker-compose` → `podman-compose`.
version: "3.8"
services:
dev:
build:
context: .
dockerfile: Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
environment:
- CHROME_PATH=/usr/bin/google-chrome
+3
View File
@@ -0,0 +1,3 @@
node_modules/
dist/
.vscode-insiders/
+45
View File
@@ -0,0 +1,45 @@
# Goon Plugin
Chrome extension scaffold (Manifest V3).
## Getting started
```bash
# 1. Install deps
npm i
# 2. Run in watch mode
npm run dev
# 3. Build for distribution
npm run build
```
## Loading in Chrome
1. Open `chrome://extensions/`
2. Enable **Developer mode** (top-right)
3. Click **Load unpacked** → select the `dist/` folder
## Project structure
```
manifest.json — Extension manifest (MV3)
package.json — Dev tooling (Vite + Vitest + TS)
vite.config.ts — Build config
src/
background/ — Service worker (MV3 background)
popup/ — Popup UI (HTML + CSS + JS)
content/ — Content script
icons/ — Extension icons (place .png files here)
```
## Recommended extensions (VS Code)
- **Chrome Extension Developer** (extension-list-viewer)
- **ESLint**
- **Prettier**
## VS Code workspace
Open `goon_plugin.code-workspace` to get the project with recommended extensions pre-installed.
+23
View File
@@ -0,0 +1,23 @@
{
"folders": [
{
"path": "."
}
],
"extensions": {
"recommendations": [
"fmvilas.popup-previewer.ChromeExtensionPopupPreviewer",
"maslansnik.chromium-ext-debugger",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bierner.markdown-preview-github-styles"
]
},
"settings": {
"extensions.autoUpdate": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": ["javascript", "typescript"],
"typescript.tsdk": "node_modules/typescript/lib"
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"manifest_version": 3,
"name": "Goon Plugin",
"version": "0.1.0",
"description": "A Chrome extension",
"permissions": [
"storage",
"tabs",
"activeTab"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "src/background/background.js",
"type": "module"
},
"action": {
"default_popup": "src/popup/popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["src/content/content.js"],
"run_at": "document_idle"
}
],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
+2658
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
{
"name": "goon-plugin",
"version": "0.1.0",
"private": true,
"description": "Chrome extension scaffold",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "vitest run",
"test:watch": "vitest",
"check": "tsc --noEmit"
},
"devDependencies": {
"chrome-types": "^0.1.336",
"eslint": "^9.0.0",
"vite": "^6.0.0",
"vitest": "^3.0.0",
"typescript": "^5.7.0"
}
}
+32
View File
@@ -0,0 +1,32 @@
/**
* background.js — Service worker for the Goon Plugin extension.
* Handles long-lived state, message routing, and lifecycle events.
* Uses Manifest V3 module-type service worker.
*/
// Fired when the extension is first installed / updated / Chrome starts.
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
console.log('[goon-plugin] Extension installed');
} else if (details.reason === 'update') {
console.log(`[goon-plugin] Extension updated from ${details.previousVersion}`);
}
});
// Listen for messages from popup or content scripts.
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
console.log('[goon-plugin] Received:', message);
switch (message.type) {
case 'PING':
sendResponse({ pong: true, timestamp: Date.now() });
break;
default:
console.warn('[goon-plugin] Unknown message type:', message.type);
sendResponse({ error: `Unknown type: ${message.type}` });
break;
}
return true; // keep sendResponse channel open for async work
});
+19
View File
@@ -0,0 +1,19 @@
/**
* content.js — Content script injected into matching pages.
* Communicates with the background service worker via chrome.runtime.
* Use cautiously: runs in the page's DOM context, so isolate from page globals.
*/
(function () {
'use strict';
console.log('[goon-plugin] Content script loaded');
// Example: inject a small element into the page.
// Uncomment and customize as needed.
//
// const badge = document.createElement('div');
// badge.textContent = 'Goon Plugin Active';
// badge.style.cssText = 'position:fixed;bottom:8px;right:8px;padding:4px 8px;...';
// document.body.appendChild(badge);
})();
+15
View File
@@ -0,0 +1,15 @@
# Icons
Place your extension icons here.
- `icon16.png` — 16x16 px, PNG
- `icon48.png` — 48x48 px, PNG
- `icon128.png` — 128x128 px, PNG
Tip: generate them from a single SVG source using [icon-generator](https://chrome.dev/tools/icon-generator/) or the `resvg` CLI:
```bash
npx @resvg/resvg-js icon.svg -w 16 -h 16 -o icon16.png
npx @resvg/resvg-js icon.svg -w 48 -h 48 -o icon48.png
npx @resvg/resvg-js icon.svg -w 128 -h 128 -o icon128.png
```
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<rect width="128" height="128" rx="24" fill="#4361ee"/>
<text x="64" y="84" text-anchor="middle" dominant-baseline="middle" font-size="64" font-weight="bold" font-family="sans-serif" fill="white">G</text>
</svg>

After

Width:  |  Height:  |  Size: 279 B

+53
View File
@@ -0,0 +1,53 @@
:root {
--bg: #ffffff;
--text: #1a1a2e;
--accent: #4361ee;
--accent-hover: #3a56d4;
--border: #e2e8f0;
--radius: 8px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg);
color: var(--text);
min-width: 320px;
padding: 16px;
}
#app {
display: flex;
flex-direction: column;
gap: 12px;
}
h1 {
font-size: 18px;
font-weight: 600;
}
#status {
font-size: 14px;
color: #64748b;
}
button {
padding: 8px 16px;
background: var(--accent);
color: white;
border: none;
border-radius: var(--radius);
font-size: 14px;
cursor: pointer;
transition: background 150ms;
}
button:hover {
background: var(--accent-hover);
}
+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Goon Plugin</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div id="app">
<h1>Goon Plugin</h1>
<p id="status">Ready</p>
<button id="action-btn">Click me</button>
</div>
<script src="popup.js" type="module"></script>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
/**
* popup.js — UI logic for the extension popup.
* Runs inside the popup iframe; uses chrome.runtime / chrome.storage APIs.
*/
const actionBtn = document.getElementById('action-btn');
const statusEl = document.getElementById('status');
actionBtn.addEventListener('click', async () => {
// TODO: replace with actual extension logic
try {
const result = await chrome.storage.local.get(['counter']);
const count = (result.counter ?? 0) + 1;
await chrome.storage.local.set({ counter: count });
statusEl.textContent = `Clicked ${count} time${count === 1 ? '' : 's'}`;
} catch (err) {
statusEl.textContent = `Error: ${err.message}`;
}
});
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022", "DOM"],
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["chrome-types"]
},
"include": ["src/**/*.ts", "vite.config.ts"],
"exclude": ["node_modules", "dist"]
}
+30
View File
@@ -0,0 +1,30 @@
/**
* Vite config for the Chrome extension.
* Builds the extension into dist/ for loading as an unpacked extension.
*
* Usage:
* npm run dev — watch mode, rebuild on change
* npm run build — production build
*/
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
root: 'src',
build: {
outDir: '../dist',
emptyOutDir: true,
rollupOptions: {
input: {
background: resolve(__dirname, 'src/background/background.js'),
content: resolve(__dirname, 'src/content/content.js'),
popup: resolve(__dirname, 'src/popup/popup.html'),
},
output: {
entryFileNames: '[name].js',
chunkFileNames: 'chunks/[name].js',
assetFileNames: '[name][extname]',
},
},
},
});