Artifact-scoped Markdown documents, structured block documents, and knowledge-index utilities for SQLRooms.
import {
BlockDocumentArtifact,
BlockDocumentsSliceConfig,
BlockDocumentChartRendererProvider,
BlockDocumentStatefulBlockRendererProvider,
DocumentsSliceConfig,
buildKnowledgeIndex,
createBlockDocumentCommands,
createBlockDocumentsSlice,
createDocumentCommands,
createDocumentsSlice,
createMarkdownDocumentBlockDefinition,
} from '@sqlrooms/documents';
import {createDocumentsCrdtMirror} from '@sqlrooms/documents/crdt';
import {
createArtifactTypeFromStatefulBlock,
defineArtifactTypes,
} from '@sqlrooms/artifacts';
const documentBlockDefinition = createMarkdownDocumentBlockDefinition();
const artifactTypes = defineArtifactTypes({
document: createArtifactTypeFromStatefulBlock(documentBlockDefinition),
'block-document': {
label: 'Block Document',
defaultTitle: 'Block Document',
component: BlockDocumentArtifact,
onCreate: ({artifactId, store}) => {
store.getState().blockDocuments.ensureBlockDocument(artifactId);
},
onEnsure: ({artifactId, store}) => {
store.getState().blockDocuments.ensureBlockDocument(artifactId);
},
onDelete: ({artifactId, store}) => {
store.getState().blockDocuments.removeBlockDocument(artifactId);
},
},
});
const roomStore = createRoomStore(
persistSliceConfigs(
{
name: 'my-room',
sliceConfigSchemas: {
documents: DocumentsSliceConfig,
blockDocuments: BlockDocumentsSliceConfig,
},
},
(set, get, store) => ({
...createDocumentsSlice()(set, get, store),
...createBlockDocumentsSlice({
onDeleteOwnedStatefulBlock: ({
blockType,
blockInstanceId,
getState,
}) => {
if (blockType === 'dashboard') {
getState().mosaicDashboard.removeDashboard(blockInstanceId);
}
},
})(set, get, store),
}),
),
);MarkdownDocument uses the Tiptap-backed MarkdownDocumentEditor. It keeps
Markdown as the controlled value, renders a rich document editing surface, and
keeps the existing CodeMirror source panel for direct Markdown edits.
MarkdownDocumentEditor is also exported as a reusable controlled editor:
<MarkdownDocumentEditor
value={markdown}
assets={assets}
onChange={setMarkdown}
/>The rich editor is the primary surface. The optional Markdown source panel can be opened alongside it and edits the same canonical Markdown string:
<MarkdownDocumentEditor
value={markdown}
onChange={setMarkdown}
sourcePanelOpen={showSource}
onSourcePanelOpenChange={setShowSource}
/>Document Markdown can reference document-owned assets with asset:// URLs:
Pass the document asset map to MarkdownDocumentEditor to render those links as
browser-loadable image data while preserving the canonical asset:// link in
Markdown source. MarkdownDocument handles this automatically for artifacts
stored in the documents slice.
The documents slice exposes upsertAsset, removeAsset, and getAsset for
managing image assets alongside Markdown content. SVG assets may use utf8 or
base64 encoding; PNG assets must use base64 encoding.
createBlockDocumentsSlice() exposes structured state for artifact types
backed by composable blocks: text, lists, images, standalone Mosaic/vgplot
charts, and direct stateful blocks such as dashboards, pivots, or Markdown
documents.
The shared block vocabulary lives in @sqlrooms/blocks.
@sqlrooms/documents builds on those contracts with the concrete
Tiptap-backed BlockDocument editor, persistence slice, commands, and AI
authoring helpers.
Block documents persist Tiptap/ProseMirror JSON as their canonical content and provide block DTO helpers for command and AI authoring surfaces:
import {
BlockDocumentsSliceConfig,
createBlockDocumentsSlice,
} from '@sqlrooms/documents';
const roomStore = createRoomStore(
persistSliceConfigs(
{
name: 'my-room',
sliceConfigSchemas: {
blockDocuments: BlockDocumentsSliceConfig,
},
},
(set, get, store) => ({
...createBlockDocumentsSlice()(set, get, store),
}),
),
);The slice can create block documents, replace the Tiptap JSON body, and append/insert/update/remove/reorder top-level blocks. Supported block DTOs include headings, paragraphs, lists, todos, images, chart images, standalone chart blocks, and direct stateful blocks.
BlockDocumentArtifact and BlockDocumentEditor provide the first rich
editor surface for this structured state. BlockDocumentArtifact injects an
editable, non-movable title node into the Tiptap document and reports title
changes through onTitleChange, so hosts can keep artifact metadata and tab
labels in sync. The editor owns Tiptap nodes for SQLRooms custom blocks, but
chart and stateful block rendering are host-provided so @sqlrooms/documents
does not import Mosaic, pivot, or other feature packages:
<BlockDocumentChartRendererProvider renderer={MosaicBlockDocumentChartRenderer}>
<BlockDocumentStatefulBlockRendererProvider
renderers={{
dashboard: DashboardBlockRenderer,
pivot: PivotBlockRenderer,
}}
blockTypes={[
{
blockType: 'dashboard',
label: 'Dashboard',
description: 'Interactive dashboard',
createNode: (blockId) => ({
type: 'blockDocumentStatefulBlock',
attrs: {
id: blockId,
blockType: 'dashboard',
blockInstanceId: createDashboardBlockState(blockId),
ownership: 'owned',
title: 'Dashboard',
caption: '',
},
}),
},
]}
>
<BlockDocumentArtifact
artifactId={blockDocumentArtifactId}
title="Worksheet"
onTitleChange={(title) =>
renameBlockDocument(blockDocumentArtifactId, title)
}
/>
</BlockDocumentStatefulBlockRendererProvider>
</BlockDocumentChartRendererProvider>If no renderer is registered, chart and stateful blocks render a clear
unsupported state while preserving their Tiptap JSON attributes. blockTypes
controls the host-specific entries shown in the plus menu.
When a block is converted through the handle menu, custom createNode
callbacks receive an optional {initialText} value with the source block text;
hosts can use it to seed stateful blocks such as embedded Markdown documents.
Stateful block types can opt into persisted vertical resizing with
resizableHeight, defaultHeight, minHeight, and maxHeight; the editor
stores the resulting height on the block node and renders a bottom resize
handle just below the block for writable documents. Interactive blocks can also opt into
requireScrollModifier; ordinary wheel gestures then keep scrolling the
document and show a short hint, while Cmd+scroll on macOS or Ctrl+scroll
elsewhere scrolls nested overflow regions inside the block. Use
scrollHintLabel to customize the hint target text.
Use a statefulBlock block when the document should host a stateful SQLRooms
surface directly, without wrapping it in an artifact shell:
blockDocuments.appendBlocks(blockDocumentArtifactId, [
{
id: 'pivot-block',
type: 'statefulBlock',
blockType: 'pivot',
blockInstanceId: 'pivot-instance-1',
ownership: 'owned',
title: 'Embedded Pivot Table',
},
]);Hosts provide renderers through BlockDocumentStatefulBlockRendererProvider:
<BlockDocumentStatefulBlockRendererProvider
renderers={{
pivot: PivotBlockRenderer,
dashboard: DashboardBlockRenderer,
}}
blockTypes={[
{
blockType: 'pivot',
label: 'Pivot Table',
description: 'Embedded pivot table',
},
]}
>
<BlockDocumentArtifact
artifactId={blockDocumentArtifactId}
title="Embedded Report"
onTitleChange={(title) =>
renameBlockDocument(blockDocumentArtifactId, title)
}
/>
</BlockDocumentStatefulBlockRendererProvider>Top-level artifacts should wrap stateful blocks or block containers at the workspace/tab layer. Block documents host the stateful block directly instead of embedding an artifact shell.
Owned stateful blocks are lifecycle-managed by the host app. Pass
onCreateOwnedStatefulBlock to initialize feature state when a new owned block
reference appears, and onDeleteOwnedStatefulBlock to clean it up when an owned
block is removed from a document or when its owning block document is deleted.
Blocks with ownership: 'shared' or ownership: 'external' are not cleaned up
by the documents slice.
Hosts can also pass onRenameOwnedStatefulBlock to synchronize block title
changes into the backing feature state. Captions stay local to the blocks
document. Stateful block renderers receive onTitleChange and
onCaptionChange callbacks when a writable document lets the embedded surface
edit its own block metadata.
The editor normalizes pasted or duplicated owned stateful blocks by assigning
fresh top-level block IDs and fresh blockInstanceId values when a duplicate
owned instance would otherwise point at the same backing state.
Standalone chart blocks are meant for focused, in-document charts. They store
the target tableName, a Mosaic ChartConfig, an optional caption, and an
optional selectionGroupId:
blockDocuments.appendBlocks(blockDocumentArtifactId, [
{
id: 'revenue-histogram',
type: 'chart',
tableName: 'sales',
config: {
chartType: 'histogram',
settings: {field: 'revenue'},
},
selectionGroupId: 'overview',
caption: 'Revenue distribution',
},
]);Hosts can render these blocks with the same Mosaic/vgplot chart implementation
and settings UI used inside dashboard panels, without embedding a full
dashboard. Charts with the same selectionGroupId in one block document share
a crossfilter selection. Charts without a group get independent
document/block-scoped selections.
Use a statefulBlock block when the document needs a multi-panel interactive
dashboard. The block instance id should map to dashboard state in the host app's
Mosaic slice, while the top-level artifact shell remains optional for workspace
navigation.
Standalone chart blocks are best for one chart with local context. Dashboard stateful blocks are best for coordinated multi-panel views, richer dashboard layout, or when dashboard AI tools are the natural authoring path.
createDocumentCommands() registers AI- and palette-friendly commands for
document artifacts:
document.listdocument.getdocument.createdocument.set-markdowndocument.append-markdown
createBlockDocumentCommands() registers commands for structured blocks
document artifacts. By default the command IDs are:
block-document.listblock-document.getblock-document.createblock-document.append-blocksblock-document.insert-blocksblock-document.update-blockblock-document.remove-blockblock-document.move-blockblock-document.create-chart-blockblock-document.create-stateful-block
Hosts can pass artifactType, artifactLabel, and commandNamespace options
to expose the same command surface under product-specific names while keeping
the package API generic.
Hosts can pass statefulBlockTypes to expose supported feature-backed block
types to block-document.create-stateful-block.
Structured block payloads may include an optional intent string. Use it for
the durable natural-language purpose of an agent- or command-created block,
such as the question a chart should answer or the job an embedded dashboard
should serve. It is persisted with the block, unlike transient mutation
metadata.
@sqlrooms/documents/crdt exposes Loro Mirror bindings for document state:
createCrdtSlice({
mirrors: {
documentState: createDocumentsCrdtMirror(),
},
});createDocumentsCrdtMirror() syncs Markdown document bodies, block document
Tiptap JSON content, document-owned assets, standalone chart block configs,
block document/document artifact metadata, and document artifact tab order.
The current artifact selection is kept local.
By default, the mirror treats block-document artifacts as block documents.
Hosts with their own artifact type names can pass
blockDocumentArtifactTypes, for example:
createDocumentsCrdtMirror({
blockDocumentArtifactTypes: ['report'],
});Hosted dashboard state should continue to use the host app's Mosaic persistence, or a future Mosaic-specific CRDT mirror.
buildKnowledgeIndex is a pure derived index. It does not persist data.
const index = buildKnowledgeIndex({
documents: roomStore.getState().documents.config,
artifacts: roomStore.getState().artifacts.config,
});It extracts [[Document Title]] wikilinks, body hashtags such as #metrics,
and optional frontmatter tags. Links are resolved against document artifact
titles. Missing or ambiguous titles are reported as unresolved links.
- BlockDocumentAgentPlanStep
- BlockDocumentAgentResult
- BlockDocumentArtifactProps
- BlockDocumentChartRendererProps
- BlockDocumentChartRenderer
- BlockDocumentChartRendererProviderProps
- BlockDocumentCommandSuffix
- BlockDocumentStatefulBlockCommandContext
- BlockDocumentStatefulBlockCommandType
- CreateBlockDocumentCommandsOptions
- BlockDocumentEditorContentProps
- BlockDocumentEditorContextValue
- BlockDocumentEditorRootProps
- BlockDocumentToolbarProps
- BlockDocumentMark
- BlockDocumentNode
- BlockDocumentContent
- BlockDocument
- BlockDocumentsSliceConfig
- BlockDocumentHeadingBlock
- BlockDocumentParagraphBlock
- BlockDocumentListBlock
- BlockDocumentTodoBlock
- BlockDocumentImageBlock
- BlockDocumentChartImageBlock
- BlockDocumentChartBlock
- BlockDocumentStatefulBlockBlock
- BlockDocumentBlock
- BlockDocumentStatefulBlockRendererProps
- BlockDocumentStatefulBlockRenderer
- BlockDocumentStatefulBlockRenderers
- BlockDocumentStatefulBlockCreateNodeOptions
- BlockDocumentStatefulBlockType
- BlockDocumentStatefulBlockRendererProviderProps
- BlockDocumentMutationOrigin
- BlockDocumentMutationMetadata
- BlockDocumentSyncMetadata
- BlockDocumentStatefulBlockReference
- BlockDocumentOwnedStatefulBlockReference
- BlockDocumentOwnedStatefulBlockDeleteContext
- BlockDocumentOwnedStatefulBlockCreateContext
- BlockDocumentOwnedStatefulBlockRenameContext
- BlockDocumentsSliceState
- CreateBlockDocumentsSliceProps
- DocumentAssetInput
- DocumentsSliceState
- CreateDocumentsSliceProps
- DocumentAsset
- MarkdownDocumentState
- DocumentsSliceConfig
- MarkdownDocumentBlockRenderProps
- CreateMarkdownDocumentBlockDefinitionOptions
- MarkdownDocumentEditorContentProps
- MarkdownDocumentEditorMode
- MarkdownDocumentEditorRootProps
- MarkdownDocumentEditorToolbarProps
- DocumentLink
- UnresolvedDocumentLink
- DocumentTag
- KnowledgeIndex
- BuildKnowledgeIndexProps
- BLOCK_DOCUMENT_AGENT_TOOL_NAME
- BlockDocumentArtifact
- BlockDocumentChartRendererProvider
- BLOCK_DOCUMENT_COMMAND_SUFFIXES
- BlockDocumentEditor
- BlockDocumentEditorContent
- BlockDocumentEditorRoot
- BlockDocumentToolbar
- BlockDocumentMark
- BlockDocumentNode
- BlockDocumentContent
- BlockDocument
- BlockDocumentsSliceConfig
- BlockDocumentHeadingBlock
- BlockDocumentParagraphBlock
- BlockDocumentListBlock
- BlockDocumentTodoBlock
- BlockDocumentImageBlock
- BlockDocumentChartImageBlock
- BlockDocumentChartBlock
- BlockDocumentStatefulBlockBlock
- BlockDocumentBlock
- BlockDocumentStatefulBlockRendererProvider
- DocumentAsset
- MarkdownDocumentState
- DocumentsSliceConfig
- MarkdownDocument
- MarkdownDocumentEditor
- MarkdownDocumentEditorContent
- MarkdownDocumentEditorRoot
- MarkdownDocumentEditorToolbar
- useBlockDocumentChartRenderer
- createBlockDocumentCommandIds
- createBlockDocumentCommands
- useBlockDocumentEditorContext
- normalizeBlockDocumentContent
- createDefaultBlockDocumentBlockId
- createEmptyBlockDocumentContent
- blockDocumentBlockToNode
- blockDocumentNodeToBlock
- blockDocumentContentToBlocks
- useBlockDocumentStatefulBlockRenderer
- useBlockDocumentStatefulBlockTypes
- createDefaultBlockDocumentsConfig
- createBlockDocumentsSlice
- createDefaultDocumentsConfig
- createDocumentsSlice
- createMarkdownDocumentBlockDefinition
- createDocumentCommands
- buildKnowledgeIndex
- useStoreWithBlockDocuments
- useStoreWithDocuments
Renames and re-exports DocumentAsset
Renames and re-exports DocumentsSliceConfig
Renames and re-exports MarkdownDocumentState
Renames and re-exports BlockDocumentBlock
Renames and re-exports BlockDocument
Renames and re-exports BlockDocumentContent
Renames and re-exports BlockDocumentMark
Renames and re-exports BlockDocumentNode
Renames and re-exports BlockDocumentsSliceConfig
