@bendyline/squisq
v2.12.5
Published
Headless utilities for doc/block rendering, spatial math, Markdown, and storage
Maintainers
Readme
@bendyline/squisq
Headless utilities for doc/block rendering, spatial math, Markdown parsing, and storage. Framework-agnostic — runs in the browser or Node.js with zero framework dependencies.
Part of the Squisq monorepo.
Install
npm install @bendyline/squisqWhat's Inside
| Module | Description |
| ------------ | -------------------------------------------------------------------------------- |
| schemas | Type definitions — Doc, BlockTemplate, Viewport, Theme, LayoutStrategy |
| doc | Template registry, 26 block templates, animation/theme utilities |
| markdown | Markdown parsing, stringifying, AST types (MarkdownDocument), tree utilities |
| spatial | Haversine distance and bearing, great-circle paths, Geohash encode/decode |
| storage | StorageAdapter interface, Memory + LocalStorage + LocalForage adapters |
New in this release
The Squiggly Square markdown standard (see docs/SquigglySquare.md) gained several authoring capabilities:
- Standalone annotations — a top-level paragraph that is exactly
{[templateName …]}becomes a heading-less template block; the body that follows it is its contents. - Inline attribute coercion —
{[map center="47.6,-122.3" zoom=9]}and{[twoColumn left="Espresso|Bold"]}now render without a data fence (typed viaTEMPLATE_INPUT_DESCRIPTORS). - Validation additions — new
unknown-input/invalid-input-value/missing-inputwarnings pluspossible-data-fence/conflicting-annotation-keyinfo diagnostics (a newinfoseverity). - YAML data fences now accept one level of nested mappings (a
mapcentercan be pure YAML). - Multi-line frontmatter —
squisq-custom-templates/squisq-custom-themescan be authored as pretty JSON via YAML literal block scalars (key: |-). - Custom-template tokens v2 (see
docs/SquigglySquare.md, "Registering custom templates") —{attr:key}, pipe defaults on every token, and per-layerrepeatwith{item}/{index}tokens.
New @bendyline/squisq/doc exports powering the above: coerceTemplateParams, lintTemplateParams, TEMPLATE_INPUT_DESCRIPTORS, and replaceDataFence.
Subpath Imports
Import only what you need:
import type { Doc, BlockTemplate, Theme } from '@bendyline/squisq/schemas';
import { materializeBlockLayers, expandDocBlocks } from '@bendyline/squisq/doc';
import { parseMarkdown, stringifyMarkdown } from '@bendyline/squisq/markdown';
import { encodeGeohash, haversineDistance } from '@bendyline/squisq/spatial';
import { LocalStorageAdapter } from '@bendyline/squisq/storage';Or import everything from the root:
import { parseMarkdown, haversineDistance, materializeBlockLayers } from '@bendyline/squisq';Quick Examples
Parse Markdown
import { parseMarkdown, stringifyMarkdown } from '@bendyline/squisq/markdown';
const doc = parseMarkdown('# Hello\n\nSome content');
console.log(doc.children); // AST nodes
const md = stringifyMarkdown(doc);Condense imported Markdown
condenseMarkdownSource(source) removes table alignment padding, shortens
GFM delimiter rows, and reduces decorative thematic breaks to three markers.
It preserves table cells and alignment, code, math, HTML, links, footnotes,
prose, and frontmatter. The output is reparsed and structurally compared with
the original before returning; the API throws if equivalence cannot be proven.
It is idempotent and does not summarize articles or remove citations.
Run condensation after link/image edits, which can re-serialize padded tables.
import { condenseMarkdownSource } from '@bendyline/squisq/markdown';
const compactMarkdown = condenseMarkdownSource(importedMarkdown);The shared source-transform registry also exposes condense, available from
the editor's Transform menu and squisq transform file.md --ops condense.
Author an ASCII Timeline
Timeline fences are source-of-truth markdown. Unicode marker rails are
auto-detected; the explicit timeline language also accepts sparse/ASCII
forms. Repeat rails for multiple tracks and use event ids in branch: lines
for forks or cross-track links. Add longer callout text after ::; optional
side=above|below, descriptionSide=above|below, and callout=false metadata
control placement or suppress labels on dense cadence points.
## Runtime timing
```timeline
Kernel: ● T28 :: Δ28 {#t28 side=above descriptionSide=below} ─────● T29 {#t29} ─────● T30 {#t30} ───►
Client: ──────────────────● Frame A {#frame-a} ───● Frame B {#frame-b} ───►
branch: t29 -> frame-a : interpolate
```markdownToDoc() derives globally aligned event positions while preserving
the original fence byte-for-byte for markdown round-trips.
Spatial Utilities
import {
destinationPoint,
encodeGeohash,
haversineDistance,
sampleGreatCircle,
} from '@bendyline/squisq/spatial';
const distanceKm = haversineDistance({ lat: 47.6, lng: -122.3 }, { lat: 37.7, lng: -122.4 });
const hash = encodeGeohash(47.6, -122.3, 7);
// Points along the shortest route, and a position projected 5 km due east.
const route = sampleGreatCircle({ lat: 47.6, lng: -122.3 }, { lat: 51.5, lng: -0.1 }, 20);
const ahead = destinationPoint({ lat: 47.6, lng: -122.3 }, 90, 5_000);Materialize a Block
import { materializeBlockLayers } from '@bendyline/squisq/doc';
const result = materializeBlockLayers(block, {
theme,
viewport,
customTemplates: doc.customTemplates,
});
render(result.layers);
if (result.diagnostic) report(result.diagnostic);The canonical materializer handles authored layers, built-in and document-scoped
templates, theme motion, and persistent layers. Failed templates produce a
visible fallback plus a structured diagnostic by default; pass
failureMode: 'empty' when a host deliberately wants no fallback UI.
Theme System
import {
compileTheme,
createThemeRegistry,
getAvailableThemes,
resolveTheme,
} from '@bendyline/squisq/schemas';
const themes = getAvailableThemes(); // 11 built-in themes
const theme = resolveTheme('cinematic');
// Host-level custom themes live in an explicit caller-owned registry.
const registry = createThemeRegistry([
compileTheme({ id: 'brand', name: 'Brand', seedColors: { primary: '#6633cc' } }),
]);
const brand = resolveTheme('brand', registry);Related Packages
| Package | Description | | ---------------------------------------------------------------------------------------------- | ------------------------------------------- | | @bendyline/squisq-react | React components for rendering docs | | @bendyline/squisq-formats | DOCX, PDF, HTML import/export | | @bendyline/squisq-editor-react | React editor with raw/WYSIWYG/preview modes |
