diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 79f7c3ec..e584b0b1 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -983,8 +983,8 @@ { "name": "modern-web-guidance", "source": { - "source": "url", - "url": "https://github.com/GoogleChrome/modern-web-guidance.git" + "source": "local", + "path": "./plugins/modern-web-guidance" }, "policy": { "installation": "AVAILABLE", diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4cc58d51..b7f1c670 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1369,10 +1369,7 @@ "category": "development", "keywords": ["web", "best-practices", "chrome", "baseline", "css", "performance"], "tags": ["skill", "web"], - "source": { - "source": "github", - "repo": "GoogleChrome/modern-web-guidance" - }, + "source": "./plugins/modern-web-guidance", "homepage": "https://github.com/GoogleChrome/modern-web-guidance" }, { diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index e4ef0ac8..a6a0239f 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -324,6 +324,11 @@ "source": "./plugins/genkit", "description": "Official Genkit skills - build AI-powered applications with Genkit in JavaScript/TypeScript, Go, Dart/Flutter, and Python: flows, generation, prompts, tool calling, agents, and model providers" }, + { + "name": "modern-web-guidance", + "source": "./plugins/modern-web-guidance", + "description": "Keep your coding agent up to date with the latest web best practices from the Google Chrome team" + }, { "name": "java-development", "source": "./plugins/java-development", diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ba8b9e65..d1e0441a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -20,6 +20,7 @@ "plugins/vue": "1.3.3", "plugins/vueuse": "1.3.2", "plugins/web-design": "1.4.3", + "plugins/modern-web-guidance": "0.0.196", "plugins/mastra": "1.7.4", "plugins/nuxt-ui": "1.6.5", "plugins/firebase": "1.1.3", diff --git a/README.md b/README.md index d23cf32c..ccd2cb4e 100644 --- a/README.md +++ b/README.md @@ -492,6 +492,12 @@ End-to-end typesafe APIs with oRPC v2 — the core `os` builder, routers, middle **Install:** `/plugin install orpc@pleaseai` | **Source:** [plugins/orpc](https://github.com/pleaseai/claude-code-plugins/tree/main/plugins/orpc) +#### Modern Web Guidance + +Keep your coding agent up to date with the latest web best practices from the Google Chrome team — modern HTML/CSS/JS patterns (`modern-web-guidance`) and Manifest V3 Chrome extensions (`chrome-extensions`). Vendored from [GoogleChrome/modern-web-guidance](https://github.com/GoogleChrome/modern-web-guidance) (Apache-2.0) and refreshed weekly by `update-skills.yml`. + +**Install:** `/plugin install modern-web-guidance@pleaseai` | **Source:** [plugins/modern-web-guidance](https://github.com/pleaseai/claude-code-plugins/tree/main/plugins/modern-web-guidance) + #### evlog Wide-event logging for TypeScript with [evlog](https://www.evlog.dev): review logging patterns and guide evlog adoption (`review-logging-patterns`), design tamper-aware audit trails (`build-audit-logs`), and debug from `.evlog/logs/` output (`analyze-logs`). Bundles the read-only evlog docs MCP server (`https://www.evlog.dev/mcp`) for current docs pages and log-cost estimates. diff --git a/plugins/modern-web-guidance/.agents/skills/chrome-extensions/SKILL.md b/plugins/modern-web-guidance/.agents/skills/chrome-extensions/SKILL.md new file mode 100644 index 00000000..91a915cd --- /dev/null +++ b/plugins/modern-web-guidance/.agents/skills/chrome-extensions/SKILL.md @@ -0,0 +1,530 @@ +--- +name: chrome-extensions +description: > + Build and publish Chrome Extensions using Manifest V3 best practices. Use this skill + whenever the user asks to create, modify, debug, or understand Chrome browser extensions, + add-ons, or anything involving the Chrome Extensions API. Trigger on mentions of: 'Chrome + extension', 'browser extension', 'manifest.json', 'content script', 'service worker' (in + browser context), 'popup' (in browser extension context), 'side panel', 'chrome.* API', + 'declarativeNetRequest', 'omnibox', 'context menu' (in extension context), 'userScripts', + 'user script', 'script manager', or any request to build functionality that integrates with + the Chrome browser UI. Also trigger for publishing to the Chrome Web Store: 'publish + extension', preparing an extension for publishing, responding to a review rejection, writing + permission justifications, or drafting a privacy policy. +--- + +# Chrome Extensions + +Build production-quality Chrome extensions using Manifest V3 and publish them to the Chrome Web Store. + +## Part 1 — Building Extensions + +### Mandatory Rules + +These address the most common causes of broken extensions. Violating any produces a non-functional build. + +#### 1. Icons: only reference files you create — or omit icons entirely + +``` +❌ BROKEN — referencing files that don't exist or reusing one file for all sizes: + "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" } + +✅ CORRECT — each size is a separate file at the correct pixel dimensions: + "icons": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" } + (where icon-16.png is 16×16px, icon-48.png is 48×48px, icon-128.png is 128×128px) + +✅ ALSO CORRECT — omit icons from manifest if you cannot generate real PNG files: + (just remove the "icons" and "default_icon" fields — Chrome uses a default icon) +``` + +**If you include icon references, you MUST create the actual image files.** Generate them with a script (see `references/extensions/icons.md`) or leave them out. Never reference non-existent files. + +#### 2. Side panel: you MUST provide a way to open it + +Defining `"side_panel": {"default_path": "..."}` does NOT make it openable. Add a trigger: + +```js +// In service-worker.js — open side panel on extension icon click +// IMPORTANT: chrome.action.onClicked ONLY fires when there is NO default_popup +chrome.action.onClicked.addListener(async (tab) => { + await chrome.sidePanel.open({ windowId: tab.windowId }); +}); +``` + +If the extension has both a popup AND side panel, add a button in the popup that calls `chrome.sidePanel.open()`. Alternatively, use `chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })` — but the property is `openPanelOnActionClick`, NOT `openPanelOnActionIconClick`; the "Icon" variant causes a synchronous TypeError that silently aborts the service worker. Do NOT also define `default_popup` when using `setPanelBehavior`. See `references/extensions/side-panel.md`. + +#### 3. Code execution: sandboxed iframes ONLY + +Extension CSP blocks `eval()`, `new Function()`, inline ` + + + + + + +``` + +In `popup.js`: +```js +document.getElementById('btn').addEventListener('click', () => { + // Handle click +}); +``` + +## Executing User Code (Code Playground Pattern) + +If you need to execute arbitrary code (e.g., a CodePen-like playground), you MUST use one of these +approaches. **Extension CSP completely blocks `eval()`, `new Function()`, and inline scripts in +normal extension pages.** There is no way around this — you need sandboxing. + +### Option 1: Sandboxed Page in Manifest (Recommended) + +Declare a sandboxed page in manifest.json. Sandboxed pages have a relaxed CSP that allows +`eval()` and inline scripts, but they cannot access chrome.* APIs. + +```json +{ + "sandbox": { + "pages": ["sandbox.html"] + } +} +``` + +Use an iframe in your extension page to embed the sandbox: + +```html + + +``` + +**CRITICAL:** Communication between the extension page and the sandboxed iframe MUST use +`postMessage`. You CANNOT access `iframe.contentDocument` or `iframe.contentWindow.document` +directly — this will throw: + +``` +SecurityError: Blocked a frame with origin "chrome-extension://..." from accessing a cross-origin frame. +``` + +Correct pattern: + +```js +// playground.js — send code to sandbox +const iframe = document.getElementById('preview'); +iframe.contentWindow.postMessage({ + html: htmlCode, + css: cssCode, + js: jsCode +}, '*'); + +// sandbox.js — receive and execute +window.addEventListener('message', (event) => { + const { html, css, js } = event.data; + // Clear previous content + document.body.innerHTML = ''; + document.head.querySelectorAll('style.user-style').forEach(s => s.remove()); + + // Apply HTML + const container = document.createElement('div'); + container.innerHTML = html; + document.body.appendChild(container); + + // Apply CSS + const style = document.createElement('style'); + style.className = 'user-style'; + style.textContent = css; + document.head.appendChild(style); + + // Execute JS (eval is allowed in sandbox!) + try { + eval(js); + } catch (e) { + const errEl = document.createElement('pre'); + errEl.style.color = 'red'; + errEl.textContent = e.message; + document.body.appendChild(errEl); + } +}); +``` + +### Option 2: Blob URL in iframe + +Create a self-contained HTML document via blob URL: + +```js +function updatePreview(htmlCode, cssCode, jsCode) { + const html = ` + + +
+ + ${htmlCode} + + + +``` + +`devtools/devtools.js`: +```js +chrome.devtools.panels.create( + 'My Panel', // Title shown in DevTools tab + 'icons/icon-16.png', // Icon (optional, can be empty string) + 'devtools/panel/panel.html', // Panel content page — RELATIVE TO EXTENSION ROOT + (panel) => { + // panel.onShown.addListener((window) => { ... }); + // panel.onHidden.addListener(() => { ... }); + } +); +``` + +**CRITICAL: The panel path is relative to the extension root**, NOT relative to the devtools.js +file. This is the most common DevTools extension bug. + +```js +// ❌ WRONG — resolves to