Hooks

Hooks for headless annotation usage and custom components.

useAnnotatable

Access annotation context inside Document.Annotatable. Use this to build custom annotation UI.

import { useAnnotatable } from "@tilt-legal/cubitt-components/document";

function CustomButton() {
  const { createAnnotation, annotations } = useAnnotatable();

  return (
    <button onClick={() => createAnnotation()}>
      Add ({annotations.length})
    </button>
  );
}

Returns

PropertyTypeDescription
annotationsAnnotationData[]Current annotations
activeAnnotationIdstring | nullSelected annotation
hoveredAnnotationIdstring | nullHovered annotation
createAnnotation(initialText?: string) => voidCreate annotation from selection
createAnnotationFromMarkdownText(citedText: string, comment: string, options?: { instance?: number; author?: AnnotationAuthor }) => AnnotationData | nullCreate annotation from markdown text. instance (1-indexed, default 1) specifies which occurrence when text appears multiple times. author overrides defaultAuthor for this annotation. Returns null if text not found.
updateAnnotation(id: string, text: string) => voidUpdate annotation text
deleteAnnotation(id: string) => voidDelete annotation
setActiveAnnotation(id: string | null) => voidSet active annotation
setHoveredAnnotation(id: string | null) => voidSet hovered annotation
scrollToAnnotation(id: string) => voidScroll to annotation in document
selectNextAnnotation() => voidNavigate to next annotation
selectPreviousAnnotation() => voidNavigate to previous annotation
getAnnotationsWithPositions() => AnnotationData[]Get annotations sorted by position
getAnnotationById(id: string) => AnnotationData | undefinedFind annotation by ID
getMarkdown() => stringGet document as markdown
applyAnnotationMark(id: string) => voidApply annotation mark to editor
removeAnnotationMark(id: string) => voidRemove annotation mark from editor

useAnnotationCard

Access per-card state inside Document.Annotation.Sidebar.Card. Use this to build custom card components.

import { useAnnotationCard } from "@tilt-legal/cubitt-components/document";

function CustomDelete() {
  const { remove, annotation, isActive } = useAnnotationCard();

  return (
    <button onClick={remove} disabled={!isActive}>
      Delete "{annotation.citedText.slice(0, 20)}..."
    </button>
  );
}

Returns

PropertyTypeDescription
annotationAnnotationDataThe annotation data
isActivebooleanWhether this card is selected
isHoveredbooleanWhether this card is hovered
activate() => voidSelect this annotation
selectText() => voidSelect the annotation text in editor
scrollToMark() => voidScroll to the mark in the document
update(text: string) => voidUpdate the annotation text
remove() => voidDelete the annotation

useHasSelection

Boolean indicating if valid text is selected in the document. Useful for enabling/disabling add buttons.

import {
  useAnnotatable,
  useHasSelection,
} from "@tilt-legal/cubitt-components/document";

function HeaderAddButton() {
  const hasSelection = useHasSelection();
  const { createAnnotation } = useAnnotatable();

  return (
    <button disabled={!hasSelection} onClick={() => createAnnotation()}>
      Add Annotation
    </button>
  );
}

Returns

boolean - true when valid text is selected.


LLM Integration

When integrating with LLMs, you can have the model analyze document content and return citations that automatically become annotations. The LLM works with markdown text and doesn't need to know about editor positions.

  1. Export: Call getMarkdown() to get the document content
  2. Analyze: Send markdown to your LLM with citation requirements
  3. Annotate: Pass LLM citations to createAnnotationFromMarkdownText()

Structured Output Schema

Define a schema for LLM citations:

import {
  z } from "zod";

const citationSchema = z.object({
  citedText: z.object({
    text: z.string(),
  instance: z.number(),
  }),
  comment: z.string(),
  });

Then consume it:

import { useAnnotatable } from "@tilt-legal/cubitt-components/document";

const { createAnnotationFromMarkdownText } = useAnnotatable();

for (const { citedText, comment } of citations) {
  createAnnotationFromMarkdownText(citedText.text, comment, {
    instance: citedText.instance,
  });
}

LLM System Prompt

Include citation requirements in your system prompt:

## Citation Requirements

For EVERY issue you identify, you MUST provide:
- `citedText.text`: The exact quoted text from the document
- `citedText.instance`: Which occurrence of this text (1 = first, 2 = second, etc.)

Guidelines:
- Quote the exact text, do not paraphrase
- Keep citations concise but complete enough to identify the issue
- If an issue spans multiple sentences, quote the most relevant portion
- Always specify the instance, even if the text only appears once (use 1)

On this page