Skip to content

feat: Blocks and BlockDocument - #666

Merged
ilyabo merged 60 commits into
mainfrom
analysis-artifacts
May 30, 2026
Merged

ilyabo merged 60 commits into
mainfrom
analysis-artifacts

Conversation

@ilyabo

@ilyabo ilyabo commented May 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Block-based worksheets with rich blocks (charts, dashboards, pivots, images, rich text) and inline stateful blocks
    • In-editor block controls: insert, move, convert, delete, resize, and crossfilter-aware chart blocks
    • Inline embedded dashboards/pivots with title editing and a worksheet chart view + settings panel
    • Artifact chooser UI for creating/selecting worksheets, dashboards, pivots
  • Documentation

    • Expanded guides for agents, block documents, stateful-block adapters, and package catalog
  • UX

    • Tab activation callback and visibility controls for embedded artifacts; modifier-scroll overlay UI

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@ilyabo, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 2 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fffd3acf-8a41-4ffa-b058-a8f7583ab29a

📥 Commits

Reviewing files that changed from the base of the PR and between a14b307 and c400d59.

📒 Files selected for processing (12)
  • AGENTS.md
  • contributing/adding-features.md
  • dev-docs/contributing/adding-features.md
  • dev-docs/contributing/architecture.md
  • dev-docs/contributing/cursor-cloud.md
  • dev-docs/contributing/patterns.md
  • dev-docs/contributing/python.md
  • dev-docs/contributing/troubleshooting.md
  • dev-docs/contributing/typescript.md
  • dev-docs/tasks/cells-blocks-convergence.md
  • dev-docs/tasks/root-block-artifacts.md
  • dev-docs/tasks/stateful-blocks-artifact-shells.md
📝 Walkthrough

Walkthrough

Adds block-document artifacts/editor (TipTap nodes/views), stateful-block contracts and artifact bridge, worksheet/pivot artifact support in CLI/UI, visibility-aware artifacts/tabs, block-document commands/AI helpers, CRDT mirror enhancements, Mosaic/Pivot adapters, UI primitives, docs, and tests.

Changes

Block Documents + Stateful Blocks

Layer / File(s) Summary
Block-document core: slice, editor, node views, commands, CRDT
packages/documents/src/..., packages/documents/__tests__/...
Adds BlockDocumentsSlice, TipTap editor and controls, node views (chart/image/rich-text/stateful-block), block<->node conversion helpers, BlockDocumentEditor (Root/Content/Toolbar), command factory (createBlockDocumentCommands), AI instruction helpers, CRDT mirror support for blockDocuments, and tests.
Stateful-block contracts & bridge
packages/blocks/*, packages/artifacts/src/ArtifactTypes.ts
Introduces @sqlrooms/blocks contracts (StatefulBlockDefinition, contexts) and createArtifactTypeFromStatefulBlock to map stateful block definitions into artifact-type lifecycle/component wiring.
Worksheet & CLI UI integration
apps/sqlrooms-cli-ui/src/*
Adds worksheet artifact type, pivot support, WorksheetArtifact panel, worksheet block renderers (dashboard/pivot/document/chart), CLI create/select wiring, artifact chooser UI, stateful-block artifact configs, and store wiring (createBlockDocumentsSlice, command registration).
Artifacts visibility & tabs
packages/artifacts/src/*
Adds visibility (workspace
Mosaic & Pivot adapters / chart plumbing
packages/mosaic/src/*, packages/pivot/src/*
Adds Mosaic dashboard/pivot stateful-block definitions and adapters, MosaicChartView/MosaicChartSettingsPanel, key-based chart retainer/issue APIs, and pivot block definition + exports.
UI primitives
packages/ui/src/*
Adds ModifierScrollOverlay and TabStrip activation callback support used by stateful-block node views and editor overlays.
Docs & dev plans
AGENTS.md, dev-docs/tasks/*, package READMEs, docs/packages.md
Extensive documentation and architecture/task plans for block documents, stateful-block shells, convergence plans, README updates across packages.

Sequence Diagram

sequenceDiagram
  participant Client
  participant CLI_Commands
  participant RoomStore
  participant CRDT_Mirror
  participant Renderer
  Client->>CLI_Commands: create/select artifact (worksheet/pivot)
  CLI_Commands->>RoomStore: createArtifact / ensureArtifact (worksheet/pivot) -> ensure backing state (ensureBlockDocument / ensurePivot / ensureState)
  RoomStore-->>Renderer: provide blocks, assets, artifact metadata
  Renderer->>RoomStore: onChange(content/title/config)
  RoomStore->>CRDT_Mirror: mirror documents, blockDocuments, artifacts (including visibility/parentArtifactId)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • dmitriy-kostianetskiy

Poem

A rabbit taps on blocks of light,
Worksheets, pivots, dashboards bright.
Stateful shells and docs take flight,
Commands hum steady through the night.
Tabs stay tidy, mirrors sing—🐇✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch analysis-artifacts

@ilyabo ilyabo changed the title Analysis artifacts feat: Analysis artifacts May 28, 2026
@ilyabo ilyabo changed the title feat: Analysis artifacts feat: Blocks and BlockDocument May 29, 2026
@ilyabo
ilyabo force-pushed the analysis-artifacts branch from 1ad2eb3 to fe210c9 Compare May 30, 2026 17:02
@ilyabo
ilyabo marked this pull request as ready for review May 30, 2026 17:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/sqlrooms-cli-ui/src/createDashboardCommands.ts (1)

85-99: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pivot title diverges from the artifact title on create.

The artifact is created with a de-duplicated unique title, but ensurePivot receives the raw title ?? group. Two pivots created in a row will get distinct tab titles (e.g. "Pivot Table 2") yet identical pivot-state titles ("Pivot Table"). Note the artifact.select path already uses artifact.title, so behavior is inconsistent between create and select. Reuse the computed unique title.

🐛 Proposed fix
     execute: ({getState}, input) => {
       const {title} = (input as CreateArtifactCommandInput | undefined) ?? {};
       const state = getState();
+      const uniqueTitle = getUniqueArtifactTitle(state, title ?? group);
       const artifactId = state.artifacts.createArtifact({
         type: artifactType,
-        title: getUniqueArtifactTitle(state, title ?? group),
+        title: uniqueTitle,
       });
       if (artifactType === 'notebook') {
         state.notebook.ensureArtifact(artifactId);
       } else if (artifactType === 'worksheet') {
         state.blockDocuments.ensureBlockDocument(artifactId);
       } else if (artifactType === 'document') {
         state.documents.ensureDocument(artifactId);
       } else if (artifactType === 'canvas') {
         state.canvas.ensureArtifact(artifactId);
       } else if (artifactType === 'pivot') {
-        state.pivot.ensurePivot(artifactId, {title: title ?? group});
+        state.pivot.ensurePivot(artifactId, {title: uniqueTitle});
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/sqlrooms-cli-ui/src/createDashboardCommands.ts` around lines 85 - 99,
The pivot's state title is using the raw title (title ?? group) while the
artifact was created with the de-duplicated title from getUniqueArtifactTitle,
causing mismatched titles; fix by capturing the computed title (call
getUniqueArtifactTitle once, e.g. const uniqueTitle =
getUniqueArtifactTitle(state, title ?? group)), pass uniqueTitle into
state.artifacts.createArtifact and then use that same uniqueTitle when calling
state.pivot.ensurePivot(artifactId, { title: uniqueTitle }); reference:
getUniqueArtifactTitle, state.artifacts.createArtifact, artifactId,
artifactType, state.pivot.ensurePivot.
🧹 Nitpick comments (10)
apps/sqlrooms-cli-ui/src/store.ts (1)

516-558: ⚡ Quick win

Consider logging when stateful block config is not found.

The onCreateOwnedStatefulBlock, onDeleteOwnedStatefulBlock, and onRenameOwnedStatefulBlock handlers silently skip operations when the blockType is not found in STATEFUL_BLOCK_ARTIFACT_CONFIGS (via optional chaining). This could hide bugs if a block type has a typo or isn't properly registered.

Consider adding debug logging when config is undefined to aid troubleshooting, or validate the blockType earlier in the call chain.

Example with debug logging
 onCreateOwnedStatefulBlock: ({
   blockInstanceId,
   blockType,
   getState,
   title,
 }) => {
   const config =
     STATEFUL_BLOCK_ARTIFACT_CONFIGS[
       blockType as keyof typeof STATEFUL_BLOCK_ARTIFACT_CONFIGS
     ];
+  if (!config) {
+    console.debug(`No stateful block config found for blockType: ${blockType}`);
+    return;
+  }
-  config?.ensureState(
+  config.ensureState(
     getState(),
     blockInstanceId,
     title ?? config.embeddedTitle,
   );
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/sqlrooms-cli-ui/src/store.ts` around lines 516 - 558, The handlers
onCreateOwnedStatefulBlock, onDeleteOwnedStatefulBlock and
onRenameOwnedStatefulBlock currently silently no-op when
STATEFUL_BLOCK_ARTIFACT_CONFIGS[blockType] is undefined; update each handler to
detect when config is falsy and emit a debug/warn log (e.g., console.warn or an
existing logger) including the blockType and blockInstanceId (and title where
relevant) so missing or misspelled block types are visible, then proceed with
the existing calls to config.ensureState, config.deleteState, or
config.renameState when config is present.
packages/documents/src/BlockDocumentEditor/BlockDocumentEditorRoot.tsx (1)

290-298: ⚡ Quick win

Memoize the context value to keep the provider stable.

contextValue (and the inline generateBlockId arrow) is allocated on every render, so every BlockDocumentEditorContext consumer re-renders even when nothing it depends on changed. Wrapping it in useMemo keeps the context reference stable.

♻️ Proposed refactor
-  const contextValue: BlockDocumentEditorContextValue = {
-    editor,
-    documentId,
-    value: normalizedValue,
-    assets,
-    onChange,
-    readOnly,
-    generateBlockId: () => generateBlockIdRef.current(),
-  };
+  const contextValue: BlockDocumentEditorContextValue = useMemo(
+    () => ({
+      editor,
+      documentId,
+      value: normalizedValue,
+      assets,
+      onChange,
+      readOnly,
+      generateBlockId: () => generateBlockIdRef.current(),
+    }),
+    [editor, documentId, normalizedValue, assets, onChange, readOnly],
+  );

As per coding guidelines: "Keep React context thin and stable."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/documents/src/BlockDocumentEditor/BlockDocumentEditorRoot.tsx`
around lines 290 - 298, The contextValue object (and the inline generateBlockId
arrow) is recreated on every render causing unnecessary re-renders; wrap the
BlockDocumentEditorContextValue creation in useMemo so the provider value stays
referentially stable, e.g. memoize contextValue and inside it keep
generateBlockId: () => generateBlockIdRef.current(), and list stable
dependencies [editor, documentId, normalizedValue, assets, onChange, readOnly]
so consumers only update when those actually change.
packages/documents/src/crdt.ts (1)

82-104: Block-document content stored as an opaque schema.Any() blob.

Storing the full ProseMirror body under schema.Any() means concurrent edits to the same block document body don't merge field-by-field — it's effectively whole-content last-write-wins, so simultaneous edits can clobber each other. This matches the existing markdown-snapshot approach and the in-file TODO, so it's consistent for now; just flagging that block-document bodies are typically richer/longer than a markdown string, which raises the practical likelihood of lost concurrent edits before the planned loro-prosemirror migration lands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/documents/src/crdt.ts` around lines 82 - 104, The blockDocuments
entry uses content: schema.Any(), which stores the entire ProseMirror body as an
opaque blob and causes whole-document last-write-wins; change the schema to a
small structured wrapper to avoid clobbering and pave the loro-prosemirror
migration: replace content: schema.Any() with content: schema.LoroMap({ type:
schema.String(), body: schema.Any() }) (or another minimal typed map) in the
blockDocuments schema so edits can be scoped/validated and later replaced by the
full loro-prosemirror schema; update any create/read usage of blockDocuments to
access content.body and add a TODO referencing loro-prosemirror for the future
migration.
dev-docs/tasks/root-block-artifacts.md (1)

3-16: ⚡ Quick win

Make superseded status unambiguous by archiving/removing the active checklist.

The file says it is superseded, but the unchecked staged todo list reads like current work. Consider replacing the checklist with a short “Archived plan” note and a pointer to dev-docs/tasks/stateful-blocks-artifact-shells.md to avoid accidental execution of stale tasks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dev-docs/tasks/root-block-artifacts.md` around lines 3 - 16, Remove the
active "Todo Checklist" block from this superseded document and replace it with
a short archival note indicating this doc is superseded; include a clear
one-line pointer to the canonical file stateful-blocks-artifact-shells.md and a
brief sentence like "See stateful-blocks-artifact-shells.md for the current
plan" so readers won't act on the stale checklist; target the "Todo Checklist"
section header to locate and delete the unchecked checklist items.
packages/artifacts/src/artifactTabs.tsx (1)

609-613: 💤 Low value

Minor: Inline callback creates new function on each render.

The onSelect callback creates a new inline function on every render. While this is unlikely to cause performance issues in practice, consider memoizing it with useCallback if the TabStrip component is sensitive to prop changes.

♻️ Optional memoization
+  const handleSelect = useCallback((artifactId: string) => {
+    artifactTabs.selectArtifact(artifactId);
+    onSelectArtifact?.(artifactId);
+  }, [artifactTabs, onSelectArtifact]);
+
   <TabStrip
     {...props}
     tabs={artifactTabs.tabs}
     openTabs={artifactTabs.openTabs}
     selectedTabId={artifactTabs.selectedTabId}
     closeable={closeable}
     preventCloseLastTab={preventCloseLastTab}
     onOpenTabsChange={artifactTabs.handleOpenTabsChange}
     onActivate={onActivateArtifact}
-    onSelect={(artifactId) => {
-      artifactTabs.selectArtifact(artifactId);
-      onSelectArtifact?.(artifactId);
-    }}
+    onSelect={handleSelect}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/artifacts/src/artifactTabs.tsx` around lines 609 - 613, The inline
onSelect handler is recreated every render; wrap it in useCallback to
stable-reference the function passed to TabStrip: create a memoized handler
(e.g., const handleSelect = useCallback((artifactId) => {
artifactTabs.selectArtifact(artifactId); onSelectArtifact?.(artifactId); },
[artifactTabs, onSelectArtifact]) ) and pass handleSelect to the TabStrip
onSelect prop so the prop identity remains stable across renders.
packages/documents/src/BlockDocumentStatefulBlockRendererContext.tsx (1)

66-82: ⚡ Quick win

Memoize the context value to keep the provider stable.

supportedBlockTypes and the value object are rebuilt on every render, so all consumers of this context re-render whenever the provider re-renders (e.g. on any store update in WorksheetArtifact), even when renderers/blockTypes are unchanged.

♻️ Memoize derived value
 import {
   createContext,
   useContext,
+  useMemo,
   type FC,
   type PropsWithChildren,
 } from 'react';
 export const BlockDocumentStatefulBlockRendererProvider: FC<
   BlockDocumentStatefulBlockRendererProviderProps
 > = ({renderers = {}, blockTypes, children}) => {
-  const supportedBlockTypes =
-    blockTypes ??
-    Object.keys(renderers).map((blockType) => ({
-      blockType,
-    }));
-
-  return (
-    <BlockDocumentStatefulBlockRendererContext.Provider
-      value={{renderers, blockTypes: supportedBlockTypes}}
-    >
-      {children}
-    </BlockDocumentStatefulBlockRendererContext.Provider>
-  );
+  const value = useMemo(
+    () => ({
+      renderers,
+      blockTypes:
+        blockTypes ??
+        Object.keys(renderers).map((blockType) => ({blockType})),
+    }),
+    [renderers, blockTypes],
+  );
+
+  return (
+    <BlockDocumentStatefulBlockRendererContext.Provider value={value}>
+      {children}
+    </BlockDocumentStatefulBlockRendererContext.Provider>
+  );
 };

As per coding guidelines: "Keep React context thin and stable."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/documents/src/BlockDocumentStatefulBlockRendererContext.tsx` around
lines 66 - 82, BlockDocumentStatefulBlockRendererProvider recreates
supportedBlockTypes and the context value on every render causing unnecessary
consumer re-renders; wrap supportedBlockTypes computation and the Provider value
in useMemo so they only change when their inputs change (memoize
supportedBlockTypes derived from blockTypes or renderers, and memoize the object
passed to BlockDocumentStatefulBlockRendererContext.Provider) referencing the
BlockDocumentStatefulBlockRendererProvider, supportedBlockTypes, renderers,
blockTypes, and BlockDocumentStatefulBlockRendererContext.Provider symbols to
locate and update the code.
apps/sqlrooms-cli-ui/src/workspace/WorksheetChartRenderer.tsx (1)

34-36: 💤 Low value

Misleading helper name. stableStringify is a plain JSON.stringify and does not produce order-stable output (object key order is not normalized). Either implement key sorting or rename to avoid implying stability. Practical impact here is low since it only gates the normalized propagation, but the name is misleading for future use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/sqlrooms-cli-ui/src/workspace/WorksheetChartRenderer.tsx` around lines
34 - 36, The helper stableStringify currently just calls JSON.stringify but its
name implies deterministic key-ordering; update the function stableStringify in
WorksheetChartRenderer.tsx to produce a stable deterministic string by
recursively serializing objects with sorted keys (e.g., implement a stable JSON
stringify that sorts Object.keys for each plain object and handles
arrays/primitive types), ensuring nested objects are also normalized so the
output is order-stable for use where normalized propagation is gated by this
value; alternatively if you prefer not to implement sorting, rename
stableStringify to something accurate like jsonStringify or stringifyUnstable
and update call sites to reflect the new name.
packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartImageNode.tsx (1)

4-38: ⚡ Quick win

Extract a shared factory for image-like block nodes.

BlockDocumentChartImageNode and BlockDocumentImageNode duplicate the same TipTap node config (attrs, atom flags, parse/render scaffolding). Consider a small helper (e.g., createBlockDocumentAssetFigureNode) parameterized by name, dataType, and optional label to keep behavior aligned and reduce future drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartImageNode.tsx`
around lines 4 - 38, BlockDocumentChartImageNode and BlockDocumentImageNode
duplicate the same TipTap node config; extract a shared factory function (e.g.,
createBlockDocumentAssetFigureNode) that accepts parameters like name, dataType
and optional label and returns the Node.create configuration used by both nodes;
replace the explicit Node.create calls for BlockDocumentChartImageNode and
BlockDocumentImageNode with calls to
createBlockDocumentAssetFigureNode('blockDocumentChartImage','block-document-chart-image','Chart
image') (and analogous call for the other image node) so attributes (id,
assetId, caption), parseHTML/renderHTML scaffolding, and addNodeView remain
centralized and consistent.
packages/documents/src/BlockDocumentSliceConfig.ts (1)

19-43: 💤 Low value

Update Zod 4 object schemas to avoid deprecated .passthrough()
Zod 4 treats .passthrough() as legacy; prefer z.looseObject({...}) (and keep .strictObject for strictness) for BlockDocumentMark, BlockDocumentNode, and BlockDocumentContent in packages/documents/src/BlockDocumentSliceConfig.ts (lines 19-43) to prevent future breakage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/documents/src/BlockDocumentSliceConfig.ts` around lines 19 - 43, The
schemas use the deprecated .passthrough(); replace each
z.object(...).passthrough() with z.looseObject({...}) and preserve the existing
shape — update BlockDocumentMark to z.looseObject({ type: z.string(), attrs:
z.record(z.string(), z.unknown()).optional() }), update BlockDocumentNode inside
its z.lazy to z.looseObject({ type: z.string(), attrs: z.record(z.string(),
z.unknown()).optional(), content: z.array(BlockDocumentNode).optional(), marks:
z.array(BlockDocumentMark).optional(), text: z.string().optional() }), and
update BlockDocumentContent to z.looseObject({ type: z.literal('doc'), content:
z.array(BlockDocumentNode).default([]) }); ensure imports still reference z and
tests/type usage still pass.
packages/mosaic/src/chart/MosaicDashboardChartContent.tsx (1)

30-31: 💤 Low value

Consider updating the props interface.

The connection and spec parameters are explicitly voided, indicating they're no longer used after refactoring to delegate to MosaicChartView. Consider removing them from MosaicDashboardChartContentProps if they're not needed for backward compatibility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mosaic/src/chart/MosaicDashboardChartContent.tsx` around lines 30 -
31, The props interface currently includes connection and spec which are
explicitly voided in MosaicDashboardChartContent; remove these unused properties
from MosaicDashboardChartContentProps and from the MosaicDashboardChartContent
component signature, update any places that construct or type the props
(including tests or parent components) to stop passing connection/spec, and
ensure MosaicChartView is used as the single source for those values; keep any
backward-compatibility typings only if required and update imports/usages of
MosaicDashboardChartContent to match the new prop shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/documents/src/BlockDocumentAi.ts`:
- Line 19: Replace locale-sensitive toLocaleLowerCase() calls with toLowerCase()
for technical identifiers: update the artifactLabel.toLocaleLowerCase()
assignment (artifactLabelLower) in BlockDocumentAi to use
artifactLabel.toLowerCase(), and make the same change for the other two
occurrences noted on lines 34 and 42 (search for .toLocaleLowerCase() in this
file); ensure all comparisons/uses that rely on artifactLabelLower remain
unchanged.

In `@packages/documents/src/BlockDocumentCommands.ts`:
- Around line 183-185: The function lowerLabel currently uses locale-sensitive
toLocaleLowerCase(); change it to the locale-insensitive toLowerCase() to avoid
locale-dependent transformations for technical identifiers—update the
lowerLabel(label: string) implementation to call label.toLowerCase() instead of
label.toLocaleLowerCase() and ensure any callers (e.g., places that normalize
labels) continue to work with the same string type.

In
`@packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentStatefulBlockNode.tsx`:
- Line 1: The import in BlockDocumentStatefulBlockNode.tsx is incorrect: move
the Node and mergeAttributes imports from `@tiptap/react` to `@tiptap/core` and keep
ReactNodeViewRenderer from `@tiptap/react`; update the import statements so that
Node and mergeAttributes are imported from '`@tiptap/core`' and
ReactNodeViewRenderer remains imported from '`@tiptap/react`' (ensure symbols
Node, mergeAttributes, ReactNodeViewRenderer are referenced exactly as used in
this file).

In
`@packages/documents/src/BlockDocumentEditor/node-views/BlockDocumentChartNodeView.tsx`:
- Around line 25-37: The attribute-change callbacks (handleTableNameChange,
handleConfigChange, handleCaptionChange and the similar handlers at lines
~57-59) currently call updateAttributes unconditionally, allowing mutations when
readOnly is true; update each handler to first check the readOnly prop and
return early if true, otherwise call updateAttributes as before; reference the
handlers by name (handleTableNameChange, handleConfigChange,
handleCaptionChange) and the updateAttributes function so you can locate and
guard each callback.

In
`@packages/documents/src/BlockDocumentEditor/node-views/BlockDocumentStatefulBlockNodeView.tsx`:
- Around line 270-303: The mousemove/mouseup listeners added in
handleResizeMouseDown can leak if the component unmounts during a drag; modify
the implementation to store the handleMouseMove and handleMouseUp functions (or
a flag/AbortController) in a ref and ensure they are removed in a component
unmount cleanup (useEffect cleanup) as well as on mouseup; keep the existing
logic that computes nextHeight and calls setResizingHeight/updateAttributes but
ensure window.removeEventListener('mousemove', handleMouseMove) and
window.removeEventListener('mouseup', handleMouseUp) are always called on
unmount to avoid stale closures and leaked listeners.

In `@packages/documents/src/BlockDocumentSliceConfig.ts`:
- Around line 292-380: blockDocumentNodeToBlock currently uses .parse() on zod
models (BlockDocumentImageBlock, BlockDocumentChartImageBlock,
BlockDocumentChartBlock, BlockDocumentStatefulBlockBlock) which will throw if
attrs are missing and break blockDocumentContentToBlocks; replace each
.parse({...}) call with const res = Model.safeParse({...}) and return
res.success ? res.data : undefined so invalid/missing attrs yield undefined (and
are filtered out by blockDocumentContentToBlocks) instead of throwing.

---

Outside diff comments:
In `@apps/sqlrooms-cli-ui/src/createDashboardCommands.ts`:
- Around line 85-99: The pivot's state title is using the raw title (title ??
group) while the artifact was created with the de-duplicated title from
getUniqueArtifactTitle, causing mismatched titles; fix by capturing the computed
title (call getUniqueArtifactTitle once, e.g. const uniqueTitle =
getUniqueArtifactTitle(state, title ?? group)), pass uniqueTitle into
state.artifacts.createArtifact and then use that same uniqueTitle when calling
state.pivot.ensurePivot(artifactId, { title: uniqueTitle }); reference:
getUniqueArtifactTitle, state.artifacts.createArtifact, artifactId,
artifactType, state.pivot.ensurePivot.

---

Nitpick comments:
In `@apps/sqlrooms-cli-ui/src/store.ts`:
- Around line 516-558: The handlers onCreateOwnedStatefulBlock,
onDeleteOwnedStatefulBlock and onRenameOwnedStatefulBlock currently silently
no-op when STATEFUL_BLOCK_ARTIFACT_CONFIGS[blockType] is undefined; update each
handler to detect when config is falsy and emit a debug/warn log (e.g.,
console.warn or an existing logger) including the blockType and blockInstanceId
(and title where relevant) so missing or misspelled block types are visible,
then proceed with the existing calls to config.ensureState, config.deleteState,
or config.renameState when config is present.

In `@apps/sqlrooms-cli-ui/src/workspace/WorksheetChartRenderer.tsx`:
- Around line 34-36: The helper stableStringify currently just calls
JSON.stringify but its name implies deterministic key-ordering; update the
function stableStringify in WorksheetChartRenderer.tsx to produce a stable
deterministic string by recursively serializing objects with sorted keys (e.g.,
implement a stable JSON stringify that sorts Object.keys for each plain object
and handles arrays/primitive types), ensuring nested objects are also normalized
so the output is order-stable for use where normalized propagation is gated by
this value; alternatively if you prefer not to implement sorting, rename
stableStringify to something accurate like jsonStringify or stringifyUnstable
and update call sites to reflect the new name.

In `@dev-docs/tasks/root-block-artifacts.md`:
- Around line 3-16: Remove the active "Todo Checklist" block from this
superseded document and replace it with a short archival note indicating this
doc is superseded; include a clear one-line pointer to the canonical file
stateful-blocks-artifact-shells.md and a brief sentence like "See
stateful-blocks-artifact-shells.md for the current plan" so readers won't act on
the stale checklist; target the "Todo Checklist" section header to locate and
delete the unchecked checklist items.

In `@packages/artifacts/src/artifactTabs.tsx`:
- Around line 609-613: The inline onSelect handler is recreated every render;
wrap it in useCallback to stable-reference the function passed to TabStrip:
create a memoized handler (e.g., const handleSelect = useCallback((artifactId)
=> { artifactTabs.selectArtifact(artifactId); onSelectArtifact?.(artifactId); },
[artifactTabs, onSelectArtifact]) ) and pass handleSelect to the TabStrip
onSelect prop so the prop identity remains stable across renders.

In `@packages/documents/src/BlockDocumentEditor/BlockDocumentEditorRoot.tsx`:
- Around line 290-298: The contextValue object (and the inline generateBlockId
arrow) is recreated on every render causing unnecessary re-renders; wrap the
BlockDocumentEditorContextValue creation in useMemo so the provider value stays
referentially stable, e.g. memoize contextValue and inside it keep
generateBlockId: () => generateBlockIdRef.current(), and list stable
dependencies [editor, documentId, normalizedValue, assets, onChange, readOnly]
so consumers only update when those actually change.

In
`@packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartImageNode.tsx`:
- Around line 4-38: BlockDocumentChartImageNode and BlockDocumentImageNode
duplicate the same TipTap node config; extract a shared factory function (e.g.,
createBlockDocumentAssetFigureNode) that accepts parameters like name, dataType
and optional label and returns the Node.create configuration used by both nodes;
replace the explicit Node.create calls for BlockDocumentChartImageNode and
BlockDocumentImageNode with calls to
createBlockDocumentAssetFigureNode('blockDocumentChartImage','block-document-chart-image','Chart
image') (and analogous call for the other image node) so attributes (id,
assetId, caption), parseHTML/renderHTML scaffolding, and addNodeView remain
centralized and consistent.

In `@packages/documents/src/BlockDocumentSliceConfig.ts`:
- Around line 19-43: The schemas use the deprecated .passthrough(); replace each
z.object(...).passthrough() with z.looseObject({...}) and preserve the existing
shape — update BlockDocumentMark to z.looseObject({ type: z.string(), attrs:
z.record(z.string(), z.unknown()).optional() }), update BlockDocumentNode inside
its z.lazy to z.looseObject({ type: z.string(), attrs: z.record(z.string(),
z.unknown()).optional(), content: z.array(BlockDocumentNode).optional(), marks:
z.array(BlockDocumentMark).optional(), text: z.string().optional() }), and
update BlockDocumentContent to z.looseObject({ type: z.literal('doc'), content:
z.array(BlockDocumentNode).default([]) }); ensure imports still reference z and
tests/type usage still pass.

In `@packages/documents/src/BlockDocumentStatefulBlockRendererContext.tsx`:
- Around line 66-82: BlockDocumentStatefulBlockRendererProvider recreates
supportedBlockTypes and the context value on every render causing unnecessary
consumer re-renders; wrap supportedBlockTypes computation and the Provider value
in useMemo so they only change when their inputs change (memoize
supportedBlockTypes derived from blockTypes or renderers, and memoize the object
passed to BlockDocumentStatefulBlockRendererContext.Provider) referencing the
BlockDocumentStatefulBlockRendererProvider, supportedBlockTypes, renderers,
blockTypes, and BlockDocumentStatefulBlockRendererContext.Provider symbols to
locate and update the code.

In `@packages/documents/src/crdt.ts`:
- Around line 82-104: The blockDocuments entry uses content: schema.Any(), which
stores the entire ProseMirror body as an opaque blob and causes whole-document
last-write-wins; change the schema to a small structured wrapper to avoid
clobbering and pave the loro-prosemirror migration: replace content:
schema.Any() with content: schema.LoroMap({ type: schema.String(), body:
schema.Any() }) (or another minimal typed map) in the blockDocuments schema so
edits can be scoped/validated and later replaced by the full loro-prosemirror
schema; update any create/read usage of blockDocuments to access content.body
and add a TODO referencing loro-prosemirror for the future migration.

In `@packages/mosaic/src/chart/MosaicDashboardChartContent.tsx`:
- Around line 30-31: The props interface currently includes connection and spec
which are explicitly voided in MosaicDashboardChartContent; remove these unused
properties from MosaicDashboardChartContentProps and from the
MosaicDashboardChartContent component signature, update any places that
construct or type the props (including tests or parent components) to stop
passing connection/spec, and ensure MosaicChartView is used as the single source
for those values; keep any backward-compatibility typings only if required and
update imports/usages of MosaicDashboardChartContent to match the new prop
shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ef6dcc45-9980-4079-b604-e5d126db8c1e

📥 Commits

Reviewing files that changed from the base of the PR and between b556abe and fe210c9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (102)
  • AGENTS.md
  • apps/sqlrooms-cli-ui/README.md
  • apps/sqlrooms-cli-ui/package.json
  • apps/sqlrooms-cli-ui/src/artifactTypes.tsx
  • apps/sqlrooms-cli-ui/src/context/createArtifactContextAiTools.ts
  • apps/sqlrooms-cli-ui/src/createDashboardCommands.ts
  • apps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.ts
  • apps/sqlrooms-cli-ui/src/store-types.ts
  • apps/sqlrooms-cli-ui/src/store.ts
  • apps/sqlrooms-cli-ui/src/workspace/ArtifactsContainerPanel.tsx
  • apps/sqlrooms-cli-ui/src/workspace/DocumentArtifact.tsx
  • apps/sqlrooms-cli-ui/src/workspace/WorksheetArtifact.tsx
  • apps/sqlrooms-cli-ui/src/workspace/WorksheetChartRenderer.tsx
  • apps/sqlrooms-cli-ui/src/workspace/WorksheetDashboardBlockRenderer.tsx
  • apps/sqlrooms-cli-ui/src/workspace/WorksheetMarkdownDocumentBlockRenderer.tsx
  • apps/sqlrooms-cli-ui/src/workspace/WorksheetPivotBlockRenderer.tsx
  • apps/sqlrooms-cli-ui/src/workspace/dashboard/DashboardArtifact.tsx
  • apps/sqlrooms-cli-ui/src/workspace/worksheetChartConfig.ts
  • dev-docs/tasks/analysis-document-artifacts.md
  • dev-docs/tasks/cells-blocks-convergence.md
  • dev-docs/tasks/root-block-artifacts.md
  • dev-docs/tasks/stateful-blocks-artifact-shells.md
  • docs/packages.md
  • packages/artifacts/README.md
  • packages/artifacts/__tests__/ArtifactsSlice.test.ts
  • packages/artifacts/package.json
  • packages/artifacts/src/ArtifactTypes.ts
  • packages/artifacts/src/ArtifactsSlice.ts
  • packages/artifacts/src/ArtifactsSliceConfig.ts
  • packages/artifacts/src/artifactTabs.tsx
  • packages/artifacts/src/index.ts
  • packages/blocks/README.md
  • packages/blocks/eslint.config.js
  • packages/blocks/package.json
  • packages/blocks/src/index.ts
  • packages/blocks/tsconfig.json
  • packages/blocks/typedoc.js
  • packages/documents/README.md
  • packages/documents/__tests__/BlockDocumentAi.test.ts
  • packages/documents/__tests__/BlockDocumentCommands.test.ts
  • packages/documents/__tests__/BlockDocumentsSlice.test.ts
  • packages/documents/__tests__/documentsCrdt.test.ts
  • packages/documents/package.json
  • packages/documents/src/BlockDocumentAi.ts
  • packages/documents/src/BlockDocumentArtifact.tsx
  • packages/documents/src/BlockDocumentChartRendererContext.tsx
  • packages/documents/src/BlockDocumentCommands.ts
  • packages/documents/src/BlockDocumentEditor.tsx
  • packages/documents/src/BlockDocumentEditor/BlockDocumentBlockControls.tsx
  • packages/documents/src/BlockDocumentEditor/BlockDocumentEditorContent.tsx
  • packages/documents/src/BlockDocumentEditor/BlockDocumentEditorContext.tsx
  • packages/documents/src/BlockDocumentEditor/BlockDocumentEditorRoot.tsx
  • packages/documents/src/BlockDocumentEditor/BlockDocumentToolbar.tsx
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentBlockIdExtension.ts
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartImageNode.tsx
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartNode.tsx
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentImageNode.tsx
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentRichTextNode.tsx
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentStatefulBlockNode.tsx
  • packages/documents/src/BlockDocumentEditor/extensions/BlockDocumentTitleNode.ts
  • packages/documents/src/BlockDocumentEditor/extensions/nodeViewEvents.ts
  • packages/documents/src/BlockDocumentEditor/node-views/BlockDocumentChartNodeView.tsx
  • packages/documents/src/BlockDocumentEditor/node-views/BlockDocumentImageNodeView.tsx
  • packages/documents/src/BlockDocumentEditor/node-views/BlockDocumentRichTextNodeView.tsx
  • packages/documents/src/BlockDocumentEditor/node-views/BlockDocumentStatefulBlockNodeView.tsx
  • packages/documents/src/BlockDocumentEditor/node-views/nodeViewUtils.ts
  • packages/documents/src/BlockDocumentSliceConfig.ts
  • packages/documents/src/BlockDocumentStatefulBlockRendererContext.tsx
  • packages/documents/src/BlockDocumentsSlice.ts
  • packages/documents/src/DocumentsSlice.ts
  • packages/documents/src/DocumentsSliceConfig.ts
  • packages/documents/src/MarkdownDocumentBlock.tsx
  • packages/documents/src/MarkdownDocumentEditor/MarkdownDocumentEditorContent.tsx
  • packages/documents/src/MarkdownDocumentEditor/MarkdownDocumentEditorRoot.tsx
  • packages/documents/src/crdt.ts
  • packages/documents/src/index.ts
  • packages/documents/src/useStoreWithBlockDocuments.ts
  • packages/mosaic/README.md
  • packages/mosaic/__tests__/MosaicDashboardSlice.test.ts
  • packages/mosaic/package.json
  • packages/mosaic/src/VgPlotChart.tsx
  • packages/mosaic/src/chart/MosaicChartSettingsPanel.tsx
  • packages/mosaic/src/chart/MosaicChartView.tsx
  • packages/mosaic/src/chart/MosaicDashboardChart.tsx
  • packages/mosaic/src/chart/MosaicDashboardChartContent.tsx
  • packages/mosaic/src/chart/useBrushSelectionParams.tsx
  • packages/mosaic/src/chart/useChartRetainer.tsx
  • packages/mosaic/src/chart/useGenerateSpec.ts
  • packages/mosaic/src/dashboard/MosaicDashboardBlock.tsx
  • packages/mosaic/src/dashboard/MosaicDashboardPanels.tsx
  • packages/mosaic/src/dashboard/MosaicDashboardSlice.ts
  • packages/mosaic/src/dashboard/toolbar/MosaicDashboardAddPanelDropdown.tsx
  • packages/mosaic/src/index.ts
  • packages/mosaic/src/useVgPlotChartRender.ts
  • packages/pivot/README.md
  • packages/pivot/package.json
  • packages/pivot/src/PivotBlock.tsx
  • packages/pivot/src/index.ts
  • packages/ui/README.md
  • packages/ui/src/components/modifier-scroll-overlay.tsx
  • packages/ui/src/components/tab-strip.tsx
  • packages/ui/src/index.ts
💤 Files with no reviewable changes (2)
  • apps/sqlrooms-cli-ui/src/workspace/DocumentArtifact.tsx
  • packages/documents/src/MarkdownDocumentEditor/MarkdownDocumentEditorContent.tsx

Comment thread packages/documents/src/BlockDocumentAi.ts Outdated
Comment thread packages/documents/src/BlockDocumentCommands.ts
Comment thread packages/documents/src/BlockDocumentSliceConfig.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/.vitepress/config.ts (1)

28-60: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Categorization here diverges from docs/packages.md.

blocks, db-settings, and ai-settings are placed under Feature Packages here, but docs/packages.md lists them under Core Packages. The experimental set (ai-rag, canvas, crdt, discuss, notebook, pivot, webcontainer) is consistent across both files; only these three differ. Please align the two so the sidebar and catalog agree.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/.vitepress/config.ts` around lines 28 - 60, The sidebar config in
docs/.vitepress/config.ts lists 'blocks', 'db-settings', and 'ai-settings' under
the "Feature Packages" array, which conflicts with docs/packages.md that
classifies them as "Core Packages"; update the config so those three entries are
moved out of the 'Feature Packages' array and added to the "Core Packages" group
(or, if you intend the opposite classification, update docs/packages.md instead)
so the "Feature Packages", "Core Packages", and the entries for 'blocks',
'db-settings', and 'ai-settings' are consistent across both files.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/packages.md`:
- Line 13: The docs show `@sqlrooms/blocks` listed under "Core Packages" in
docs/packages.md but the sidebar config in docs/.vitepress/config.ts places
"blocks" (and db-settings, ai-settings) under "Feature Packages"; choose one
canonical category and update both files to match—either move the
`@sqlrooms/blocks` entry in docs/packages.md into the "Feature Packages" section
or edit the sidebar entries in docs/.vitepress/config.ts so "blocks" is under
"Core Packages"—and ensure the labels and grouping for the symbols "blocks",
"db-settings", and "ai-settings" are consistent across both files.

---

Outside diff comments:
In `@docs/.vitepress/config.ts`:
- Around line 28-60: The sidebar config in docs/.vitepress/config.ts lists
'blocks', 'db-settings', and 'ai-settings' under the "Feature Packages" array,
which conflicts with docs/packages.md that classifies them as "Core Packages";
update the config so those three entries are moved out of the 'Feature Packages'
array and added to the "Core Packages" group (or, if you intend the opposite
classification, update docs/packages.md instead) so the "Feature Packages",
"Core Packages", and the entries for 'blocks', 'db-settings', and 'ai-settings'
are consistent across both files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48e202b6-1dd8-4817-bfb2-3c20b89436ff

📥 Commits

Reviewing files that changed from the base of the PR and between fe210c9 and 0e07f03.

📒 Files selected for processing (7)
  • apps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.ts
  • apps/sqlrooms-cli-ui/src/store.ts
  • apps/sqlrooms-cli-ui/src/workspace/ArtifactsContainerPanel.tsx
  • docs/.vitepress/config.ts
  • docs/packages.md
  • packages/blocks/README.md
  • packages/documents/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/documents/README.md
  • apps/sqlrooms-cli-ui/src/store.ts
  • apps/sqlrooms-cli-ui/src/workspace/ArtifactsContainerPanel.tsx

Comment thread docs/packages.md
### Core Packages

- **[@sqlrooms/artifacts](/api/artifacts/)** — Artifact registry, workspace tab helpers, and layout integration for dashboards, notebooks, documents, canvases, pivots, and apps.
- **[@sqlrooms/blocks](/api/blocks/)** — Shared block contracts and vocabulary for composable SQLRooms blocks and block containers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Cross-check @sqlrooms/blocks placement.

Listed under Core Packages here, but the docs sidebar (docs/.vitepress/config.ts) puts blocks (along with db-settings and ai-settings) under Feature Packages. See the related comment on config.ts; pick one categorization and keep both files in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/packages.md` at line 13, The docs show `@sqlrooms/blocks` listed under
"Core Packages" in docs/packages.md but the sidebar config in
docs/.vitepress/config.ts places "blocks" (and db-settings, ai-settings) under
"Feature Packages"; choose one canonical category and update both files to
match—either move the `@sqlrooms/blocks` entry in docs/packages.md into the
"Feature Packages" section or edit the sidebar entries in
docs/.vitepress/config.ts so "blocks" is under "Core Packages"—and ensure the
labels and grouping for the symbols "blocks", "db-settings", and "ai-settings"
are consistent across both files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant