Skip to content
Back to skills

Add Inject

ASecurity

Create a game UI injection that modifies existing game elements with proper cleanup

  • 666 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 2, 2026
ai-agentstypescriptapi

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned September 2, 2026

npx -y skills add majiayu000/claude-skill-registry --skill add-inject --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Add Inject?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Add Inject
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-add-inject-claude-skill-registry/badge)](https://www.skillsdirectory.com/skills/majiayu000-add-inject-claude-skill-registry)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: add-inject
description: Create a game UI injection that modifies existing game elements with proper cleanup
aliases: [new-inject, create-inject, add-qol]
---

# Add Inject Skill

## Usage
```
/add-inject <featureName>
```

**Note:** This creates a game UI injection (modifies existing game UI). For Gemini HUD UI, use `/add-component` or `/add-section`.

---

## Step 1: Ask Questions

### 1. Injection Purpose
```
What game UI element does this modify?
Brief description:
```

### 2. Target Elements
```
Which game UI elements are targeted?

Examples:
- Inventory panel
- Shop interface
- Player stats display
- Game toolbar
- Chat window
```

### 3. Modification Type
```
What modifications are made?

[ ] Add new elements (buttons, indicators, overlays)
[ ] Modify existing elements (styles, text, attributes)
[ ] Add event listeners (click handlers, observers)
[ ] Inject data displays (stats, timers, counters)
```

### 4. Game Data
```
Does it need game data?

A) Static data → MGData
B) Real-time state → Globals
C) Both
D) None
```

### 5. Sprites
```
Does it display game sprites? → MGSprite.toCanvas()
```

### 6. Persistence
```
Does it need persistent state?

A) Yes - User preferences saved
B) No - Stateless
```

---

## Step 2: Create Structure

```
src/ui/inject/qol/<featureName>/
├── index.ts        # Public API (init, destroy, isEnabled)
├── inject.ts       # DOM injection logic
├── styles.css.ts   # Scoped styles (optional)
└── state.ts        # Persistent state (optional)
```

---

## Step 3: File Templates

### index.ts

```typescript
/**
 * <FeatureName> Game UI Injection
 *
 * <Description of what it modifies>
 */

import { injectElements, removeElements } from './inject';

// ─────────────────────────────────────────────────────────────────────────────
// State
// ─────────────────────────────────────────────────────────────────────────────

let injected = false;
const cleanups: (() => void)[] = [];

// ─────────────────────────────────────────────────────────────────────────────
// Public API
// ─────────────────────────────────────────────────────────────────────────────

export const <FeatureName>Inject = {
    /**
     * Inject into game UI
     * Idempotent - safe to call multiple times
     */
    init(): void {
        if (injected) return;
        injected = true;

        const cleanup = injectElements();
        if (cleanup) cleanups.push(cleanup);

        console.log('[<FeatureName>Inject] Initialized');
    },

    /**
     * Remove all injected elements
     * Idempotent - safe to call multiple times
     */
    destroy(): void {
        if (!injected) return;

        cleanups.forEach(fn => fn());
        cleanups.length = 0;
        removeElements();

        injected = false;
        console.log('[<FeatureName>Inject] Destroyed');
    },

    /**
     * Check if injection is active
     */
    isEnabled(): boolean {
        return injected;
    },
};
```

### inject.ts

```typescript
/**
 * <FeatureName> DOM Injection Logic
 */

import { <featureName>Css } from './styles.css';
// If using sprites:
// import { MGSprite } from '../../../../modules/sprite';
// If using game data:
// import { MGData } from '../../../../modules/data';
// If using globals:
// import { getMyInventory } from '../../../../globals/variables/myInventory';

// ─────────────────────────────────────────────────────────────────────────────
// Constants
// ─────────────────────────────────────────────────────────────────────────────

const CONTAINER_SELECTOR = '.game-ui-container';  // Adjust to target element
const INJECT_CLASS = 'gemini-qol-<featureName>';

// ─────────────────────────────────────────────────────────────────────────────
// Injection
// ─────────────────────────────────────────────────────────────────────────────

export function injectElements(): (() => void) | null {
    const container = document.querySelector(CONTAINER_SELECTOR);
    if (!container) {
        console.warn('[<FeatureName>Inject] Container not found');
        return null;
    }

    const cleanups: (() => void)[] = [];

    // Inject styles
    const style = document.createElement('style');
    style.textContent = <featureName>Css;
    style.setAttribute('data-gemini', '<featureName>');
    document.head.appendChild(style);
    cleanups.push(() => style.remove());

    // Create injected element
    const wrapper = document.createElement('div');
    wrapper.className = INJECT_CLASS;
    // ... build element
    container.appendChild(wrapper);
    cleanups.push(() => wrapper.remove());

    // Add event listeners (track for cleanup!)
    const handleClick = () => { /* ... */ };
    wrapper.addEventListener('click', handleClick);
    cleanups.push(() => wrapper.removeEventListener('click', handleClick));

    // Subscribe to globals (track for cleanup!)
    // const unsub = getMyInventory().subscribe((inv) => { /* update UI */ });
    // cleanups.push(unsub);

    // Return combined cleanup
    return () => {
        cleanups.forEach(fn => fn());
    };
}

export function removeElements(): void {
    // Remove all injected elements by class
    document.querySelectorAll(`.${INJECT_CLASS}`).forEach(el => el.remove());

    // Remove injected styles
    document.querySelectorAll('style[data-gemini="<featureName>"]').forEach(el => el.remove());
}
```

### styles.css.ts

```typescript
/**
 * <FeatureName> Injection Styles
 *
 * Scoped to .gemini-qol-<featureName>
 */

export const <featureName>Css = `
    .gemini-qol-<featureName> {
        /* Use CSS variables when possible */
        position: absolute;
        z-index: 100;
    }

    .gemini-qol-<featureName>__button {
        background: var(--color-primary, #4a9eff);
        color: var(--color-text, #fff);
        border: none;
        border-radius: 4px;
        padding: 8px 12px;
        cursor: pointer;
        min-height: 44px;  /* Touch-friendly */
    }

    .gemini-qol-<featureName>__button:hover {
        opacity: 0.9;
    }
`;
```

### state.ts (if persistent)

```typescript
/**
 * <FeatureName> Injection State
 */

import { storageGet, storageSet } from '../../../../utils/storage';

const STORAGE_KEY = 'inject:<featureName>:config';

export interface <FeatureName>Config {
    enabled: boolean;
    // ... other settings
}

const DEFAULT_CONFIG: <FeatureName>Config = {
    enabled: true,
};

export function loadConfig(): <FeatureName>Config {
    return storageGet(STORAGE_KEY, DEFAULT_CONFIG);
}

export function saveConfig(config: <FeatureName>Config): void {
    storageSet(STORAGE_KEY, config);
}
```

---

## Step 4: Register (if standalone)

If this injection is standalone (not part of a feature):

### Export → `src/ui/inject/qol/index.ts`

```typescript
export { <FeatureName>Inject } from './<featureName>';
```

### Initialize in bootstrap or feature

```typescript
import { <FeatureName>Inject } from '../../ui/inject/qol/<featureName>';

// Initialize
<FeatureName>Inject.init();

// Cleanup
<FeatureName>Inject.destroy();
```

---

## Step 5: Validate

### Structure
- [ ] `index.ts` with `init()`, `destroy()`, `isEnabled()`
- [ ] `inject.ts` with `injectElements()`, `removeElements()`
- [ ] `styles.css.ts` with scoped classes
- [ ] `state.ts` (if persistent)

### Lifecycle
- [ ] `init()` is idempotent (guard against double-inject)
- [ ] `destroy()` removes ALL injected elements
- [ ] All event listeners tracked and removed
- [ ] All MutationObservers disconnected
- [ ] All intervals/timeouts cleared
- [ ] All Global subscriptions unsubscribed

### Styling
- [ ] Classes prefixed with `gemini-qol-<name>`
- [ ] Uses CSS variables where possible
- [ ] Touch-friendly (44px min targets)
- [ ] Styles removed on destroy

### Game Data (if applicable)
- [ ] Uses `MGData.get()` (no hardcoded data)
- [ ] Uses `MGSprite.toCanvas()` (no hardcoded sprite paths)
- [ ] Globals subscribed with cleanup

### Boundaries
- [ ] Only modifies game UI (never Gemini HUD)
- [ ] No imports from `src/ui/components/` or `src/ui/hud/`
- [ ] No Shadow DOM

---

## Integration with Features

If the injection is part of a feature:

```typescript
// In src/features/<featureName>/index.ts
import { <FeatureName>Inject } from '../../ui/inject/qol/<featureName>';

function init(): void {
    // ... feature init
    <FeatureName>Inject.init();
}

function destroy(): void {
    <FeatureName>Inject.destroy();
    // ... feature cleanup
}
```

---

## References

- Rules: `.claude/rules/ui/ui.inject.md`
- Existing injections: `src/ui/inject/qol/*/`
- Storage: `src/utils/storage.ts`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.