Annotation overview
Type-safe annotation system for attaching metadata to boxes.
Annotations allow boxes to carry arbitrary typed data (colors, styles,
semantic tags) that renderers can interpret. The annotation type parameter
A on Box<A> flows through all composition operations.
Mental model
Annotation<A>— a wrapper holding metadata of typeA- Annotations are structural — they implement
EqualandHash - They compose via
combineAnnotationsfor layered styling
Common tasks
- Create an annotation:
createAnnotation,empty - Inspect:
isAnnotation,getAnnotationData - Transform:
mapAnnotationData,combineAnnotations
combinators
combineAnnotations
Combines two annotations by merging their data.
Merges annotation data using a custom merger function. Essential for combining multiple styling annotations or accumulating annotation properties across box compositions.
Signature
export declare const combineAnnotations: <A>(
first: Annotation<A>,
second: Annotation<A>,
merger: (a: A, b: A) => A
) => Annotation<A>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
const redColor = Ansi.red
const boldStyle = Ansi.bold
const combined = Annotation.combineAnnotations(redColor, boldStyle, (first, second) => [...first, ...second])
const allCommands = Annotation.getAnnotationData(combined)
// Contains both color and bold commandsconstructors
createAnnotation
Creates a new Annotation with the provided data.
The fundamental constructor for creating typed annotations. Wraps arbitrary data in the Annotation abstraction for use with the Box system.
Signature
export declare const createAnnotation: <A>(data: A) => Annotation<A>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Box from "effect-boxes/Box"
// Create custom annotation types
type TooltipData = { text: string; position: "top" | "bottom" }
const tooltip = Annotation.createAnnotation<TooltipData>({
text: "Click for more info",
position: "top"
})
// Apply to box
const buttonWithTooltip = Box.text("Click me").pipe(Box.annotate(tooltip))createAnnotations
Creates an array of annotations from an array of data.
Efficiently wraps multiple data items in annotations. Useful for batch processing of styling data or creating multiple annotations with similar structure.
Signature
export declare const createAnnotations: <A>(dataArray: readonly A[]) => Annotation<A>[]Example
import * as Annotation from "effect-boxes/Annotation"
type ColorData = { name: string; hex: string }
const colorData: ColorData[] = [
{ name: "red", hex: "#ff0000" },
{ name: "green", hex: "#00ff00" },
{ name: "blue", hex: "#0000ff" }
]
const colorAnnotations = Annotation.createAnnotations(colorData)
// Array of 3 Annotation<ColorData> instancesempty
Predefined empty annotation for use as a default or placeholder.
Provides a safe default annotation that contains no data. Useful for initializing annotation systems, providing fallback values, or creating placeholder annotations that can be replaced later.
Signature
export declare const empty: Annotation<never>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
import * as Box from "effect-boxes/Box"
const getStatusStyle = (isError: boolean) => (isError ? Ansi.red : Annotation.empty)
const statusBox = Box.text("Status: OK").pipe(Box.annotate(getStatusStyle(false)))
// Box with no styling applied due to empty annotationguards
isAnnotation
Type guard to check if a value is an Annotation.
Signature
export declare const isAnnotation: (value: unknown) => value is Annotation<unknown>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
const style = Ansi.red
if (Annotation.isAnnotation(style)) {
console.log("This is an annotation")
// TypeScript now knows style is Annotation<unknown>
}isAnnotationWithData
Type guard to check if a value is an Annotation with specific data type.
Provides type-safe checking for annotations containing specific data types, useful when working with different annotation systems.
Signature
export declare const isAnnotationWithData: <A>(
value: unknown,
dataGuard: (data: unknown) => data is A
) => value is Annotation<A>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
const isStringAnnotation = (data: unknown): data is string => typeof data === "string"
const annotation = Annotation.createAnnotation("some text")
if (Annotation.isAnnotationWithData(annotation, isStringAnnotation)) {
// TypeScript knows annotation is Annotation<string>
const text = Annotation.getAnnotationData(annotation) // string
}models
Annotation (interface)
Generic annotation wrapper with branded type safety.
Annotations provide a way to attach arbitrary data to boxes without affecting their layout properties. This abstraction allows styling systems (like ANSI colors) to work with the Box layout system.
Signature
export interface Annotation<A = never> extends Pipeable, Equal.Equal, Hash.Hash {
readonly [AnnotationTypeId]: "annotation"
readonly data: A
}AnnotationData (type alias)
Type-level utility to extract the data type from an Annotation type.
Signature
export type AnnotationData<T> = T extends Annotation<infer A> ? A : neverAnnotationTypeId
Signature
export declare const AnnotationTypeId: typeof AnnotationTypeIdAnnotationTypeId (type alias)
Signature
export type AnnotationTypeId = typeof AnnotationTypeIdtransformations
mapAnnotationData
Maps the data of an Annotation<A> to create an Annotation<B>.
Transforms annotation data while preserving the annotation structure. Essential for converting between different annotation formats or modifying annotation data for different rendering contexts.
Signature
export declare const mapAnnotationData: <A, B>(annotation: Annotation<A>, mapper: (data: A) => B) => Annotation<B>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
const redAnsi = Ansi.red
const cssAnnotation = Annotation.mapAnnotationData(redAnsi, (ansiCommands) => ({ className: "text-red-500" }))
const cssData = Annotation.getAnnotationData(cssAnnotation)
console.log(cssData) // { className: "text-red-500" }utilities
cloneAnnotation
Creates a copy of an annotation with the same data.
Creates a deep copy of an annotation with identical data. Useful for creating independent annotation instances that can be modified separately or for ensuring annotation immutability in complex operations.
Signature
export declare const cloneAnnotation: <A>(annotation: Annotation<A>) => Annotation<A>Example
import * as Annotation from "effect-boxes/Annotation"
import * as Box from "effect-boxes/Box"
type Style = { color: string; weight: string }
const baseStyle = Annotation.createAnnotation<Style>({
color: "blue",
weight: "normal"
})
const clonedStyle = Annotation.cloneAnnotation(baseStyle)
// Both annotations have the same data but are separate instances
const box1 = Box.text("Text 1").pipe(Box.annotate(baseStyle))
const box2 = Box.text("Text 2").pipe(Box.annotate(clonedStyle))extractAnnotationData
Extracts data from an array of annotations.
Unwraps multiple annotations to get their underlying data as an array.
The inverse operation of createAnnotations, useful for batch processing
annotation data or preparing data for external systems.
Signature
export declare const extractAnnotationData: <A>(annotations: readonly Annotation<A>[]) => A[]Example
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
const annotations = [Ansi.red, Ansi.bold, Ansi.underlined]
const allCommands = Annotation.extractAnnotationData(annotations)
// Flatten all ANSI commands for processing
const flatCommands = allCommands.flat()
console.log(flatCommands.length) // Total number of ANSI commandsfilterAnnotation
Filters annotation data based on a predicate function.
Returns the annotation if the predicate passes, otherwise undefined. Useful for conditionally applying annotations or filtering out annotations that don't meet certain criteria.
Signature
export declare const filterAnnotation: <A>(
annotation: Annotation<A>,
predicate: (data: A) => boolean
) => Annotation<A> | undefinedExample
import * as Annotation from "effect-boxes/Annotation"
import * as Box from "effect-boxes/Box"
type ConditionalStyle = { color: string; condition: boolean }
const conditionalRed = Annotation.createAnnotation<ConditionalStyle>({
color: "red",
condition: true
})
const validStyle = Annotation.filterAnnotation(conditionalRed, (data) => data.condition)
if (validStyle) {
// Apply the style to a box
const styledBox = Box.text("Error!").pipe(Box.annotate(validStyle))
}getAnnotationData
Extracts the data from an Annotation<A>.
Unwraps the annotation wrapper to access the underlying data. Essential for consuming annotation data in styling systems and custom renderers.
Signature
export declare const getAnnotationData: <A>(annotation: Annotation<A>) => AExample
import * as Annotation from "effect-boxes/Annotation"
import * as Ansi from "effect-boxes/Ansi"
const redStyle = Ansi.red
const ansiCommands = Annotation.getAnnotationData(redStyle)
console.log(ansiCommands)
// [{ _tag: "ForegroundColor", name: "red", code: "31" }]