Repository navigation
feat: Blocks and BlockDocument - #666
Conversation
|
Warning Review limit reached
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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughAdds 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. ChangesBlock Documents + Stateful Blocks
Sequence DiagramsequenceDiagram
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)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
1ad2eb3 to
fe210c9
Compare
There was a problem hiding this comment.
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 winPivot title diverges from the artifact title on create.
The artifact is created with a de-duplicated unique title, but
ensurePivotreceives the rawtitle ?? 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 theartifact.selectpath already usesartifact.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 winConsider logging when stateful block config is not found.
The
onCreateOwnedStatefulBlock,onDeleteOwnedStatefulBlock, andonRenameOwnedStatefulBlockhandlers silently skip operations when theblockTypeis not found inSTATEFUL_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
configis undefined to aid troubleshooting, or validate theblockTypeearlier 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 winMemoize the context value to keep the provider stable.
contextValue(and the inlinegenerateBlockIdarrow) is allocated on every render, so everyBlockDocumentEditorContextconsumer re-renders even when nothing it depends on changed. Wrapping it inuseMemokeeps 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-documentcontentstored as an opaqueschema.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 plannedloro-prosemirrormigration 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 winMake 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.mdto 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 valueMinor: Inline callback creates new function on each render.
The
onSelectcallback creates a new inline function on every render. While this is unlikely to cause performance issues in practice, consider memoizing it withuseCallbackif theTabStripcomponent 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 winMemoize the context value to keep the provider stable.
supportedBlockTypesand thevalueobject are rebuilt on every render, so all consumers of this context re-render whenever the provider re-renders (e.g. on any store update inWorksheetArtifact), even whenrenderers/blockTypesare 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 valueMisleading helper name.
stableStringifyis a plainJSON.stringifyand 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 thenormalizedpropagation, 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 winExtract a shared factory for image-like block nodes.
BlockDocumentChartImageNodeandBlockDocumentImageNodeduplicate the same TipTap node config (attrs, atom flags, parse/render scaffolding). Consider a small helper (e.g.,createBlockDocumentAssetFigureNode) parameterized byname,dataType, and optionallabelto 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 valueUpdate Zod 4 object schemas to avoid deprecated
.passthrough()
Zod 4 treats.passthrough()as legacy; preferz.looseObject({...})(and keep.strictObjectfor strictness) forBlockDocumentMark,BlockDocumentNode, andBlockDocumentContentinpackages/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 valueConsider updating the props interface.
The
connectionandspecparameters are explicitly voided, indicating they're no longer used after refactoring to delegate toMosaicChartView. Consider removing them fromMosaicDashboardChartContentPropsif 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (102)
AGENTS.mdapps/sqlrooms-cli-ui/README.mdapps/sqlrooms-cli-ui/package.jsonapps/sqlrooms-cli-ui/src/artifactTypes.tsxapps/sqlrooms-cli-ui/src/context/createArtifactContextAiTools.tsapps/sqlrooms-cli-ui/src/createDashboardCommands.tsapps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.tsapps/sqlrooms-cli-ui/src/store-types.tsapps/sqlrooms-cli-ui/src/store.tsapps/sqlrooms-cli-ui/src/workspace/ArtifactsContainerPanel.tsxapps/sqlrooms-cli-ui/src/workspace/DocumentArtifact.tsxapps/sqlrooms-cli-ui/src/workspace/WorksheetArtifact.tsxapps/sqlrooms-cli-ui/src/workspace/WorksheetChartRenderer.tsxapps/sqlrooms-cli-ui/src/workspace/WorksheetDashboardBlockRenderer.tsxapps/sqlrooms-cli-ui/src/workspace/WorksheetMarkdownDocumentBlockRenderer.tsxapps/sqlrooms-cli-ui/src/workspace/WorksheetPivotBlockRenderer.tsxapps/sqlrooms-cli-ui/src/workspace/dashboard/DashboardArtifact.tsxapps/sqlrooms-cli-ui/src/workspace/worksheetChartConfig.tsdev-docs/tasks/analysis-document-artifacts.mddev-docs/tasks/cells-blocks-convergence.mddev-docs/tasks/root-block-artifacts.mddev-docs/tasks/stateful-blocks-artifact-shells.mddocs/packages.mdpackages/artifacts/README.mdpackages/artifacts/__tests__/ArtifactsSlice.test.tspackages/artifacts/package.jsonpackages/artifacts/src/ArtifactTypes.tspackages/artifacts/src/ArtifactsSlice.tspackages/artifacts/src/ArtifactsSliceConfig.tspackages/artifacts/src/artifactTabs.tsxpackages/artifacts/src/index.tspackages/blocks/README.mdpackages/blocks/eslint.config.jspackages/blocks/package.jsonpackages/blocks/src/index.tspackages/blocks/tsconfig.jsonpackages/blocks/typedoc.jspackages/documents/README.mdpackages/documents/__tests__/BlockDocumentAi.test.tspackages/documents/__tests__/BlockDocumentCommands.test.tspackages/documents/__tests__/BlockDocumentsSlice.test.tspackages/documents/__tests__/documentsCrdt.test.tspackages/documents/package.jsonpackages/documents/src/BlockDocumentAi.tspackages/documents/src/BlockDocumentArtifact.tsxpackages/documents/src/BlockDocumentChartRendererContext.tsxpackages/documents/src/BlockDocumentCommands.tspackages/documents/src/BlockDocumentEditor.tsxpackages/documents/src/BlockDocumentEditor/BlockDocumentBlockControls.tsxpackages/documents/src/BlockDocumentEditor/BlockDocumentEditorContent.tsxpackages/documents/src/BlockDocumentEditor/BlockDocumentEditorContext.tsxpackages/documents/src/BlockDocumentEditor/BlockDocumentEditorRoot.tsxpackages/documents/src/BlockDocumentEditor/BlockDocumentToolbar.tsxpackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentBlockIdExtension.tspackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartImageNode.tsxpackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentChartNode.tsxpackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentImageNode.tsxpackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentRichTextNode.tsxpackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentStatefulBlockNode.tsxpackages/documents/src/BlockDocumentEditor/extensions/BlockDocumentTitleNode.tspackages/documents/src/BlockDocumentEditor/extensions/nodeViewEvents.tspackages/documents/src/BlockDocumentEditor/node-views/BlockDocumentChartNodeView.tsxpackages/documents/src/BlockDocumentEditor/node-views/BlockDocumentImageNodeView.tsxpackages/documents/src/BlockDocumentEditor/node-views/BlockDocumentRichTextNodeView.tsxpackages/documents/src/BlockDocumentEditor/node-views/BlockDocumentStatefulBlockNodeView.tsxpackages/documents/src/BlockDocumentEditor/node-views/nodeViewUtils.tspackages/documents/src/BlockDocumentSliceConfig.tspackages/documents/src/BlockDocumentStatefulBlockRendererContext.tsxpackages/documents/src/BlockDocumentsSlice.tspackages/documents/src/DocumentsSlice.tspackages/documents/src/DocumentsSliceConfig.tspackages/documents/src/MarkdownDocumentBlock.tsxpackages/documents/src/MarkdownDocumentEditor/MarkdownDocumentEditorContent.tsxpackages/documents/src/MarkdownDocumentEditor/MarkdownDocumentEditorRoot.tsxpackages/documents/src/crdt.tspackages/documents/src/index.tspackages/documents/src/useStoreWithBlockDocuments.tspackages/mosaic/README.mdpackages/mosaic/__tests__/MosaicDashboardSlice.test.tspackages/mosaic/package.jsonpackages/mosaic/src/VgPlotChart.tsxpackages/mosaic/src/chart/MosaicChartSettingsPanel.tsxpackages/mosaic/src/chart/MosaicChartView.tsxpackages/mosaic/src/chart/MosaicDashboardChart.tsxpackages/mosaic/src/chart/MosaicDashboardChartContent.tsxpackages/mosaic/src/chart/useBrushSelectionParams.tsxpackages/mosaic/src/chart/useChartRetainer.tsxpackages/mosaic/src/chart/useGenerateSpec.tspackages/mosaic/src/dashboard/MosaicDashboardBlock.tsxpackages/mosaic/src/dashboard/MosaicDashboardPanels.tsxpackages/mosaic/src/dashboard/MosaicDashboardSlice.tspackages/mosaic/src/dashboard/toolbar/MosaicDashboardAddPanelDropdown.tsxpackages/mosaic/src/index.tspackages/mosaic/src/useVgPlotChartRender.tspackages/pivot/README.mdpackages/pivot/package.jsonpackages/pivot/src/PivotBlock.tsxpackages/pivot/src/index.tspackages/ui/README.mdpackages/ui/src/components/modifier-scroll-overlay.tsxpackages/ui/src/components/tab-strip.tsxpackages/ui/src/index.ts
💤 Files with no reviewable changes (2)
- apps/sqlrooms-cli-ui/src/workspace/DocumentArtifact.tsx
- packages/documents/src/MarkdownDocumentEditor/MarkdownDocumentEditorContent.tsx
There was a problem hiding this comment.
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 winCategorization here diverges from
docs/packages.md.
blocks,db-settings, andai-settingsare placed under Feature Packages here, butdocs/packages.mdlists 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
📒 Files selected for processing (7)
apps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.tsapps/sqlrooms-cli-ui/src/store.tsapps/sqlrooms-cli-ui/src/workspace/ArtifactsContainerPanel.tsxdocs/.vitepress/config.tsdocs/packages.mdpackages/blocks/README.mdpackages/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
| ### 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. |
There was a problem hiding this comment.
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.
Summary by CodeRabbit
New Features
Documentation
UX