@rune-kit/rune 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -40
- package/compiler/__tests__/pack-split.test.js +145 -0
- package/compiler/bin/rune.js +26 -9
- package/compiler/doctor.js +42 -0
- package/compiler/emitter.js +27 -4
- package/compiler/parser.js +41 -3
- package/compiler/transformer.js +10 -6
- package/compiler/transforms/compliance.js +40 -0
- package/docs/ANTIGRAVITY-GAP-ANALYSIS.md +369 -0
- package/docs/ARCHITECTURE.md +332 -0
- package/docs/COMMUNITY-PACKS.md +109 -0
- package/docs/CONTRIBUTING-L4.md +215 -0
- package/docs/CROSS-IDE-ANALYSIS.md +164 -0
- package/docs/EXTENSION-TEMPLATE.md +108 -0
- package/docs/MESH-RULES.md +34 -0
- package/docs/MULTI-PLATFORM.md +804 -0
- package/docs/SKILL-DEPTH-AUDIT.md +191 -0
- package/docs/SKILL-TEMPLATE.md +72 -0
- package/docs/TRADE-MATRIX.md +327 -0
- package/docs/VERSIONING.md +91 -0
- package/docs/VISION.md +263 -0
- package/docs/assets/demo-subtitles.srt +215 -0
- package/docs/assets/end-card.html +276 -0
- package/docs/assets/mesh-diagram.html +654 -0
- package/docs/assets/thumbnail.html +295 -0
- package/docs/guides/cli.md +403 -0
- package/docs/guides/index.html +1346 -0
- package/docs/index.html +674 -0
- package/docs/references/claudekit-analysis.md +414 -0
- package/docs/references/voltagent-analysis.md +189 -0
- package/docs/script.js +277 -0
- package/docs/skills/index.html +832 -0
- package/docs/style.css +583 -0
- package/docs/video-demo-plan.md +172 -0
- package/extensions/ai-ml/PACK.md +38 -474
- package/extensions/ai-ml/skills/ai-agents.md +172 -0
- package/extensions/ai-ml/skills/code-sandbox.md +187 -0
- package/extensions/ai-ml/skills/deep-research.md +146 -0
- package/extensions/ai-ml/skills/embedding-search.md +66 -0
- package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -0
- package/extensions/ai-ml/skills/llm-architect.md +125 -0
- package/extensions/ai-ml/skills/llm-integration.md +64 -0
- package/extensions/ai-ml/skills/prompt-patterns.md +72 -0
- package/extensions/ai-ml/skills/rag-patterns.md +66 -0
- package/extensions/ai-ml/skills/web-extraction.md +114 -0
- package/extensions/analytics/PACK.md +19 -484
- package/extensions/analytics/skills/ab-testing.md +72 -0
- package/extensions/analytics/skills/dashboard-patterns.md +83 -0
- package/extensions/analytics/skills/data-validation.md +68 -0
- package/extensions/analytics/skills/funnel-analysis.md +81 -0
- package/extensions/analytics/skills/sql-patterns.md +57 -0
- package/extensions/analytics/skills/statistical-analysis.md +79 -0
- package/extensions/analytics/skills/tracking-setup.md +71 -0
- package/extensions/backend/PACK.md +44 -618
- package/extensions/backend/skills/api-patterns.md +84 -0
- package/extensions/backend/skills/async-pipeline.md +193 -0
- package/extensions/backend/skills/auth-patterns.md +97 -0
- package/extensions/backend/skills/background-jobs.md +133 -0
- package/extensions/backend/skills/caching-patterns.md +108 -0
- package/extensions/backend/skills/cli-generation.md +133 -0
- package/extensions/backend/skills/database-patterns.md +87 -0
- package/extensions/backend/skills/middleware-patterns.md +104 -0
- package/extensions/chrome-ext/PACK.md +19 -921
- package/extensions/chrome-ext/skills/cws-preflight.md +143 -0
- package/extensions/chrome-ext/skills/cws-publish.md +104 -0
- package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -0
- package/extensions/chrome-ext/skills/ext-messaging.md +139 -0
- package/extensions/chrome-ext/skills/ext-storage.md +133 -0
- package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -0
- package/extensions/content/PACK.md +43 -335
- package/extensions/content/skills/blog-patterns.md +88 -0
- package/extensions/content/skills/cms-integration.md +131 -0
- package/extensions/content/skills/content-scoring.md +107 -0
- package/extensions/content/skills/i18n.md +83 -0
- package/extensions/content/skills/mdx-authoring.md +137 -0
- package/extensions/content/skills/reference.md +1014 -0
- package/extensions/content/skills/seo-patterns.md +67 -0
- package/extensions/content/skills/video-repurpose.md +153 -0
- package/extensions/devops/PACK.md +38 -457
- package/extensions/devops/skills/chaos-testing.md +67 -0
- package/extensions/devops/skills/ci-cd.md +75 -0
- package/extensions/devops/skills/docker.md +58 -0
- package/extensions/devops/skills/edge-serverless.md +163 -0
- package/extensions/devops/skills/infra-as-code.md +158 -0
- package/extensions/devops/skills/kubernetes.md +110 -0
- package/extensions/devops/skills/monitoring.md +57 -0
- package/extensions/devops/skills/server-setup.md +64 -0
- package/extensions/devops/skills/ssl-domain.md +42 -0
- package/extensions/ecommerce/PACK.md +62 -226
- package/extensions/ecommerce/skills/cart-system.md +79 -0
- package/extensions/ecommerce/skills/inventory-mgmt.md +102 -0
- package/extensions/ecommerce/skills/order-management.md +126 -0
- package/extensions/ecommerce/skills/payment-integration.md +472 -0
- package/extensions/ecommerce/skills/shopify-dev.md +69 -0
- package/extensions/ecommerce/skills/subscription-billing.md +93 -0
- package/extensions/ecommerce/skills/tax-compliance.md +117 -0
- package/extensions/gamedev/PACK.md +66 -317
- package/extensions/gamedev/skills/asset-pipeline.md +74 -0
- package/extensions/gamedev/skills/audio-system.md +129 -0
- package/extensions/gamedev/skills/camera-system.md +87 -0
- package/extensions/gamedev/skills/ecs.md +98 -0
- package/extensions/gamedev/skills/game-loops.md +72 -0
- package/extensions/gamedev/skills/input-system.md +199 -0
- package/extensions/gamedev/skills/multiplayer.md +180 -0
- package/extensions/gamedev/skills/particles.md +105 -0
- package/extensions/gamedev/skills/physics-engine.md +89 -0
- package/extensions/gamedev/skills/scene-management.md +146 -0
- package/extensions/gamedev/skills/threejs-patterns.md +90 -0
- package/extensions/gamedev/skills/webgl.md +71 -0
- package/extensions/mobile/PACK.md +56 -223
- package/extensions/mobile/skills/app-store-connect.md +152 -0
- package/extensions/mobile/skills/app-store-prep.md +66 -0
- package/extensions/mobile/skills/deep-linking.md +109 -0
- package/extensions/mobile/skills/flutter.md +60 -0
- package/extensions/mobile/skills/ios-build-pipeline.md +142 -0
- package/extensions/mobile/skills/native-bridge.md +66 -0
- package/extensions/mobile/skills/ota-updates.md +97 -0
- package/extensions/mobile/skills/push-notifications.md +111 -0
- package/extensions/mobile/skills/react-native.md +82 -0
- package/extensions/saas/PACK.md +26 -720
- package/extensions/saas/skills/billing-integration.md +121 -0
- package/extensions/saas/skills/feature-flags.md +130 -0
- package/extensions/saas/skills/multi-tenant.md +103 -0
- package/extensions/saas/skills/onboarding-flow.md +139 -0
- package/extensions/saas/skills/subscription-flow.md +95 -0
- package/extensions/saas/skills/team-management.md +144 -0
- package/extensions/security/PACK.md +10 -448
- package/extensions/security/skills/api-security.md +140 -0
- package/extensions/security/skills/compliance.md +68 -0
- package/extensions/security/skills/owasp-audit.md +64 -0
- package/extensions/security/skills/pentest-patterns.md +77 -0
- package/extensions/security/skills/secret-mgmt.md +65 -0
- package/extensions/security/skills/supply-chain.md +65 -0
- package/extensions/trading/PACK.md +18 -535
- package/extensions/trading/skills/chart-components.md +55 -0
- package/extensions/trading/skills/experiment-loop.md +125 -0
- package/extensions/trading/skills/fintech-patterns.md +47 -0
- package/extensions/trading/skills/indicator-library.md +58 -0
- package/extensions/trading/skills/quant-analysis.md +111 -0
- package/extensions/trading/skills/realtime-data.md +58 -0
- package/extensions/trading/skills/trade-logic.md +104 -0
- package/extensions/ui/PACK.md +36 -853
- package/extensions/ui/skills/a11y-audit.md +91 -0
- package/extensions/ui/skills/animation-patterns.md +106 -0
- package/extensions/ui/skills/component-patterns.md +75 -0
- package/extensions/ui/skills/design-decision.md +108 -0
- package/extensions/ui/skills/design-system.md +68 -0
- package/extensions/ui/skills/landing-patterns.md +155 -0
- package/extensions/ui/skills/palette-picker.md +173 -0
- package/extensions/ui/skills/react-health.md +90 -0
- package/extensions/ui/skills/type-system.md +125 -0
- package/extensions/ui/skills/web-vitals.md +153 -0
- package/extensions/zalo/PACK.md +117 -0
- package/extensions/zalo/skills/zalo-oa-mcp.md +317 -0
- package/extensions/zalo/skills/zalo-oa-messaging.md +429 -0
- package/extensions/zalo/skills/zalo-oa-setup.md +236 -0
- package/extensions/zalo/skills/zalo-oa-webhook.md +189 -0
- package/extensions/zalo/skills/zalo-personal-messaging.md +194 -0
- package/extensions/zalo/skills/zalo-personal-setup.md +153 -0
- package/extensions/zalo/skills/zalo-rate-guard.md +219 -0
- package/hooks/.gitkeep +0 -0
- package/hooks/auto-format/index.cjs +48 -0
- package/hooks/context-watch/index.cjs +68 -0
- package/hooks/hooks.json +99 -0
- package/hooks/metrics-collector/index.cjs +42 -0
- package/hooks/post-session-reflect/index.cjs +153 -0
- package/hooks/pre-compact/index.cjs +95 -0
- package/hooks/pre-tool-guard/index.cjs +68 -0
- package/hooks/run-hook +17 -0
- package/hooks/run-hook.cjs +16 -0
- package/hooks/run-hook.cmd +1 -0
- package/hooks/secrets-scan/index.cjs +100 -0
- package/hooks/session-start/index.cjs +65 -0
- package/hooks/typecheck/index.cjs +65 -0
- package/package.json +9 -4
- package/references/ui-pro-max-data/LICENSE-UI-PRO-MAX +21 -0
- package/references/ui-pro-max-data/charts.csv +26 -0
- package/references/ui-pro-max-data/colors.csv +162 -0
- package/references/ui-pro-max-data/styles.csv +85 -0
- package/references/ui-pro-max-data/typography.csv +74 -0
- package/references/ui-pro-max-data/ui-reasoning.csv +162 -0
- package/references/ui-pro-max-data/ux-guidelines.csv +100 -0
- package/skills/ba/SKILL.md +10 -0
- package/skills/brainstorm/SKILL.md +63 -1
- package/skills/completion-gate/SKILL.md +34 -1
- package/skills/context-engine/SKILL.md +13 -0
- package/skills/cook/SKILL.md +155 -5
- package/skills/debug/SKILL.md +56 -1
- package/skills/design/SKILL.md +11 -0
- package/skills/fix/SKILL.md +26 -1
- package/skills/mcp-builder/SKILL.md +48 -1
- package/skills/plan/SKILL.md +23 -6
- package/skills/review/SKILL.md +44 -5
- package/skills/review-intake/SKILL.md +17 -1
- package/skills/skill-forge/SKILL.md +38 -3
- package/skills/skill-router/SKILL.md +89 -7
- package/skills/team/SKILL.md +24 -1
- package/skills/test/SKILL.md +28 -1
- package/skills/verification/SKILL.md +98 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "ext-storage"
|
|
3
|
+
pack: "@rune/chrome-ext"
|
|
4
|
+
description: "Typed Chrome storage patterns — choose the right storage tier, define schema, implement typed helpers, handle schema migrations, and monitor quota."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ext-storage
|
|
10
|
+
|
|
11
|
+
Typed Chrome storage patterns — choose the right storage tier, define schema, implement typed helpers, handle schema migrations, and monitor quota. Prevents the #3 MV3 failure: storing state in service worker JS variables that reset on termination.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Choose storage type**
|
|
16
|
+
| Type | Capacity | Persistence | Sync | Use For |
|
|
17
|
+
|------|----------|-------------|------|---------|
|
|
18
|
+
| `chrome.storage.local` | 10 MB | Until uninstall | No | User data, large payloads, cached content |
|
|
19
|
+
| `chrome.storage.sync` | 100 KB / 8 KB per item | Cross-device | Yes | Settings, small preferences |
|
|
20
|
+
| `chrome.storage.session` | 10 MB | Until browser closes | No | Ephemeral state that service worker needs across terminations |
|
|
21
|
+
| `chrome.storage.managed` | Read-only | Admin-controlled | No | Enterprise policy |
|
|
22
|
+
|
|
23
|
+
CRITICAL: `chrome.storage.session` is the correct replacement for service worker JS variables. If you need state to survive a 30-second termination but clear on browser close, use session storage.
|
|
24
|
+
|
|
25
|
+
**Step 2 — Define TypeScript storage schema**
|
|
26
|
+
Create `src/types/storage.ts` with versioned schema interface. Include a `version` field for migration tracking.
|
|
27
|
+
|
|
28
|
+
**Step 3 — Implement typed get/set helpers**
|
|
29
|
+
Create `src/lib/storage.ts` with typed wrappers that preserve the schema type. Avoid `chrome.storage.*.get(null)` which returns `any` — always specify keys.
|
|
30
|
+
|
|
31
|
+
**Step 4 — Add migration logic**
|
|
32
|
+
On `chrome.runtime.onInstalled` with `reason === 'update'`, check stored schema version and run incremental migrations. Each migration transforms data from version N to N+1.
|
|
33
|
+
|
|
34
|
+
**Step 5 — Implement quota monitoring**
|
|
35
|
+
Chrome storage has hard limits that throw `QUOTA_BYTES_PER_ITEM` and `QUOTA_BYTES` errors on write. Wrap all writes with error handling and warn the user or prune old data when approaching 80% capacity.
|
|
36
|
+
|
|
37
|
+
#### Example
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// src/types/storage.ts — versioned storage schema
|
|
41
|
+
export const STORAGE_VERSION = 2;
|
|
42
|
+
|
|
43
|
+
export interface StorageSchema {
|
|
44
|
+
version: number;
|
|
45
|
+
settings: {
|
|
46
|
+
useBuiltinAI: boolean;
|
|
47
|
+
externalApiKey: string;
|
|
48
|
+
maxLength: number;
|
|
49
|
+
theme: 'light' | 'dark' | 'system';
|
|
50
|
+
};
|
|
51
|
+
cache: {
|
|
52
|
+
lastSummary: string;
|
|
53
|
+
lastUrl: string;
|
|
54
|
+
timestamp: number;
|
|
55
|
+
} | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const STORAGE_DEFAULTS: StorageSchema = {
|
|
59
|
+
version: STORAGE_VERSION,
|
|
60
|
+
settings: {
|
|
61
|
+
useBuiltinAI: true,
|
|
62
|
+
externalApiKey: '',
|
|
63
|
+
maxLength: 500,
|
|
64
|
+
theme: 'system',
|
|
65
|
+
},
|
|
66
|
+
cache: null,
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// src/lib/storage.ts — typed get/set helpers with quota monitoring
|
|
72
|
+
|
|
73
|
+
import type { StorageSchema } from '../types/storage';
|
|
74
|
+
import { STORAGE_DEFAULTS, STORAGE_VERSION } from '../types/storage';
|
|
75
|
+
|
|
76
|
+
type StorageKey = keyof StorageSchema;
|
|
77
|
+
|
|
78
|
+
export async function storageGet<K extends StorageKey>(
|
|
79
|
+
key: K
|
|
80
|
+
): Promise<StorageSchema[K]> {
|
|
81
|
+
const result = await chrome.storage.local.get(key);
|
|
82
|
+
return (result[key] as StorageSchema[K]) ?? STORAGE_DEFAULTS[key];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function storageSet<K extends StorageKey>(
|
|
86
|
+
key: K,
|
|
87
|
+
value: StorageSchema[K]
|
|
88
|
+
): Promise<void> {
|
|
89
|
+
try {
|
|
90
|
+
await chrome.storage.local.set({ [key]: value });
|
|
91
|
+
} catch (err) {
|
|
92
|
+
const error = err as Error;
|
|
93
|
+
if (error.message.includes('QUOTA_BYTES')) {
|
|
94
|
+
console.warn('[Storage] Quota exceeded — clearing cache');
|
|
95
|
+
await chrome.storage.local.remove('cache');
|
|
96
|
+
// retry once after clearing cache
|
|
97
|
+
await chrome.storage.local.set({ [key]: value });
|
|
98
|
+
} else {
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Quota monitoring — warn at 80% capacity
|
|
105
|
+
export async function checkStorageQuota(): Promise<void> {
|
|
106
|
+
const bytesUsed = await chrome.storage.local.getBytesInUse(null);
|
|
107
|
+
const quota = chrome.storage.local.QUOTA_BYTES; // 10 MB = 10,485,760 bytes
|
|
108
|
+
const pct = (bytesUsed / quota) * 100;
|
|
109
|
+
if (pct > 80) {
|
|
110
|
+
console.warn(`[Storage] ${pct.toFixed(1)}% of local storage used (${bytesUsed} / ${quota} bytes)`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Migration runner — call on onInstalled with reason='update'
|
|
115
|
+
export async function runMigrations(): Promise<void> {
|
|
116
|
+
const stored = await chrome.storage.local.get('version');
|
|
117
|
+
const currentVersion = (stored['version'] as number | undefined) ?? 1;
|
|
118
|
+
|
|
119
|
+
if (currentVersion < 2) {
|
|
120
|
+
// v1 → v2: renamed 'apiKey' to 'externalApiKey'
|
|
121
|
+
const legacy = await chrome.storage.local.get('settings');
|
|
122
|
+
const legacySettings = legacy['settings'] as Record<string, unknown> | undefined;
|
|
123
|
+
if (legacySettings?.['apiKey']) {
|
|
124
|
+
await chrome.storage.local.set({
|
|
125
|
+
settings: { ...legacySettings, externalApiKey: legacySettings['apiKey'], apiKey: undefined },
|
|
126
|
+
version: 2,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await chrome.storage.local.set({ version: STORAGE_VERSION });
|
|
132
|
+
}
|
|
133
|
+
```
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "mv3-scaffold"
|
|
3
|
+
pack: "@rune/chrome-ext"
|
|
4
|
+
description: "Manifest V3 project scaffolding — detect extension type, generate minimal-permission manifest, scaffold service worker with correct lifecycle patterns, scaffold content script, and generate build config."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# mv3-scaffold
|
|
10
|
+
|
|
11
|
+
Manifest V3 project scaffolding — detect extension type, generate minimal-permission manifest, scaffold service worker with correct lifecycle patterns, scaffold content script, and generate build config. Prevents the #1 MV3 mistake: carrying MV2 mental models (background pages, remote scripts, setTimeout for keepalive) into an MV3 project.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Detect or clarify extension type**
|
|
16
|
+
Use `Read` on any existing `manifest.json` or project description to classify the extension type:
|
|
17
|
+
- **popup**: user-triggered UI (toolbar button → popup.html)
|
|
18
|
+
- **sidebar**: persistent panel (chrome.sidePanel API, Chrome 114+)
|
|
19
|
+
- **content-injector**: modifies host pages (content scripts + optional popup)
|
|
20
|
+
- **background-only**: no visible UI, reacts to events (alarms, network, tabs)
|
|
21
|
+
- **devtools**: extends Chrome DevTools panel
|
|
22
|
+
|
|
23
|
+
If undetectable from files, ask the user. Extension type determines which APIs, permissions, and scaffold components are generated.
|
|
24
|
+
|
|
25
|
+
**Step 2 — Generate minimal-permission manifest.json**
|
|
26
|
+
Emit `manifest.json` with only the permissions required for the detected type. Flag over-permissioning immediately — requesting `<all_urls>` when only `activeTab` is needed is the #1 CWS rejection cause.
|
|
27
|
+
|
|
28
|
+
Key MV3 manifest rules:
|
|
29
|
+
- `"manifest_version": 3` — mandatory, MV2 deprecated Jan 2023
|
|
30
|
+
- `"background"` uses `{ "service_worker": "background.js" }` — NOT `"scripts"` array
|
|
31
|
+
- `"action"` replaces `"browser_action"` and `"page_action"`
|
|
32
|
+
- No `"content_security_policy"` that relaxes `script-src` (blocks CWS review)
|
|
33
|
+
- No `"web_accessible_resources"` with `matches: ["<all_urls>"]` unless justified
|
|
34
|
+
- External URLs in `"host_permissions"` require justification in CWS dashboard
|
|
35
|
+
|
|
36
|
+
**Step 3 — Scaffold service worker (CRITICAL lifecycle patterns)**
|
|
37
|
+
Generate `background.ts` / `background.js` with the following non-negotiable patterns:
|
|
38
|
+
|
|
39
|
+
CRITICAL: service workers terminate after 30 seconds of idle. Every assumption that breaks because of this:
|
|
40
|
+
- JS variables reset on termination — use `chrome.storage.session` for ephemeral state
|
|
41
|
+
- `setTimeout` / `setInterval` — NOT reliable across terminations, use `chrome.alarms`
|
|
42
|
+
- Pending async operations mid-flight get killed — use alarm + storage to resume
|
|
43
|
+
- `fetch()` initiated in a response to a non-event call may not complete
|
|
44
|
+
|
|
45
|
+
All event listeners MUST be registered at the top level synchronously — NOT inside `async` functions, Promises, or conditionals. Chrome only registers listeners present during the initial synchronous execution of the service worker.
|
|
46
|
+
|
|
47
|
+
**Step 4 — Scaffold content script**
|
|
48
|
+
Generate `content.ts` with correct isolation model:
|
|
49
|
+
- Runs in an **isolated world** — own JS context, cannot access page's JS variables
|
|
50
|
+
- Has access to the DOM but NOT to `chrome.storage`, `chrome.tabs`, most `chrome.*` APIs (exceptions: `chrome.runtime`, `chrome.storage`, `chrome.i18n`)
|
|
51
|
+
- Must message the service worker for privileged operations
|
|
52
|
+
- Inject only when needed — prefer `"run_at": "document_idle"` over `"document_start"`
|
|
53
|
+
|
|
54
|
+
**Step 5 — Scaffold popup/sidebar UI**
|
|
55
|
+
For popup and sidebar types, generate `popup.html` + `popup.ts`:
|
|
56
|
+
- Popup HTML MUST NOT load remote scripts (`<script src="https://...">`) — blocked by CSP
|
|
57
|
+
- All scripts must be local and listed in `web_accessible_resources` if loaded from content scripts
|
|
58
|
+
- Popup closes when user clicks away — don't depend on popup state for background operations
|
|
59
|
+
- For sidebar: register `chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })`
|
|
60
|
+
|
|
61
|
+
**Step 6 — Generate build config**
|
|
62
|
+
Emit a build configuration based on detected tooling:
|
|
63
|
+
- If `vite` in `package.json` → emit `vite.config.ts` using `@crxjs/vite-plugin` (hot-reload for extension dev)
|
|
64
|
+
- Otherwise → emit vanilla TypeScript config with `tsc` + file copy script
|
|
65
|
+
- Include `web-ext` config for local loading and reload
|
|
66
|
+
|
|
67
|
+
#### Example
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
// manifest.json — content-injector type, minimal permissions
|
|
71
|
+
{
|
|
72
|
+
"manifest_version": 3,
|
|
73
|
+
"name": "Page Summarizer",
|
|
74
|
+
"version": "1.0.0",
|
|
75
|
+
"description": "Summarize any page using built-in AI or an external API.",
|
|
76
|
+
"permissions": ["activeTab", "storage", "sidePanel"],
|
|
77
|
+
"host_permissions": [],
|
|
78
|
+
"background": {
|
|
79
|
+
"service_worker": "background.js",
|
|
80
|
+
"type": "module"
|
|
81
|
+
},
|
|
82
|
+
"content_scripts": [
|
|
83
|
+
{
|
|
84
|
+
"matches": ["<all_urls>"],
|
|
85
|
+
"js": ["content.js"],
|
|
86
|
+
"run_at": "document_idle"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"action": {
|
|
90
|
+
"default_title": "Summarize this page",
|
|
91
|
+
"default_icon": { "128": "icons/icon128.png" }
|
|
92
|
+
},
|
|
93
|
+
"side_panel": {
|
|
94
|
+
"default_path": "sidebar.html"
|
|
95
|
+
},
|
|
96
|
+
"icons": { "128": "icons/icon128.png" },
|
|
97
|
+
"content_security_policy": {
|
|
98
|
+
"extension_pages": "script-src 'self'; object-src 'self'"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// background.ts — correct MV3 service worker patterns
|
|
105
|
+
// CRITICAL: all listeners registered synchronously at top level
|
|
106
|
+
|
|
107
|
+
chrome.runtime.onInstalled.addListener(({ reason }) => {
|
|
108
|
+
if (reason === 'install') {
|
|
109
|
+
console.log('[SW] Extension installed');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Use chrome.alarms — NOT setTimeout (alarms survive service worker termination)
|
|
114
|
+
chrome.runtime.onInstalled.addListener(() => {
|
|
115
|
+
chrome.alarms.create('heartbeat', { periodInMinutes: 1 });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
119
|
+
if (alarm.name === 'heartbeat') {
|
|
120
|
+
// periodic work here — service worker woke up for this
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Message handler — registered synchronously, NOT inside async function
|
|
125
|
+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
126
|
+
if (message.type === 'SUMMARIZE_PAGE') {
|
|
127
|
+
// Return true to keep the message channel open for async response
|
|
128
|
+
handleSummarize(message.payload).then(sendResponse);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
async function handleSummarize(payload: { text: string }): Promise<{ summary: string }> {
|
|
134
|
+
// Service worker is alive for the duration of this message handler
|
|
135
|
+
const summary = await callExternalApi(payload.text);
|
|
136
|
+
return { summary };
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// content.ts — isolated world, limited chrome.* access
|
|
142
|
+
const selectedText = window.getSelection()?.toString() ?? '';
|
|
143
|
+
|
|
144
|
+
if (selectedText.length > 0) {
|
|
145
|
+
// Content scripts can message service worker
|
|
146
|
+
chrome.runtime.sendMessage(
|
|
147
|
+
{ type: 'SUMMARIZE_PAGE', payload: { text: selectedText } },
|
|
148
|
+
(response: { summary: string }) => {
|
|
149
|
+
if (chrome.runtime.lastError) {
|
|
150
|
+
console.error('[Content] Message failed:', chrome.runtime.lastError.message);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
displaySummary(response.summary);
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function displaySummary(summary: string): void {
|
|
159
|
+
const panel = document.createElement('div');
|
|
160
|
+
panel.id = 'rune-summarizer-panel';
|
|
161
|
+
panel.textContent = summary;
|
|
162
|
+
document.body.appendChild(panel);
|
|
163
|
+
}
|
|
164
|
+
```
|