Text Monitor Package

The Text Monitor package captures typing behavior on text fields and editors: keystroke timing, pauses, paste events, and deletions. Output is a per-session composition classification (Imported, Single Drafted, Substituted, Quoted, Original). This page covers the standalone @isnotai/text-monitor package for sites that do not run the NotAI pixel. If the pixel is already installed, enable text monitoring there instead (see the pixel install guide).

Overview

The package ships in two shapes that share the same capture engine:

  • npm package with programmatic control. Use this when editors mount and unmount dynamically, or when you want to attach and detach at specific lifecycle points.
  • CDN /auto.js script for a declarative drop-in install. Use this when a single script tag is enough and you do not need runtime control.

Both shapes produce the same output and use the same configuration keys. Pick whichever fits your stack.

When to Use This

Use the standalone package if:

  • Your site does not run the NotAI pixel.
  • You want text monitoring on a specific subset of pages or fields.
  • You need programmatic control over attach and detach timing (for example, editors that mount and unmount during a single-page-app navigation).

Use the pixel-bundled path instead if:

  • You are already running the NotAI pixel.
  • You want a one-line script install with no runtime code.
  • You want RCE auto-detection.

Do not load both the pixel and the standalone package on the same page.

Install

Install from npm:

npm
npm install @isnotai/text-monitor
yarn
yarn add @isnotai/text-monitor
pnpm
pnpm add @isnotai/text-monitor

Or use the CDN /auto.js entry (see Declarative Install below).

Programmatic Usage

Create a monitor with your integration ID and region, then attach it to any supported text field. Detach on cleanup.

JavaScript
import { createMonitor } from '@isnotai/text-monitor';

const monitor = createMonitor({
  integrationId: 'a7f3c2e1',
  region: 'us'
});

// Attach to a specific field
monitor.attach(document.getElementById('essay'));

// Later, when the field unmounts
monitor.detach(document.getElementById('essay'));

Attach is idempotent. Detaching a field that was never attached is a no-op.

Options

Option Type Required Description
integrationId string Yes 8-character hex, from your dashboard. See Integration ID.
region string Yes 'us' or 'eu'. Must match the region on your account. See Regions.
extraSelectors string[] No CSS selectors for additional custom editors beyond the standard set.

Declarative Install (auto.js)

For a single-script install, drop the /auto.js script tag into your page. It reads its configuration from a data-config attribute and attaches to supported editors on load.

HTML
<script
  src="https://cdn.isnotai.com/text-monitor/auto.js"
  data-config='{"integrationId":"a7f3c2e1","region":"us"}'
  async></script>

For custom editors not covered by the default selectors, pass extraSelectors:

HTML
<script
  src="https://cdn.isnotai.com/text-monitor/auto.js"
  data-config='{"integrationId":"a7f3c2e1","region":"us","extraSelectors":[".my-editor"]}'
  async></script>

Runtime attach and detach are not available through /auto.js. If you need programmatic control, use the npm package.

Self-hosted Script

Some customers need the text monitor to be served from their own origin so it survives aggressive adblocker lists that target well-known third-party domains. For those cases we issue a pre-packaged bundle with your integration ID, region, and reporting endpoints baked in, which you host on a subdomain of your own site.

The setup is self-service from your dashboard:

  1. In your dashboard, open Settings → Self-hosted Script. You will see the CNAME targets your package needs.
  2. Create the CNAME records on your own subdomain (for example, verify.example.com). The dashboard verifies propagation once the records are live.
  3. Download your packaged bundle. It is built with your integration ID, region, and CNAMEd reporting hosts baked in.
  4. Host the file on your own CDN. Your install tag becomes a single script with no data-* attributes:
HTML
<script src="//your-cdn.example.com/text-monitor.js" async></script>

The same packaged bundle works for programmatic control: import its createMonitor export from the local copy instead of the public npm package.

When you need to change configuration, return to the dashboard and download a fresh package. There is no runtime config for the self-hosted build; everything lives in the file you host.

Supported Editors

The package includes adapters for the following editor surfaces. Adapter selection is automatic: attach to any container and the package dispatches to the right adapter.

  • <textarea>
  • <input type="text">
  • Any element with contenteditable="true"
  • Quill editor instances
  • TinyMCE editor instances

If you use a different editor, pass its root container selector via extraSelectors. The package falls back to universal content diffing for anything that is not one of the specialized adapters above.

Integration ID

Every NotAI deployment is identified by an 8-character hexadecimal integration ID (for example a7f3c2e1). Copy yours from Integrations in your NotAI dashboard.

The integration ID is public by design: it identifies which account receives the session data. It does not authenticate the sender, and a leaked integration ID does not expose account data.

Regions

NotAI operates two data regions: US and EU. Pick the region that matches your account.

  • US accounts: set region: 'us' (the default).
  • EU accounts: set region: 'eu'. Your site must also allow the EU reporting hosts in its Content Security Policy (see below).

A session sent to the wrong region will be rejected.

React Usage

Attach the monitor inside a useEffect hook. Detach in the cleanup function so navigation or conditional rendering does not leave a dangling attachment.

React
import { useEffect, useRef } from 'react';
import { createMonitor } from '@isnotai/text-monitor';

const monitor = createMonitor({
  integrationId: 'a7f3c2e1',
  region: 'us'
});

function EssayForm() {
  const ref = useRef(null);

  useEffect(() => {
    if (!ref.current) return;
    monitor.attach(ref.current);
    return () => monitor.detach(ref.current);
  }, []);

  return <textarea ref={ref} />;
}

Create the monitor once at module scope, not on every render.

Vue Usage

Attach in onMounted, detach in onUnmounted.

Vue 3
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { createMonitor } from '@isnotai/text-monitor';

const monitor = createMonitor({
  integrationId: 'a7f3c2e1',
  region: 'us'
});

const textareaRef = ref(null);

onMounted(() => monitor.attach(textareaRef.value));
onUnmounted(() => monitor.detach(textareaRef.value));
</script>

<template>
  <textarea ref="textareaRef"></textarea>
</template>

Content Security Policy

Allow the CDN script host and the reporting hosts that match your region. Pick one regional block. Never combine.

US

CSP
script-src 'self' https://cdn.isnotai.com;
connect-src 'self' https://chl.isnot.ai wss://chl.isnot.ai;

EU

CSP
script-src 'self' https://cdn.isnotai.com;
connect-src 'self' https://chl-eu.isnot.ai wss://chl-eu.isnot.ai;

If you already have the CDN host in script-src for other assets, only the region-specific connect-src line is new.

Troubleshooting

Monitor is not capturing

  • Confirm integrationId and region are correct for your account.
  • Ensure your page's origin is on the authorized origins list for your integration. Add origins from Settings → Authorized Origins in your dashboard. Requests from origins outside the list are rejected.
  • Verify your Content Security Policy allows the hosts for your region (see above).
  • Check the element you passed to attach() is one of the supported editor surfaces.

CSP violations in the browser console

  • Confirm you are using the CSP block for your account's region, not the other one.
  • If the /auto.js script is blocked, add https://cdn.isnotai.com to script-src.
  • If the reporting connection is blocked, add the regional host to connect-src.

Memory leaks in single-page apps

  • Call detach() in every cleanup path where the attached element is removed from the DOM.
  • Create the monitor once at module scope. Do not call createMonitor inside render paths.