Repository navigation
feat: Add chat prompt context - #598
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds run-context schemas/helpers; captures runContext at startAnalysis; composes session-scoped instructions including formatted run-context text; injects runContext into tool executions; adds DnD ContextSelector with reorder; extends DnD across layout/tab components; expands public exports and tests. ChangesRun Context Feature
Shared DnD & Layout / UI
Sequence Diagram(s)sequenceDiagram
participant User
participant AiSlice
participant ChatTransport
participant ToolLoopAgent
User->>AiSlice: startAnalysis()
AiSlice->>AiSlice: getRunContext() -> draftSession.runContext
User->>AiSlice: sendPrompt(text)
AiSlice->>ChatTransport: send request (sessionId, prompt)
ChatTransport->>ToolLoopAgent: start with wrapped tools (sessionId, aiRunContext)
ToolLoopAgent->>ToolLoopAgent: tool.execute(options + aiRunContext)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/ai-core/src/components/ContextSelector.tsx (1)
454-461: 💤 Low valueTwo
Object.assigncalls can be merged into one.♻️ Proposed refactor
-export const ContextSelector: ContextSelectorComponent = Object.assign(Root, { +export const ContextSelector: ContextSelectorComponent = Object.assign(Root, { Badge, SearchDropdown, -}) as ContextSelectorComponent; - -Object.assign(ContextSelector, { [CHAT_CONTEXT_SELECTOR_SLOT]: true, -}); +}) as ContextSelectorComponent;🤖 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/ai-core/src/components/ContextSelector.tsx` around lines 454 - 461, Merge the two Object.assign calls into a single assignment: when creating ContextSelector, assign Root plus the properties Badge and SearchDropdown and also add the keyed property [CHAT_CONTEXT_SELECTOR_SLOT] in one Object.assign call so ContextSelector is created with all properties at once; locate the ContextSelector declaration (currently using Object.assign(Root, { Badge, SearchDropdown }) and the subsequent Object.assign(ContextSelector, { [CHAT_CONTEXT_SELECTOR_SLOT]: true }) and combine them into a single Object.assign(Root, { Badge, SearchDropdown, [CHAT_CONTEXT_SELECTOR_SLOT]: true }) assigned to ContextSelector.
🤖 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/ai-core/__tests__/AiSlice.modelSelection.test.ts`:
- Line 3: The test imports from `@jest/globals` but that package is not declared
in ai-core's devDependencies; add "@jest/globals": "^30.3.0" (matching other
packages) to the ai-core package.json devDependencies so the import in the test
(import {jest} from '@jest/globals') resolves in CI.
In `@packages/ai-core/src/components/ContextSelector.tsx`:
- Around line 178-181: The context value currently captures a freshly created
setOpen closure that references onOpenChange but isn’t stable or included in the
useMemo deps, causing a stale callback in controlled mode; to fix, memoize
setOpen (e.g., with useCallback) so it doesn't get recreated each render and
include that memoized setOpen in the useMemo dependency array that builds the
context value (references: setOpen, isControlled, setUncontrolledOpen,
onOpenChange, useMemo, and the context value used by SearchDropdown).
In `@packages/ai/src/index.ts`:
- Around line 91-95: Replace the inline redeclaration of
CHAT_CONTEXT_SELECTOR_SLOT with a re-export from `@sqlrooms/ai-core`: remove the
const CHAT_CONTEXT_SELECTOR_SLOT = Symbol.for(...) declaration in
packages/ai/src/index.ts and instead export the symbol from the ai-core package
so callers (and isContextSelector) use the exact same exported symbol; ensure
the export uses the exact exported name CHAT_CONTEXT_SELECTOR_SLOT from
'@sqlrooms/ai-core'.
---
Nitpick comments:
In `@packages/ai-core/src/components/ContextSelector.tsx`:
- Around line 454-461: Merge the two Object.assign calls into a single
assignment: when creating ContextSelector, assign Root plus the properties Badge
and SearchDropdown and also add the keyed property [CHAT_CONTEXT_SELECTOR_SLOT]
in one Object.assign call so ContextSelector is created with all properties at
once; locate the ContextSelector declaration (currently using
Object.assign(Root, { Badge, SearchDropdown }) and the subsequent
Object.assign(ContextSelector, { [CHAT_CONTEXT_SELECTOR_SLOT]: true }) and
combine them into a single Object.assign(Root, { Badge, SearchDropdown,
[CHAT_CONTEXT_SELECTOR_SLOT]: true }) assigned to ContextSelector.
🪄 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: 0f830b4b-2bb7-4cac-b244-583159b12c54
📒 Files selected for processing (14)
packages/ai-config/src/index.tspackages/ai-config/src/schema/AnalysisSessionSchema.tspackages/ai-core/__tests__/AiSlice.modelSelection.test.tspackages/ai-core/__tests__/ContextSelector.test.tspackages/ai-core/__tests__/migration.test.tspackages/ai-core/src/AiSlice.tspackages/ai-core/src/chatTransport.tspackages/ai-core/src/components/Chat.tsxpackages/ai-core/src/components/ContextSelector.tsxpackages/ai-core/src/components/QueryControls.tsxpackages/ai-core/src/index.tspackages/ai-core/src/types.tspackages/ai/src/index.tspackages/ui/src/components/tab-strip.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/ai-core/package.json`:
- Line 54: The project mixes Jest 30 globals with ts-jest v29 which is
incompatible; update the dependency in packages/ai-core package.json by
replacing the existing "ts-jest" entry with a Jest-30-compatible release (e.g.
"ts-jest": "^30.0.0") or otherwise pin "@jest/globals" back to ^29.x to match
ts-jest, and ensure any presets like "@sqlrooms/preset-jest" are aligned (update
or override its ts-jest/jest versions) so that "@jest/globals" and "ts-jest" use
the same major Jest release.
🪄 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: 70a5d28d-01b4-4548-bf73-1fa729a6e6c5
📒 Files selected for processing (4)
packages/ai-core/__tests__/ContextSelector.test.tspackages/ai-core/package.jsonpackages/ai-core/src/components/ContextSelector.tsxpackages/cells/__tests__/defaultCellRegistry.integration.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/ai-core/tests/ContextSelector.test.ts
- packages/ai-core/src/components/ContextSelector.tsx
| "typedoc": "typedoc" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check what version of Jest `@sqlrooms/preset-jest` pulls in and
# verify the ts-jest peer-dependency range against the installed Jest version.
# Find preset-jest package.json to inspect its Jest dependency
fd -i "package.json" --exclude "node_modules" | xargs grep -l "preset-jest" 2>/dev/null | head -5
# Show the preset-jest package.json if it exists in the monorepo
fd -g "package.json" --search-path packages/preset-jest 2>/dev/null | xargs cat 2>/dev/null || true
# Show ts-jest peer-dependency declaration
rg -n "peerDependencies" node_modules/ts-jest/package.json 2>/dev/null | head -10 || true
rg -A 10 '"peerDependencies"' node_modules/ts-jest/package.json 2>/dev/null | head -15 || trueRepository: sqlrooms/sqlrooms
Length of output: 609
Align ts-jest with Jest 30 across the project.
The @jest/globals@^30.3.0 dependency is correct for Jest 30, but ts-jest@^29.4.4 is built for Jest 29 and is incompatible with Jest 30. This is a pre-existing issue introduced by @sqlrooms/preset-jest, which already specifies jest@^30.1.3 alongside ts-jest@^29.4.4. Adding @jest/globals@^30 to this package makes the incompatibility explicit across the codebase. Update ts-jest to a Jest 30-compatible version (or pin @jest/globals to ^29.x if remaining on Jest 29).
🤖 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/ai-core/package.json` at line 54, The project mixes Jest 30 globals
with ts-jest v29 which is incompatible; update the dependency in
packages/ai-core package.json by replacing the existing "ts-jest" entry with a
Jest-30-compatible release (e.g. "ts-jest": "^30.0.0") or otherwise pin
"@jest/globals" back to ^29.x to match ts-jest, and ensure any presets like
"@sqlrooms/preset-jest" are aligned (update or override its ts-jest/jest
versions) so that "@jest/globals" and "ts-jest" use the same major Jest release.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
packages/ui/src/components/tab-strip.tsx (3)
497-506: 💤 Low value
onDragOveroverlay logic flips overlay back on afterisDragInsideTabStripreturned true.The flow is:
- If
!over || isDragInsideTabStrip(event)→setHideSharedDragOverlay(true)and return.- Otherwise set
hideSharedDragOverlayto(over.data.current.tabStripId === dndScopeId).Step 2 only runs when the drag is outside this tab strip. In that case
tabStripId === dndScopeIdwould only be true if the user dragged the source tab outside its own strip and hovered some other droppable that happens to declare the sametabStripId— which usually won't happen. So effectively, whenever the drag leaves the strip,hideSharedDragOverlayis set tofalse, revealing the overlay. That's the intended UX.However,
onDragMoveonly ever sets it totrue(line 494), never back tofalse. Between anonDragMove(inside) tick and the nextonDragOver(outside) tick there can be intermediate frames wherehideSharedDragOverlayis stale. Consider unifying overlay visibility into a single derived value computed in one place (e.g., compute it inonDragMovetoo, mirroringonDragOver).🤖 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/ui/src/components/tab-strip.tsx` around lines 497 - 506, The onDragOver handler flips hideSharedDragOverlay based on tabStripId but onDragMove only ever sets it to true, causing stale visibility between events; update onDragMove to apply the same visibility logic used in onDragOver (use isDragInsideTabStrip(event) and compare event.over?.data.current?.tabStripId === dndScopeId when dndMode === 'shared') or extract the logic into a shared helper used by both onDragMove and onDragOver so both handlers compute and call setHideSharedDragOverlay consistently.
182-188: ⚡ Quick winSpread order may let user-provided drag data clobber
kind.
...getTabDragData?.(tab)is spread between the literalkind: DEFAULT_TAB_DRAG_KINDand the trailingtabId/tabStripId. That ordering correctly protectstabIdandtabStripIdfrom being overridden by user data, but the spread can intentionally or accidentally overwritekind(e.g.,ArtifactTabsRootrelies on this to setkind: 'artifact'). If overridingkindis intended, please document it on thegetTabDragDataprop type (TabStripDragData) so callers know they can flip the discriminator; if not, freezekindby putting it last.🤖 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/ui/src/components/tab-strip.tsx` around lines 182 - 188, The spread of getTabDragData?.(tab) sits between kind and tabId/tabStripId which allows user data to overwrite kind; either explicitly document in the TabStripDragData prop type that callers may override the discriminator, or (preferred) freeze kind by moving the spread to the front and placing tabId, tabStripId and kind after it (e.g., data: { ...getTabDragData?.(tab), tabId: tab.id, tabStripId: dndScopeId, kind: DEFAULT_TAB_DRAG_KIND }) so neither tabId/tabStripId nor kind can be clobbered.
480-518: ⚡ Quick winConsider consolidating
useDndMonitorlogic to avoid unnecessary hook registration in'internal'mode.
useDndMonitoris called unconditionally inTabStripTabs, but in'internal'mode it serves no purpose since all handlers contain earlyif (dndMode !== 'shared') return;guards. WhileuseDndMonitordoes not throw outside aDndContext(it simply no-ops), registering and maintaining the hook in internal mode is unnecessary overhead. For clarity and efficiency, consider either:
- Wrapping the
useDndMonitorcall in a conditional:if (dndMode === 'shared') { useDndMonitor(...) }- Extracting the monitor logic into a small inner component rendered only in
'shared'modeThe current code functions correctly but can be streamlined.
🤖 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/ui/src/components/tab-strip.tsx` around lines 480 - 518, The DnD monitor is registered unconditionally in TabStripTabs despite only being needed for dndMode === 'shared'; move the useDndMonitor call and its handlers into a small inner component (e.g., SharedDndMonitor) that you render only when dndMode === 'shared' so React hook rules remain satisfied. Inside SharedDndMonitor, use useDndMonitor with the existing handlers (onDragStart/onDragMove/onDragOver/onDragEnd/onDragCancel) and reference the same helpers and state setters (isDragInsideTabStrip, handleDragEnd, setActiveDraggedTabId, setHideSharedDragOverlay, dndScopeId) so behavior is unchanged but the hook is not registered in 'internal' mode.packages/layout/src/docking/DockingProvider.tsx (1)
29-33: 💤 Low valueConsider documenting the new ancestor
DndContextrequirement.
DockingProviderpreviously created its ownDndContext; it now usesuseDndMonitor, which throws when invoked outside aDndContext. The current wiring works becauseLayoutRendererwrapsDockingProviderinRoomDndProvider, but consumers reusingDockingProviderdirectly will hit a confusing runtime error. A short JSDoc note on the component (and/or a dev-time assert) would help.📝 Suggested doc
+/** + * Renders docking preview/overlay for layout panels. Must be rendered inside + * a `RoomDndProvider` (or any `@dnd-kit/core` `DndContext`); otherwise + * `useDndMonitor` will throw. + */ export const DockingProvider: FC<PropsWithChildren<DockingProviderProps>> = ({Also applies to: 163-168
🤖 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/layout/src/docking/DockingProvider.tsx` around lines 29 - 33, DockingProvider now calls useDndMonitor and therefore requires to be rendered inside a DndContext (previously it created its own); add a short JSDoc above the DockingProvider export describing this requirement (mention DndContext and that LayoutRenderer wraps it with RoomDndProvider) and add a dev-time assert inside DockingProvider to surface a clear error when useDndMonitor is missing (e.g., detect that useDndMonitor returns undefined/error and throw a descriptive error like "DockingProvider must be wrapped in a DndContext/RoomDndProvider"). Ensure references to DockingProvider, useDndMonitor, DndContext, LayoutRenderer and RoomDndProvider are included in the doc/assert so consumers can find the intended wrapper.packages/ai-core/src/components/QueryControls.tsx (1)
122-165: ⚡ Quick winConsider isolating DnD wiring behind
contextDropTargetpresence.
useDroppableanduseDndMonitorare invoked unconditionally. WhencontextDropTargetis not provided, this still:
- Registers a droppable using the shared fallback id
'chat-composer-context-drop-target-disabled'. MultipleQueryControlsinstances mounted simultaneously without DnD will collide on this id and may trigger@dnd-kitduplicate-id warnings.- Subscribes to every
onDragEndviauseDndMonitoronly to immediately bail out.A cleaner pattern is to extract the DnD wiring into a small internal subcomponent that is only rendered when
contextDropTargetis provided, and conditionally pass itssetNodeRef/isAcceptedContextDropOverto the container. That removes the dummy id and avoids subscribing dnd listeners for the common no-DnD case.♻️ Sketch of the refactor
// Conditionally render a child that owns useDroppable + useDndMonitor. function ContextDropTarget({ target, children, }: { target: NonNullable<QueryControlsProps['contextDropTarget']>; children: (args: { setNodeRef: (el: HTMLElement | null) => void; isAcceptedOver: boolean; }) => React.ReactNode; }) { const {active, isOver, setNodeRef} = useDroppable({ id: target.id, data: {roomDndPriority: 100}, }); const data = active?.data.current; const canAccept = Boolean(data && target.canAccept(data)); useDndMonitor({ onDragEnd: (event) => { if (event.over?.id !== target.id) return; if ( !event.collisions?.some( (c) => c.id === target.id && c.data?.pointerWithin === true, ) ) return; const d = event.active.data.current; if (target.canAccept(d)) target.onDrop(d); }, }); return <>{children({setNodeRef, isAcceptedOver: isOver && canAccept})}</>; }Then wrap the composer container only when
contextDropTargetis defined.🤖 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/ai-core/src/components/QueryControls.tsx` around lines 122 - 165, The current implementation always calls useDroppable and useDndMonitor even when contextDropTarget is null, causing a shared fallback id and unnecessary DnD subscriptions; extract the DnD wiring into a small internal component (e.g., ContextDropTarget) that is only rendered when contextDropTarget is non-null, move useDroppable and useDndMonitor there (use the real target.id instead of the fallback), compute the accepted-over boolean inside that component and pass setNodeRef (formerly setContextDropTargetRef) and isAcceptedOver (formerly isAcceptedContextDropOver) back to the parent via a render-prop or children callback, and remove isPointerWithinContextDropTarget from the outer scope so the onDragEnd logic lives with the DnD hooks and calls target.onDrop when appropriate.
🤖 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/artifacts/src/artifactTabs.tsx`:
- Around line 557-564: The consumer-supplied getTabDragData currently replaces
the artifact discriminator and fields if it returns any object, causing loss of
kind/id/type/title; update the getTabDragData logic so it merges the consumer
payload with the artifact defaults rather than using the consumer result
verbatim: call getTabDragData?.(tab) to get userData, then produce { kind:
'artifact', id: (tab as ArtifactTabDescriptor).artifact.id, type: (tab as
ArtifactTabDescriptor).artifact.type, title: (tab as
ArtifactTabDescriptor).artifact.title, ...userData } (or spread userData first
if you want user override) so DEFAULT_TAB_DRAG_KIND and RoomDndProvider can
still detect artifact tabs and downstream code can read id/type/title. Ensure
this change is applied where getTabDragData is passed into TabStrip so artifact
fields are preserved unless intentionally overridden.
In
`@packages/layout/src/node-renderers/grid-node-renderer/useScrollNewGridChildIntoView.ts`:
- Around line 46-47: The hook useScrollNewGridChildIntoView currently declares a
non-nullable RefObject<HTMLDivElement> but initializes scrollContainerRef with
useRef<HTMLDivElement>(null!), which lies about nullability; change the hook's
return type to RefObject<HTMLDivElement | null> and initialize
scrollContainerRef with useRef<HTMLDivElement | null>(null) so the type
accurately reflects that current may be null before mount, keeping internal
null-checks as-is and leaving callers that pass the ref to DOM elements
unchanged.
---
Nitpick comments:
In `@packages/ai-core/src/components/QueryControls.tsx`:
- Around line 122-165: The current implementation always calls useDroppable and
useDndMonitor even when contextDropTarget is null, causing a shared fallback id
and unnecessary DnD subscriptions; extract the DnD wiring into a small internal
component (e.g., ContextDropTarget) that is only rendered when contextDropTarget
is non-null, move useDroppable and useDndMonitor there (use the real target.id
instead of the fallback), compute the accepted-over boolean inside that
component and pass setNodeRef (formerly setContextDropTargetRef) and
isAcceptedOver (formerly isAcceptedContextDropOver) back to the parent via a
render-prop or children callback, and remove isPointerWithinContextDropTarget
from the outer scope so the onDragEnd logic lives with the DnD hooks and calls
target.onDrop when appropriate.
In `@packages/layout/src/docking/DockingProvider.tsx`:
- Around line 29-33: DockingProvider now calls useDndMonitor and therefore
requires to be rendered inside a DndContext (previously it created its own); add
a short JSDoc above the DockingProvider export describing this requirement
(mention DndContext and that LayoutRenderer wraps it with RoomDndProvider) and
add a dev-time assert inside DockingProvider to surface a clear error when
useDndMonitor is missing (e.g., detect that useDndMonitor returns
undefined/error and throw a descriptive error like "DockingProvider must be
wrapped in a DndContext/RoomDndProvider"). Ensure references to DockingProvider,
useDndMonitor, DndContext, LayoutRenderer and RoomDndProvider are included in
the doc/assert so consumers can find the intended wrapper.
In `@packages/ui/src/components/tab-strip.tsx`:
- Around line 497-506: The onDragOver handler flips hideSharedDragOverlay based
on tabStripId but onDragMove only ever sets it to true, causing stale visibility
between events; update onDragMove to apply the same visibility logic used in
onDragOver (use isDragInsideTabStrip(event) and compare
event.over?.data.current?.tabStripId === dndScopeId when dndMode === 'shared')
or extract the logic into a shared helper used by both onDragMove and onDragOver
so both handlers compute and call setHideSharedDragOverlay consistently.
- Around line 182-188: The spread of getTabDragData?.(tab) sits between kind and
tabId/tabStripId which allows user data to overwrite kind; either explicitly
document in the TabStripDragData prop type that callers may override the
discriminator, or (preferred) freeze kind by moving the spread to the front and
placing tabId, tabStripId and kind after it (e.g., data: {
...getTabDragData?.(tab), tabId: tab.id, tabStripId: dndScopeId, kind:
DEFAULT_TAB_DRAG_KIND }) so neither tabId/tabStripId nor kind can be clobbered.
- Around line 480-518: The DnD monitor is registered unconditionally in
TabStripTabs despite only being needed for dndMode === 'shared'; move the
useDndMonitor call and its handlers into a small inner component (e.g.,
SharedDndMonitor) that you render only when dndMode === 'shared' so React hook
rules remain satisfied. Inside SharedDndMonitor, use useDndMonitor with the
existing handlers (onDragStart/onDragMove/onDragOver/onDragEnd/onDragCancel) and
reference the same helpers and state setters (isDragInsideTabStrip,
handleDragEnd, setActiveDraggedTabId, setHideSharedDragOverlay, dndScopeId) so
behavior is unchanged but the hook is not registered in 'internal' mode.
🪄 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: a0ec7161-20eb-42cc-8f74-a5d1c9f69b90
📒 Files selected for processing (12)
packages/ai-core/src/components/QueryControls.tsxpackages/artifacts/src/artifactTabs.tsxpackages/layout/src/LayoutRenderer.tsxpackages/layout/src/dnd/RoomDndProvider.tsxpackages/layout/src/docking/DockingProvider.tsxpackages/layout/src/index.tspackages/layout/src/node-renderers/grid-node-renderer/useScrollNewGridChildIntoView.tspackages/layout/src/node-renderers/leaf-node-renderer/LeafLayoutPanel.tsxpackages/room-shell/src/RoomShell.tsxpackages/room-shell/src/index.tspackages/ui/src/components/tab-strip.tsxpackages/ui/src/index.ts
✅ Files skipped from review due to trivial changes (2)
- packages/layout/src/index.ts
- packages/ui/src/index.ts
| ): RefObject<HTMLDivElement> { | ||
| const scrollContainerRef = useRef<HTMLDivElement>(null!); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Find hook call sites"
rg -n --type=ts --type=tsx '\buseScrollNewGridChildIntoView\s*\(' -C2
echo
echo "2) Find direct .current dereferences that may need null checks"
rg -n --type=ts --type=tsx '\bscrollContainerRef\.current\.[A-Za-z_]\w*' -C2Repository: sqlrooms/sqlrooms
Length of output: 131
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find the file first to confirm it exists
echo "=== Locating file ==="
find . -name "useScrollNewGridChildIntoView.ts" 2>/dev/null || echo "File not found"
echo
echo "=== Searching for hook call sites (corrected syntax) ==="
rg 'useScrollNewGridChildIntoView' -A2 -B2 || echo "No matches found"
echo
echo "=== Searching for scrollContainerRef usage ==="
rg 'scrollContainerRef' -A2 -B2 || echo "No matches found"Repository: sqlrooms/sqlrooms
Length of output: 7632
🏁 Script executed:
cat -n ./packages/layout/src/node-renderers/grid-node-renderer/useScrollNewGridChildIntoView.tsRepository: sqlrooms/sqlrooms
Length of output: 3435
🏁 Script executed:
cat -n ./packages/layout/src/node-renderers/grid-node-renderer/GridLayout.tsx | head -200Repository: sqlrooms/sqlrooms
Length of output: 8056
🏁 Script executed:
grep -n "scrollContainerRef" ./packages/layout/src/node-renderers/grid-node-renderer/GridLayout.tsxRepository: sqlrooms/sqlrooms
Length of output: 162
🏁 Script executed:
sed -n '495,510p' ./packages/layout/src/node-renderers/grid-node-renderer/GridLayout.tsx
sed -n '590,605p' ./packages/layout/src/node-renderers/grid-node-renderer/GridLayout.tsxRepository: sqlrooms/sqlrooms
Length of output: 1215
Fix type contract mismatch on returned ref
The return type RefObject<HTMLDivElement> claims non-nullable current, but initialization with useRef<HTMLDivElement>(null!) suppresses this guarantee. While the hook properly null-checks before accessing .current internally and the current call site passes the ref safely to a DOM element, the type lie could mislead future callers into unsafe dereferences.
Update the type to accurately reflect that current is nullable before mount:
Suggested fix
-): RefObject<HTMLDivElement> {
- const scrollContainerRef = useRef<HTMLDivElement>(null!);
+): RefObject<HTMLDivElement | null> {
+ const scrollContainerRef = useRef<HTMLDivElement | null>(null);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ): RefObject<HTMLDivElement> { | |
| const scrollContainerRef = useRef<HTMLDivElement>(null!); | |
| ): RefObject<HTMLDivElement | null> { | |
| const scrollContainerRef = useRef<HTMLDivElement | null>(null); |
🤖 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/layout/src/node-renderers/grid-node-renderer/useScrollNewGridChildIntoView.ts`
around lines 46 - 47, The hook useScrollNewGridChildIntoView currently declares
a non-nullable RefObject<HTMLDivElement> but initializes scrollContainerRef with
useRef<HTMLDivElement>(null!), which lies about nullability; change the hook's
return type to RefObject<HTMLDivElement | null> and initialize
scrollContainerRef with useRef<HTMLDivElement | null>(null) so the type
accurately reflects that current may be null before mount, keeping internal
null-checks as-is and leaving callers that pass the ref to DOM elements
unchanged.
Solving #607
Summary by CodeRabbit
New Features
Public API
Tests