Introduction

SearSuite provides composable infrastructure for spreadsheet and rich-document editors. Its current implementation uses TypeScript. The packages separate structured document models, transactional commands, rendering contracts, and optional plugins. Host applications select plugins and supply the platform adapters required by their editor configuration.

Sheet, Doc, and Workbook content is represented as explicit document state. Each stateful plugin owns its contribution. Commands provide the mutation boundary: the editor stages owner-defined operations and publishes committed results to observers and rendering. Hosts can inspect or observe state through typed public contracts.

Assemblies construct editor runtimes from selected packages. Visual editors require host-provided rendering and input adapters; headless assembly does not require visual host adapters. The host remains responsible for application UI, persistence, credentials, and product policy.

The Guide describes the package boundaries and runtime model. The Reference Manual documents public types, functions, and commands.

Modules and their relationships

SearSuite separates portable contracts, editor composition, optional features, and host integration across these package families:

  • sear-foundation-* defines dependency injection, platform, geometry, error, and shared utility contracts.
  • sear-domain-* defines document data, sparse matrices, operations, and protocols independently of a host interface.
  • sear-render-* defines scene, input, and drawing contracts for visual editors.
  • sear-assembly-* constructs editor runtimes and coordinates plugins, commands, and lifecycle.
  • sear-plugin-* contributes optional state, commands, and extension points.
  • Host application selects an assembly and plugins, then supplies UI, platform adapters, and transport.
Module dependencies
Assembly entrysear-assembly-setupComposes the editor and selected features
Feature packagesear-plugin-cell-fillFill state and commands
Visual runtimesear-render-engineScenes and input orchestration
Command APIsear-assembly-commandsCommand registration and dispatch
Plugin APIsear-assembly-pluginExtension points and lifecycle
Portable modelsear-domain-otDocument operations
Shared primitivesear-foundation-errorError contracts

Design principles

Document state has explicit owners. Assemblies connect selected plugins and host adapters through contracts, while the core editor remains independent of individual optional features.

One mutation path

State mutations in a running editor enter through typed commands. Input handlers and host code dispatch through the same command bus. The owning plugin registers a handler that prepares its operations; the Sheet core stages and commits those operations as one change. Rendering and observations consume the committed result.

Selection of cell D3 (zero-based coordinates [2, 3]) illustrates the command and query boundaries:

import { GetSelection, SelectRangeCommand } from '@searsuite/sear-plugin-selection';

editor.commands.run(SelectRangeCommand, { anchor: [2, 3], focus: [2, 3] });
const selection = editor.queries.run(GetSelection, undefined);

SheetSelectionPlugin normalizes merged-cell ranges and prepares Rect OT operations for the active cell and selected range. After commit, navigation and highlighting update from the new selection. GetSelection reads that selection through the plugin's query contract.

One owner per state

Every independently stateful plugin defines its state and the operations that change it. The runtime coordinates commits across owners without requiring a shared schema for all plugin state. The same ownership contract applies to built-in and third-party plugins.

Adapt through contracts

Reusable packages depend on platform-neutral interfaces. Assemblies bind concrete host adapters at composition time. The current Web implementation supplies rendering, input, platform, text-shaping, storage, and transport adapters. Implementations for other hosts can satisfy the same contracts without changing document state, commands, or plugin ownership.

Queries, observations, and capabilities

The editor facade separates current-state reads, subscriptions, and service lookup. These contracts prevent hosts and plugins from depending on another owner's storage, event source, or service construction:

  • Queries perform synchronous, side-effect-free reads of current editor state. A selection query, for example, reads the current range through a typed contract rather than through plugin storage. Ordinary queries return detached snapshots. Reader queries return live, read-only projections whose reads deliver detached values. Neither form publishes subsequent changes.
  • Observations provide typed subscriptions to state changes and transient events, eliminating polling and direct access to plugin event sources. State observations deliver a current snapshot on subscription followed by later values; event observations deliver only future events. Subscriptions return a disposer, and state observations also support getSnapshot().
  • Capabilities expose editor-local services and extension values through typed tokens. Optional services can be shared without extending the editor facade or exposing its DI container. The facade provides a read-only lookup: get returns undefined for an absent capability, while require throws. Returned values follow their own contracts and may expose operations.
await editor.commands.run(commandDefinition, commandInput);
const result = editor.queries.run(queryDefinition, queryInput);
const dispose = editor.observations.subscribe(observationDefinition, listener);
const service = editor.capabilities.get(capabilityDefinition);

Foundation

sear-foundation-* contains contracts and primitives shared by domain, rendering, assembly, and plugin packages. These packages have no dependency on a specific editor type or host platform.

sear-foundation-di provides the container abstraction used to isolate services per editor. The Platform contract in sear-foundation-platform abstracts timers, events, identifiers, and other environment services. Geometry and range packages define portable coordinates and bounds; error and utility packages define shared low-level types.

Assemblies bind concrete platform implementations during editor creation. UI, transport, and persistence policy remain host responsibilities.

Domain

sear-domain-* defines structured document data, sparse matrices, operations, and protocols. These packages specify state representations and change semantics independently of rendering, host adapters, and optional features.

State has one owner

Independently evolving plugins retain separate state contracts. Changes to one plugin's schema or transactions do not require corresponding changes to unrelated plugins. A shared schema for all plugin state would couple those otherwise independent changes.

A state-owning plugin registers its state and mutation commands. It may also contribute field projections, queries, observations, visual readers, export codecs, or OT definitions. DataEngine manages registered data and projections; collaboration and persistence require explicit owner contributions.

Runtime data falls into four categories:

  • Document state contains editor or Workbook content eligible for export.
  • Session state contains contributor-keyed local values retained across editor remounts.
  • Derived state contains computed values, indexes, layout caches, and render projections.
  • Host state contains transport, credentials, dialogs, application UI, and product policy.

Sparse storage represents defaults by absence. A missing alignment or row size uses the current configuration; rendering that default does not create a persisted entry.

The Sheet model

The base Sheet matrix stores cell content. Other plugins own independent state, including dimensions, formatting, merged ranges, formulas, and selection. An assembly composes the selected owners in one editor-local runtime. Structural commands coordinate their typed operations while ownership remains with each plugin.

CellTypeCatalog associates persisted cell types with commit strategies, display text, render registrations, and clipboard codecs. Provider registries expose formula source, calculated values, images, and links as separate projections.

SheetCellVisualReaderRegistry composes visual properties from owner-provided readers. Conflicting property registrations fail during assembly. Grid and merged-cell renderers consume the composed projection without importing individual feature matrices.

Portable operations and protocols

Matrix OT and Rect OT describe changes independently of a view. The collaboration domain manages revisions, pending operations, acknowledgements, catch-up, ordering, and rebasing. Unknown operation families are rejected explicitly.

Render

sear-render-* defines scene, drawing, input, and viewport contracts for visual editors. Host adapters implement platform-specific painting and event handling against those contracts.

RenderContext exposes immediate drawing operations using platform-neutral values. Web Canvas, SVG, and WebGL adapters implement this interface. Retained scene nodes, cell render registries, dirty-region strategies, hit testing, cursor policy, input routing, and scrolling are separate concerns with their own contracts.

TextShaper returns glyph, cluster, caret-stop, and advance data using UTF-16 offsets. Native text-system values remain inside their adapters. Each editor composition root registers one text shaper.

Platform abstracts timers, event registration, resize observation, host-view offsets, identifiers, and warnings. Reusable render packages depend on that contract rather than host globals, allowing different hosts to use the same transaction and plugin layers.

Assembly

sear-assembly-* composes Sheet, Doc, and Workbook runtimes from document state, selected plugins, and host adapters. Each assembly owns command dispatch, service wiring, and editor lifecycle. Plugin-owned state remains with its contributor inside the isolated editor runtime.

Editor facades

createSheetEditor() assembles a single Sheet from document state, selected plugins, a viewport session, and rendering and input adapters. Its facade exposes commands, queries, observations, and runtime capabilities. An embedded rich-text editor can be supplied separately.

createDocEditor() assembles a standalone rich-document runtime. Its facade exposes commands, observations, capabilities, and document accessors such as getDocument() and getText().

createWorkbookEditor() owns Workbook structure and mounts a Sheet editor for the active Sheet. Its facade exposes commands, observations, and capabilities. Structural reads use document; access to the mounted Sheet uses sheets. Each editor facade provides destroy() for runtime teardown.

import {
  createEmptySheetDocumentState, createSheetEditor, type SheetEditorAdapters,
} from '@searsuite/sear-assembly-setup';
import { SelectRangeCommand } from '@searsuite/sear-plugin-selection';

function openSheet(adapters: SheetEditorAdapters) {
  const editor = createSheetEditor({ adapters, sheetState: createEmptySheetDocumentState(20, 10) });
  editor.commands.run(SelectRangeCommand, { anchor: [0, 0], focus: [0, 0] });
  return editor;
}

The host retains the returned editor until its view is unmounted, then calls destroy(). The website Canvas demo provides a complete Web adapter configuration.

Workbook composition

WorkbookDocumentState stores stable Sheet identities, names, order, visibility, and per-Sheet document state. WorkbookSessionState stores the active Sheet identity, detached editor sessions, and local plugin values. Internal references use stable sheetId values; display names can change independently.

Only the active Sheet editor remains mounted after a switch. The Workbook first captures the current Sheet's document and local session state. It then creates the destination Sheet editor from its saved state and destroys the previous editor after the replacement succeeds. Returning to a Sheet restores its captured session, including viewport and selection, without adding those local values to WorkbookDocumentState.

Workbook and Sheet plugins are configured separately. Workbook-owned services, including the formula-function registry and Sheet-keyed collaboration channels, survive Sheet remounts. Structural operations use staged Workbook transactions and history separate from content edits in the mounted Sheet.

Plugins

sear-plugin-* packages provide optional behavior through plugin-owned state, commands, and extension points. Built-in and third-party plugins participate in the same lifecycle and registration contracts.

Registration and ownership

Registration follows explicit lifecycle phases. Plugins may contribute services, state, commands, queries, observations, runtime capabilities, session adapters, visual readers, and document-state codecs. The assembly orders these contributions and releases acquired resources in reverse order on failure or teardown.

State registration establishes ownership, and commands define mutations. Queries provide on-demand reads; observations publish state and event streams; capabilities expose editor-local services and extension values. Session adapters capture local remount state. Collaborative state owners also contribute versioned OT definitions and a durable export codec.

Package placement follows dependency direction:

  • Domain packages define portable values and protocols.
  • Plugin packages own optional stateful behavior.
  • Host adapters implement platform-specific conversion.
  • Assembly packages coordinate composition and lifecycle.

Finite feature values have one canonical enum-like contract. Consumers import that definition instead of maintaining independent value lists or literal unions.

History, collaboration, and export

History records successful commits. Sheet transaction owners supply their history adapters; Workbook history records structural transactions separately. Undo and redo update their stacks only after inverse or replay operations succeed.

Collaborative transaction owners declare a stable identifier, versioned encode/decode/invert functions, and the required pairwise transform relations. Editor plugins connect committed transactions to host transport. Revision tracking and rebasing remain in the collaboration domain.

Durable export uses owner-provided codecs registered by stateful plugins. Runtime snapshot commands capture state for in-memory remounting; persisted files require explicit versioned codecs. Third-party plugins use the same export registry as built-in plugins.

Host application

A host application selects the editor assembly and plugins, supplies concrete platform adapters, and owns the surrounding interface and transport. Browser, headless, desktop, and other hosts can use the same domain and editor contracts.

A visual host provides a render context, text shaper, input and viewport adapters, and an implementation of Platform for timers, events, and environment services. Dialogs, credentials, persistence, file downloads, and collaboration transport remain host responsibilities. Reusable packages access them through abstract contracts.

The browser Canvas example illustrates host assembly with Web adapters. Headless assembly does not require visual host adapters.