DocsReference

Types, IntelliSense and examples

TypeScript, checked JavaScript and the classic browser global, verified as package consumers.

On this page

@voice/review renders findings supplied by your host application. Its ESM entry includes TypeScript declarations and JSDoc for completion, callback inference and hover help. JavaScript consumers can use // @ts-check; classic scripts can reference types for the VoiceReview global. Analysis and source editing belong to the host, outside this library's API.

Build and editor setup#

In this workspace:

sh
npm run build -w @voice/review
npm run test:types -w @voice/review

Resolve imports by the package name @voice/review, not by a private src/ path. The package's exports and types entries point at built declarations. Build the local package before consuming it in another project. The consumer test exercises NodeNext and Bundler module resolution.

For JavaScript projects, // @ts-check enables checking in the current file. A project can instead set allowJs, checkJs, strict, and noEmit in its TypeScript configuration. Include the DOM library when replacing the default lib list. An editor using the TypeScript language service can show controller methods, callback parameters, literal options such as encoding: 'utf16', method signatures, and the public JSDoc text.

TypeScript: mount, update, dispose#

This complete example uses an exact plain-text source string. The example host creates its own markup; the annotation library leaves that markup intact. For HTML, Markdown or framework content files, obtain units and source spans from a source adapter instead of deriving file offsets from rendered HTML.

ts
import {
  mountVoiceReview, codePointOffsetToUtf16,
  type Finding, type SourceSpan, type TextUnit,
} from '@voice/review';

export function showReview(root: HTMLElement): () => void {
  const source = '🧭 Clear copy.';
  const sourceSpan: SourceSpan = {
    start: 0, end: source.length, encoding: 'utf16',
  };
  const unit: TextUnit = {
    id: 'intro', text: source, sourceSpan,
    kind: 'paragraph', language: 'en',
  };
  const finding: Finding = {
    id: 'example-1', ruleId: 'host.example', category: 'wording', severity: 'info',
    message: 'Example finding', explanation: 'Supplied example data; no analysis is run.',
    quote: 'Clear', unitId: unit.id,
    start: codePointOffsetToUtf16(source, 2),
    end: codePointOffsetToUtf16(source, 7),
    suggestion: null, engine: 'host/example',
  };
  const paragraph = root.ownerDocument.createElement('p');
  paragraph.dataset.voiceUnit = unit.id;
  paragraph.textContent = source;
  root.replaceChildren(paragraph);

  const review = mountVoiceReview({
    root, units: [unit], findings: [finding], feedback: [],
    onSelect(selection) {
      // selection is inferred: unitId, quote, start and end.
      console.info(selection.quote);
    },
  });
  review.update({ findings: [finding] });
  console.info(review.getDiagnostics());
  return () => review.dispose();
}

Invoke the returned cleanup when the host changes route, removes the view or replaces its iframe. Call mountVoiceReview after the target document has loaded. A new root needs a new mount; update() changes data and callbacks on the existing root. The checked example is typescript.ts.

JavaScript: the same API with JSDoc#

js
// @ts-check
import { mountVoiceReview } from '@voice/review';

/**
 * @param {HTMLElement} root
 * @param {readonly import('@voice/review').TextUnit[]} units
 * @param {readonly import('@voice/review').Finding[]} findings
 * @returns {() => void}
 */
export function showReview(root, units, findings) {
  const review = mountVoiceReview({
    root, units, findings, feedback: [],
    onSelect(selection) { console.info(selection.quote); },
  });
  return () => review.dispose();
}

The callback and returned controller are inferred without converting this file to TypeScript. A complete source-span example is javascript.js.

A classic script without a bundler#

Load the built runtime as a normal script, then load your host integration:

html
<script src="/library/voice-review.js"></script>
<script src="/review-host.js"></script>

At the top of review-host.js, when @voice/review is available to the editor:

js
/// <reference types="@voice/review/standalone" />
// @ts-check
const connection = VoiceReview.connectVoiceStudio({
  studioOrigin: 'http://127.0.0.1:5188',
});
window.addEventListener('pagehide', () => connection.dispose(), { once: true });

The triple-slash reference supplies declarations only. It does not download or execute the library. Do not import the standalone runtime as an ESM module to obtain a global: ESM consumers use named imports from @voice/review. The checked global example is standalone.js.

Coordinates and AI integration#

SourceSpan.start/end refer to the original source file. A finding's or selection's start/end refer to the text of its mapped unit. Both use zero-based, half-open UTF-16 code units. An emoji can occupy two code units. In the plain text example, Clear is at [3, 8) in UTF-16, although a code-point engine reports [2, 7). Convert both endpoints with codePointOffsetToUtf16 before sending such findings to this library. Matching coordinates alone do not establish a source file mapping: markup, entities and excluded content require an adapter.

A host can pass validated AI findings into mountVoiceReview or update just as it passes local-rule findings. The host must validate the engine's schema, offset encoding, exact quote, unit identity and source version. It owns the goal, marketing context, model route, job execution, result provenance and review decisions. The bridge transports supplied review data to an explicitly integrated live page; it does not transmit backend credentials or start model work.

Use createReviewClient only in the authorized host when feedback must persist through the Studio backend. It exposes typed getDocument and saveFeedback operations. Supply the current bearer through a host-owned in-memory getter; never embed it in the reviewed website, URLs, source control or reports. FeedbackInput carries expectedVersion, expectedReviewRevision and a requestId. Preserve the same request ID and exact payload when retrying an uncertain save. Source application remains a separate, guarded backend workflow. The overlay alone has no durable feedback store and no source-write operation.

See the Library README for persistence examples and the bridge contract for origin/session checks.

What verification proves#

packages/review/test/types.test.mjs creates an isolated consumer outside this workspace using only the package's declared distributable files. It compiles real TypeScript, checkJs, classic-global and deliberately invalid consumer fixtures with both NodeNext and Bundler resolution. Expected errors ensure that encoding, root, category and callback constraints have not silently degraded to any.

The test also calls the actual TypeScript language service for controller and selection completions, classic-global completion, cleanup signatures and JSDoc hover text. This validates the metadata used by compatible editors. It does not drive the VS Code interface, prove the behavior of a particular editor extension, or verify rendering in VS Code's embedded browser. Existing Library runtime tests cover mounting, updates, teardown, anchoring and bridge behavior separately.

Git and source reference

Source for this page: docs/library-types.md. Content hash and Git revision: Build record.

Download the Markdown source
Next guideThe live-page bridge protocol →