@bendyline/squisq
v2.3.0
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, 25 block templates, animation/theme utilities |
| markdown | Markdown parsing, stringifying, AST types (MarkdownDocument), tree utilities |
| spatial | Haversine distance, 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);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 { encodeGeohash, haversineDistance } 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);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 |
