npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wout_f/aindredacteur

v1.9.6

Published

DSP Tools - AI-powered editing tools for Tweakers DSP

Readme

aindredacteur Package

A standalone JavaScript package for AI-powered text editing, designed for the Tweakers DSP but usable in any CMS environment.

The package is fully autonomous - it handles CMS detection, layout modifications, field discovery, and UI creation automatically. No DOM manipulation required from your side.

Installation

npm install aindredacteur-package

Quick Start

Simple Integration (Recommended)

The package handles everything automatically - just call init() with your API endpoints:

import { Aindredacteur } from 'aindredacteur-package';

const aiPackage = new Aindredacteur();

await aiPackage.init({
    api: {
        endpoints: {
            proofreader: 'https://your-api/proofreader',
            titleSuggester: 'https://your-api/title-suggester',
            feedback: 'https://your-api/feedback'
        },
        apiKey: 'your-api-key'
    },
    options: {
        autoRegister: true,  // Auto-detect and register CMS fields (default)
        debug: false
    }
});

// That's it! The package will:
// 1. Detect your CMS type automatically
// 2. Apply necessary layout modifications
// 3. Discover and register all relevant fields
// 4. Create and display AI panels

What the Package Does Automatically

When you call init(), the package:

  1. Detects CMS Type: Uses built-in detection to identify your CMS (News, Review, Geek, etc.)
  2. Applies Layout: Injects CSS to create side-by-side layout for editors and AI panels
  3. Prepares Containers: Enhances field containers with appropriate classes and ARIA attributes
  4. Registers Fields: Discovers and registers all CMS fields automatically
  5. Creates UI: Generates AI panels and suggestion buttons in the correct positions

No manual DOM manipulation needed - the package is fully self-contained.

Configuration

API Configuration

{
    api: {
        endpoints: {
            proofreader: 'https://...',    // Text analysis endpoint
            titleSuggester: 'https://...',  // Title suggestions endpoint
            feedback: 'https://...'         // Content feedback endpoint
        },
        // Single API key for all endpoints
        apiKey: 'your-key',
        // Or per-endpoint keys
        apiKeys: {
            proofreader: 'key1',
            titleSuggester: 'key2',
            feedback: 'key3'
        }
    }
}

Options

{
    options: {
        autoRegister: true,    // Auto-detect and register CMS fields (default: true)
        debug: false,          // Enable debug logging
        useV2Pipeline: true    // Use V2 suggestion pipeline (recommended)
    }
}

autoRegister Option

The autoRegister option controls automatic field detection and registration:

autoRegister: true (Default - Recommended)

await aiPackage.init({
    api: { endpoints: { /* ... */ } },
    options: { autoRegister: true }
});

// Package automatically:
// - Detects CMS type
// - Applies layout modifications
// - Discovers all fields
// - Registers fields with StateManager
// - Creates AI panels

autoRegister: false (Manual Control)

await aiPackage.init({
    api: { endpoints: { /* ... */ } },
    options: { autoRegister: false }
});

// Package still detects CMS and applies layout, but you must register fields manually:
const fieldElement = document.querySelector('#my_field');
const containerElement = document.querySelector('#my_container');
aiPackage.addField(fieldElement, containerElement, 'articleBody');

Use autoRegister: false only if you need custom field registration logic or want to control which fields are registered.

Legacy Configuration (Deprecated)

The old configuration format is still supported but deprecated:

// Old format (deprecated)
await aiPackage.init({
    apiEndpoints: {
        proofReader: 'https://...',
        titleSuggester: 'https://...',
        feedback: 'https://...'
    },
    apiKey: 'your-key'
});

Migration Guide

Migrating from Manual DOM Manipulation

Before (Old Pattern - Deprecated)

// 1. Manually detect CMS type
const cmsType = detectCMSType();

// 2. Manually apply layout modifications
applyLayoutCSS(cmsType);
createFieldContainers();

// 3. Initialize package
const aiPackage = new Aindredacteur();
await aiPackage.init({
    apiEndpoints: { /* ... */ }
});

// 4. Manually register each field
const bodyField = document.querySelector('#news_form_body');
const bodyContainer = document.querySelector('#row_news_form_body .field');
aiPackage.addField(bodyField, bodyContainer, 'articleBody');

const titleField = document.querySelector('#news_form_name');
const titleContainer = document.querySelector('#row_news_form_name .field');
aiPackage.addField(titleField, titleContainer, 'title');

After (New Pattern - Recommended)

// Just initialize - package handles everything
const aiPackage = new Aindredacteur();
await aiPackage.init({
    api: {
        endpoints: {
            proofreader: 'https://...',
            titleSuggester: 'https://...',
            feedback: 'https://...'
        },
        apiKey: 'your-key'
    },
    options: {
        autoRegister: true  // Default, can be omitted
    }
});

// That's it! No manual DOM manipulation needed.

Migrating Chrome Extensions

Before (Old Extension Pattern)

// extension/content.js
import { ChromeExtensionAdapter } from './chrome-extension-adapter.js';
import { Aindredacteur } from './aindredacteur-package/index.js';

// 1. Create adapter
const adapter = new ChromeExtensionAdapter();

// 2. Detect CMS and apply layout
await adapter.detectAndSetupCMS();

// 3. Initialize package
const aiPackage = new Aindredacteur();
await aiPackage.init({ apiEndpoints: { /* ... */ } });

// 4. Register fields via adapter
adapter.registerFieldsWithAI(aiPackage);

After (New Extension Pattern)

// extension/content.js
import { Aindredacteur } from './aindredacteur-package/index.js';

// Just initialize - package handles CMS detection, layout, and field registration
const aiPackage = new Aindredacteur();
await aiPackage.init({
    api: {
        endpoints: {
            proofreader: chrome.runtime.getURL('api/proofreader'),
            titleSuggester: chrome.runtime.getURL('api/title-suggester'),
            feedback: chrome.runtime.getURL('api/feedback')
        }
    },
    options: {
        autoRegister: true
    }
});

// ChromeExtensionAdapter is no longer needed

Key Changes

  1. No Manual CMS Detection: Package detects CMS automatically using CMSRegistry
  2. No Manual Layout: Package applies layout CSS automatically using LayoutManager
  3. No Manual Field Registration: Package discovers and registers fields automatically
  4. Simplified Configuration: Just provide API endpoints, package handles the rest
  5. Cleaner Code: Reduced from ~100 lines to ~10 lines for typical integration

Custom Transport Layer

For advanced use cases, you can provide a custom transport:

import { Aindredacteur } from 'aindredacteur-package';
import { FetchTransport, MockTransport } from 'aindredacteur-package/core/transport.js';

// Custom fetch transport
const transport = new FetchTransport({
    apiKey: 'your-key',
    timeout: 30000
});

await aiPackage.init({
    transport,
    api: {
        endpoints: { /* ... */ }
    }
});

// Mock transport for testing
const mockTransport = new MockTransport({
    'proofreader': { changes: [], flags: {} }
});

await aiPackage.init({ transport: mockTransport });

Architecture

How It Works

The package is fully autonomous and handles all DOM manipulation internally:

┌─────────────────────────────────────────────────────────────┐
│                     Your Application                         │
│                                                              │
│  await aiPackage.init({ api: { endpoints: {...} } })       │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                  Aindredacteur Package                       │
│                                                              │
│  ┌────────────────┐  ┌─────────────────┐  ┌──────────────┐ │
│  │  CMSRegistry   │  │ LayoutManager   │  │ StateManager │ │
│  │                │  │                 │  │              │ │
│  │ • Detect CMS   │  │ • Apply CSS     │  │ • Track      │ │
│  │ • Get config   │  │ • Prepare       │  │   fields     │ │
│  │                │  │   containers    │  │ • Manage UI  │ │
│  └────────────────┘  └─────────────────┘  └──────────────┘ │
│                                                              │
│  ┌────────────────────────────────────────────────────────┐ │
│  │              Auto-Registration                          │ │
│  │  • Discover fields from CMS config                     │ │
│  │  • Register with StateManager                          │ │
│  │  • Create AI panels                                    │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Key Components

CMSRegistry: Detects CMS type by checking DOM structure against known patterns. Returns configuration with field selectors and layout dimensions.

LayoutManager: Applies CMS-specific CSS modifications to create side-by-side layout. Prepares field containers with appropriate classes and ARIA attributes.

StateManager: Tracks registered fields, manages UI panels, and coordinates suggestion application.

Auto-Registration: Discovers fields based on CMS configuration and automatically registers them with the StateManager.

Initialization Flow

  1. CMS Detection: CMSRegistry.detect() identifies CMS type
  2. Layout Application: LayoutManager.applyLayout() injects CSS
  3. Container Preparation: LayoutManager.prepareContainers() enhances containers
  4. Field Discovery: Iterate over fields from CMS configuration
  5. Field Registration: Register each field with StateManager
  6. UI Creation: Create AI panels and suggestion buttons

All of this happens automatically when you call init() with autoRegister: true.

Supported CMS Types

The package auto-detects these CMS types and applies appropriate layout modifications:

| CMS Type | Detection Selector | Auto-Registered Fields | |----------|-------------------|------------------------| | News | #row_news_form_body .field | Article Body, Title, Introduction | | Geek | #row_geek_form_body .field | Article Body, Title, Introduction | | Review | #row_review_page_form_body | Chapter Body, Title | | Review Snippet | #review_form_articleSnippet_snippetText | Snippet Text | | Plan | #row_plan_form_body .field | Article Body, Title, Introduction | | Product Card | #row_product_card_form_description .field | Description | | Scorecard | textarea#scorecard_form_bottomline | Bottom Line |

When a CMS is detected, the package automatically:

  • Applies CMS-specific layout CSS (page width, grid layout, styling)
  • Prepares field containers with appropriate classes and ARIA attributes
  • Discovers and registers all fields defined in the CMS configuration
  • Creates AI panels in the correct positions

If no CMS is detected, the package logs a warning and continues without layout modifications. You can still use manual field registration in this case.

Troubleshooting

No CMS Detected

If you see the warning [Aindredacteur] No CMS detected, skipping layout modifications:

  1. Check DOM Structure: Ensure your page has the expected CMS selectors
  2. Manual Registration: Use autoRegister: false and register fields manually
  3. Custom CMS: Add your CMS to CMSRegistry if it's not supported
// Manual registration when CMS not detected
await aiPackage.init({
    api: { endpoints: { /* ... */ } },
    options: { autoRegister: false }
});

const field = document.querySelector('#my_custom_field');
const container = document.querySelector('#my_custom_container');
aiPackage.addField(field, container, 'articleBody');

Layout Not Applied

If the layout doesn't appear correctly:

  1. Check Console: Look for error messages in browser console
  2. Verify CMS Detection: Check aiPackage.cmsType to confirm detection
  3. Inspect CSS: Verify layout CSS was injected (check <head> for <style> tags)
  4. Check Conflicts: Look for CSS conflicts with existing page styles

Fields Not Registered

If fields aren't being registered automatically:

  1. Enable Debug Mode: Set options.debug: true to see detailed logs
  2. Check Selectors: Verify field elements exist in DOM when init() is called
  3. Timing Issues: Ensure init() is called after DOM is ready
  4. Manual Registration: Fall back to manual registration if needed
// Debug mode for troubleshooting
await aiPackage.init({
    api: { endpoints: { /* ... */ } },
    options: { 
        autoRegister: true,
        debug: true  // Enables detailed logging
    }
});

Chrome Extension Context

If loading in a Chrome extension:

  1. Resource Paths: Package automatically detects Chrome extension context
  2. Web Accessible Resources: Ensure package files are listed in manifest
  3. CSP Issues: Check Content Security Policy allows inline styles
// manifest.json
{
    "web_accessible_resources": [{
        "resources": [
            "aindredacteur-package/*",
            "aindredacteur-package/**/*"
        ],
        "matches": ["<all_urls>"]
    }]
}

API

Aindredacteur

Main class for the AI editing package.

Constructor

const ai = new Aindredacteur();

Methods

init(config)

Initialize the package with API configuration.

await ai.init({
    api: {
        endpoints: {
            proofreader: 'https://...',
            titleSuggester: 'https://...',
            feedback: 'https://...'
        },
        apiKey: 'your-key'
    },
    options: {
        autoRegister: true,  // Auto-detect and register fields (default: true)
        debug: false,        // Enable debug logging
        useV2Pipeline: true  // Use V2 suggestion pipeline (recommended)
    }
});

When autoRegister: true (default), the package automatically:

  1. Detects CMS type using CMSRegistry
  2. Applies layout modifications using LayoutManager
  3. Prepares field containers
  4. Discovers and registers all CMS fields
addField(element, container, fieldType)

Manually register a field (only needed when autoRegister: false).

const fieldElement = document.querySelector('#my_field');
const containerElement = document.querySelector('#my_container');
const fieldId = ai.addField(fieldElement, containerElement, 'articleBody');

Parameters:

  • element: The editor element (textarea, input, or CKEditor instance)
  • container: The container element where AI panels will be created
  • fieldType: Field type ('articleBody', 'title', 'introduction', etc.)

Returns: Field ID string

runProofreader(fieldId)

Run proofreader analysis on a specific field.

await ai.runProofreader(fieldId);
getTitleSuggestions(articleText)

Get AI-generated title suggestions.

const suggestions = await ai.getTitleSuggestions(articleText);
getFeedback(introText, articleText)

Get content quality feedback.

const feedback = await ai.getFeedback(introText, articleText);
destroy()

Clean up and remove all package modifications.

ai.destroy();

This removes:

  • All injected CSS styles
  • All added body classes
  • All AI panel elements
  • All field container enhancements
  • All event listeners

The page is restored to its pre-initialization state.

Properties

cmsType

The detected CMS type (e.g., 'news', 'review', 'scorecard').

console.log(ai.cmsType); // 'news'
cmsName

The human-readable CMS name.

console.log(ai.cmsName); // 'News CMS'
detectedCMS

The full CMS configuration object from CMSRegistry.

console.log(ai.detectedCMS);
// {
//   type: 'news',
//   name: 'News CMS',
//   fields: { ... },
//   layout: { ... }
// }

Testing

Unit Tests

Run package unit tests with Vitest:

cd aindredacteur-package
npx vitest run

Integration Tests

Test the package in a real browser environment:

# Start development server
npm run dev

# Open test page
# Navigate to http://localhost:8001/dev-tools/test-page.html

Mock Transport for Testing

Use MockTransport to test without real API calls:

import { Aindredacteur } from 'aindredacteur-package';
import { MockTransport } from 'aindredacteur-package/core/transport.js';

const mockTransport = new MockTransport({
    'proofreader': {
        changes: [
            { type: 'spelling', original: 'teh', suggestion: 'the', offset: 0 }
        ],
        flags: { hasSpellingErrors: true }
    },
    'titleSuggester': {
        suggestions: ['Great Title 1', 'Great Title 2']
    }
});

const aiPackage = new Aindredacteur();
await aiPackage.init({ 
    transport: mockTransport,
    options: { autoRegister: false }
});

Examples

Basic CMS Integration

import { Aindredacteur } from 'aindredacteur-package';

// Initialize on page load
document.addEventListener('DOMContentLoaded', async () => {
    const aiPackage = new Aindredacteur();
    
    await aiPackage.init({
        api: {
            endpoints: {
                proofreader: 'https://api.example.com/proofreader',
                titleSuggester: 'https://api.example.com/title-suggester',
                feedback: 'https://api.example.com/feedback'
            },
            apiKey: 'your-api-key'
        },
        options: {
            autoRegister: true,
            debug: true
        }
    });
    
    console.log(`Initialized for CMS: ${aiPackage.cmsName}`);
});

Chrome Extension Integration

// content-script.js
import { Aindredacteur } from './aindredacteur-package/index.js';

async function initializeExtension() {
    const aiPackage = new Aindredacteur();
    
    await aiPackage.init({
        api: {
            endpoints: {
                proofreader: chrome.runtime.getURL('api/proofreader'),
                titleSuggester: chrome.runtime.getURL('api/title-suggester'),
                feedback: chrome.runtime.getURL('api/feedback')
            }
        },
        options: {
            autoRegister: true
        }
    });
    
    // Make available for debugging
    window.aiPackage = aiPackage;
}

// Wait for CKEditor if present
if (window.CKEDITOR?.instances) {
    initializeExtension();
} else if (window.CKEDITOR) {
    CKEDITOR.on('instanceReady', initializeExtension);
} else {
    initializeExtension();
}

Manual Field Registration

import { Aindredacteur } from 'aindredacteur-package';

const aiPackage = new Aindredacteur();

await aiPackage.init({
    api: { endpoints: { /* ... */ } },
    options: { autoRegister: false }
});

// Register custom fields
const bodyField = document.querySelector('#custom_body_field');
const bodyContainer = document.querySelector('#custom_body_container');
const bodyFieldId = aiPackage.addField(bodyField, bodyContainer, 'articleBody');

const titleField = document.querySelector('#custom_title_field');
const titleContainer = document.querySelector('#custom_title_container');
const titleFieldId = aiPackage.addField(titleField, titleContainer, 'title');

// Run proofreader on specific field
await aiPackage.runProofreader(bodyFieldId);

With CKEditor

import { Aindredacteur } from 'aindredacteur-package';

// Wait for CKEditor to be ready
CKEDITOR.on('instanceReady', async (event) => {
    const aiPackage = new Aindredacteur();
    
    await aiPackage.init({
        api: { endpoints: { /* ... */ } },
        options: { autoRegister: true }
    });
    
    // Package automatically detects and registers CKEditor instances
    console.log('AI package initialized with CKEditor support');
});

Contributing

Development Setup

# Install dependencies
npm install

# Run tests
npx vitest run

# Run tests in watch mode
npx vitest

# Run tests with coverage
npx vitest run --coverage

Adding New CMS Support

To add support for a new CMS type:

  1. Add CMS Configuration to CMSRegistry (core/cms-registry.js):
{
    type: 'my-cms',
    name: 'My CMS',
    detector: () => {
        return document.querySelector('#my-cms-identifier') !== null;
    },
    fields: {
        articleBody: {
            container: '#my-cms-body-container',
            editor: '#my-cms-body-editor',
            editorType: 'ckeditor'
        },
        title: {
            container: '#my-cms-title-container',
            input: '#my-cms-title-input',
            editorType: 'input'
        }
    },
    layout: {
        pageWidth: '1280px',
        contentWidth: '1260px',
        gridColumns: '724px 380px',
        ckeMaxWidth: '704px'
    }
}
  1. Test Detection: Verify CMS is detected correctly
  2. Test Layout: Verify layout modifications work correctly
  3. Test Auto-Registration: Verify fields are registered automatically

License

UNLICENSED