Upgrade Guide
This document provides detailed guidance for upgrading between different versions of SQLRooms packages. Each section outlines breaking changes, required code modifications, and implementation examples to ensure a smooth upgrade process.
When upgrading, please follow the version-specific instructions below that apply to your project. If you encounter any issues during the upgrade process, please refer to our GitHub issues or contact support.
0.29.0 (upcoming)
@sqlrooms/artifacts: "Sheets" terminology migrated to "Artifacts" (breaking)
The concept of "sheets" has been replaced with "artifacts" to better represent the variety of content types (app builders, charts, maps, etc.) that can be created and managed.
API Changes
Store namespace:
state.sheets→state.artifactscreateSheetsSlice→createArtifactsSliceSheetsSlice→ArtifactsSlice
Component renames:
Sheets→ArtifactsSheetsTabs→ArtifactTabsSheetsPanel→ArtifactsPanel
Type renames:
Sheet→ArtifactSheetType→ArtifactType
Migration Example
Before:
import {createSheetsSlice, SheetsSlice} from '@sqlrooms/sheets';
type RoomState = SheetsSlice & ...;
const store = createRoomStore<RoomState>((set, get, store) => ({
...createSheetsSlice()(set, get, store),
}));
const sheets = useRoomStore((state) => state.sheets.items);After:
import {createArtifactsSlice, ArtifactsSlice} from '@sqlrooms/artifacts';
type RoomState = ArtifactsSlice & ...;
const store = createRoomStore<RoomState>((set, get, store) => ({
...createArtifactsSlice()(set, get, store),
}));
const artifacts = useRoomStore((state) => state.artifacts.items);@sqlrooms/layout, @sqlrooms/layout-config: Layout config refactored (breaking)
This release introduces explicit panel identity and dock boundaries, replacing the previous path-based panel lookup system.
Removed APIs
getPanelByPath: Path-based panel lookup function removeduseGetPanelByPath: Hook for path-based panel lookup removeduseGetPanelInfoByPath: Hook for path-based panel info removeddraggableproperty: Removed from split and tabs nodespathSegmentproperty: Removed from split and tabs nodes
@sqlrooms/ai-core, @sqlrooms/ai: Upgraded to AI SDK v6 with ToolLoopAgent (breaking)
The AI SDK dependency has been upgraded from v5 to v6. Tool execution now uses ToolLoopAgent instead of streamText. If you only use createAiSlice without customization, no changes are needed — the transport layer is updated internally.
Sub-agent composition
The tool-as-agent pattern now uses ToolLoopAgent + streamSubAgent:
// Before (v5)
const result = await streamText({
model,
system: instructions,
messages: [{role: 'user', content: prompt}],
tools,
maxSteps: 10,
});
// After (v6)
import {ToolLoopAgent, stepCountIs} from 'ai';
import {streamSubAgent} from '@sqlrooms/ai';
const agent = new ToolLoopAgent({
model,
instructions,
tools,
stopWhen: stepCountIs(10),
temperature: 0,
});
const resultText = await streamSubAgent(agent, prompt, abortSignal);addToolResult → addToolOutput
// Before
addToolResult({toolCallId, result: {...}});
// After — note: `tool` is a new required field in v6
addToolOutput({tool: toolName, toolCallId, output: {...}});ToolRendererProps new states
Tool renderers may now receive three additional states for approval workflows: approval-requested, approval-responded, and output-denied. Update any exhaustive switch/if-else on state in custom renderers.
Remote transport
If you use createRemoteChatTransportFactory, your server-side route must migrate from streamText to ToolLoopAgent + createAgentUIStreamResponse. The transport now sends instructions, maxSteps, and temperature in the request body.
Note: The
ai-nextjsexample shows a reference implementation that intentionally ignores these client-supplied fields and uses server-controlled defaults for security. Production endpoints should decide whether to trust client-supplied values forinstructions,maxSteps, andtemperaturebased on their security model.
@sqlrooms/kepler: initialKeplerState was replaced with createInitialMapKeplerState (breaking)
createKeplerSlice() no longer accepts a static initialKeplerState object.
Use createInitialMapKeplerState instead. It is called whenever a map's kepler state is initialized, so consumers can override the default map state and, if needed, derive things like the basemap style from the current theme by calling getTheme().
Before
createKeplerSlice({
initialKeplerState: {
mapStyle: {
styleType: 'positron',
},
},
});After
import {getTheme} from '@sqlrooms/ui';
createKeplerSlice({
createInitialMapKeplerState: ({defaultInitialMapKeplerState}) => ({
...defaultInitialMapKeplerState,
mapStyle: {
...defaultInitialMapKeplerState.mapStyle,
styleType: getTheme() === 'dark' ? 'dark-matter' : 'positron',
},
}),
});@sqlrooms/ui: toast export now uses Sonner (breaking)
The top-level toast export from @sqlrooms/ui now points to Sonner's API.
- Before:
toast({...})used SQLRooms' legacy Radix-based object API. - After:
toast.success(...),toast.error(...), etc. use Sonner.
If you still need the old API temporarily, import legacyToast from @sqlrooms/ui.
Before
import {toast} from '@sqlrooms/ui';
toast({
variant: 'default',
title: 'Table created',
description: 'File loaded',
});After (Sonner)
import {toast} from '@sqlrooms/ui';
toast.success('Table created', {
description: 'File loaded',
});Temporary compatibility option
import {legacyToast} from '@sqlrooms/ui';
legacyToast({
variant: 'default',
title: 'Table created',
description: 'File loaded',
});@sqlrooms/ai, @sqlrooms/vega, @sqlrooms/ai-rag: Tools migrated to native AI SDK format (breaking)
All built-in tools and the tool authoring API now use the AI SDK's tool() factory instead of the OpenAssistant format. The @openassistant/utils dependency has been removed.
Custom tools: before
import {z} from 'zod';
const myTool = {
name: 'my_tool',
description: 'Does something',
parameters: z.object({text: z.string()}),
execute: async ({text}) => ({
llmResult: {success: true, details: `Result: ${text}`},
additionalData: {processed: text},
}),
component: MyToolResult, // renderer attached to tool
};Custom tools: after
import {tool} from 'ai';
import {z} from 'zod';
const myTool = tool({
description: 'Does something',
inputSchema: z.object({text: z.string()}),
execute: async ({text}) => ({
// flat output — no llmResult / additionalData nesting
success: true,
details: `Result: ${text}`,
processed: text,
}),
// optional: control what the LLM sees (defaults to full JSON)
toModelOutput: ({output}) => ({type: 'text', value: output.details}),
});
// renderer is registered separately — see toolRenderers below@sqlrooms/ai-core, @sqlrooms/ai: Tool renderers decoupled from tools (breaking)
Tool renderers (component) are no longer attached to individual tools. They are now registered once in createAiSlice via the new toolRenderers option, typed against the tools map.
Before
createAiSlice({
tools: {
query: createQueryTool(store), // had component: QueryToolResult
chart: createVegaChartTool(), // had component: VegaChartToolResult
},
// ...
});After
import {createDefaultAiTools, createDefaultAiToolRenderers} from '@sqlrooms/ai';
import {VegaChartToolResult} from '@sqlrooms/vega';
createAiSlice({
tools: {
...createDefaultAiTools(store),
chart: createVegaChartTool(),
},
toolRenderers: {
...createDefaultAiToolRenderers(), // includes QueryToolResult
chart: VegaChartToolResult,
// myCustomTool: MyCustomToolResult,
},
// ...
});@sqlrooms/ai: Tool output type renames (breaking)
The llmResult/additionalData split has been replaced with a single flat output type per tool.
| Package | Old type | New type |
|---|---|---|
@sqlrooms/ai | QueryToolLlmResult + QueryToolAdditionalData | QueryToolOutput |
@sqlrooms/ai | QueryToolOutput.errorMessage | QueryToolOutput.error |
@sqlrooms/vega | VegaChartToolLlmResult + VegaChartToolAdditionalData | VegaChartToolOutput |
@sqlrooms/vega | VegaChartToolArgs (type alias) | removed — use VegaChartToolParameters |
@sqlrooms/vega | VegaChartToolContext | removed |
@sqlrooms/ai-rag | RagToolAdditionalData + RagToolContext | removed — use RagToolOutput |
@sqlrooms/ai: QueryToolResult props changed (breaking)
QueryToolResult now receives ToolRendererProps<QueryToolOutput, QueryToolParameters> instead of standalone props. If you render it directly, update the call-site:
Before
<QueryToolResult
title="My query"
sqlQuery={sql}
showSql={false}
formatValue={myFormatter}
/>After
Use createQueryToolRenderer and register it in toolRenderers:
import {createQueryToolRenderer} from '@sqlrooms/ai';
toolRenderers: {
query: createQueryToolRenderer({showSql: false, formatValue: myFormatter}),
}@sqlrooms/vega: createVegaChartTool options removed (breaking)
The embedOptions, editable, and editorMode options have been removed from createVegaChartTool. Pass them directly as props to VegaChartToolResult instead.
Before
createVegaChartTool({editable: false, editorMode: 'sql'});After
import {VegaChartToolResult} from '@sqlrooms/vega';
// In your toolRenderers:
toolRenderers: {
chart: (props) => <VegaChartToolResult {...props} editable={false} editorMode="sql" />,
}@sqlrooms/ai-rag: ragToolRenderer exported separately (breaking)
The RAG tool renderer is no longer attached to the tool. Import and register it explicitly.
Before
// renderer was bundled inside createRagTool() as `component`
tools: {
search_documentation: createRagTool();
}After
import {createRagTool, ragToolRenderer} from '@sqlrooms/ai-rag';
tools: {search_documentation: createRagTool()},
toolRenderers: {search_documentation: ragToolRenderer},@sqlrooms/ai-core: In-chat tool result editing removed (breaking)
The setSessionToolAdditionalData API and the toolAdditionalData session field have been removed. In-chat editing of tool results (e.g. inline Vega chart spec editing) is no longer supported — charts and other tool outputs are now rendered read-only within the chat.
// Before
state.ai.setSessionToolAdditionalData(sessionId, toolCallId, data);
// After — remove the call entirely; no replacement needed.If you were using toolAdditionalData to persist user edits to charts, extract the chart into a first-class entity stored independently of the chat instead. Persisted sessions with toolAdditionalData are automatically cleaned up on load.
@sqlrooms/ai: Remote transport — drop data-tool-additional-output custom chunk (breaking)
If you have a custom Next.js (or other server-side) route that manually wrote data-tool-additional-output data chunks to ferry additionalData to the client, you can remove that code entirely.
Before
// app/api/chat/route.ts
result.pipeThrough(
new TransformStream({
async onChunk({chunk}) {
if (chunk.type === 'tool-result') {
writer.write({
type: 'data-tool-additional-output',
transient: true,
data: {
toolCallId: chunk.toolCallId,
toolName: chunk.toolName,
output: getToolAdditionalData(chunk.toolCallId),
},
});
}
},
}),
);After
Remove the onChunk handler. createAgentUIStreamResponse embeds the full tool execute() output directly into the UIMessage stream as a tool-result part, which the renderer receives via ToolRendererProps.output. No side-channel is needed.
// chatTransport.ts — inside the local transport factory
return createAgentUIStreamResponse({
agent,
uiMessages: sanitizeMessagesForLLM(fixIncompleteToolCalls(messagesCopy)),
abortSignal,
});Why this works: Previously, execute() returned {llmResult, additionalData} — the UI data (additionalData) was separate and had to be sent manually. Now execute() returns a single flat output object. The AI SDK propagates the full output to the client through the standard UIMessage parts, so ToolRendererProps.output is populated automatically without any custom data chunks.
@sqlrooms/ai-core: Removed exports
convertToAiSDKTools— removed (tools are now native AI SDK tools)findToolComponent— replaced byfindToolRendererVegaChartToolParametersTypefrom@sqlrooms/vega— removed (useVegaChartToolParametersdirectly)
@sqlrooms/layout, @sqlrooms/layout-config: Layout config refactored (breaking)
The layout system has been significantly refactored. LayoutConfig is now LayoutNode | null directly — the outer { type: 'mosaic', nodes: ... } wrapper is gone. Type names have been renamed from MosaicLayout* to Layout*, and react-resizable-panels now handles all layout rendering.
Limited automatic migration: The Zod schema uses z.preprocess to detect and convert only legacy binary tree formats ({first, second, direction, splitPercentage?}) to the new n-ary format with children arrays.
Manual migration required for:
- The outer
{ type: 'mosaic', nodes: ... }wrapper (must be removed) - N-ary
splitPercentagesarrays on nodes that already usechildrenarrays (must be converted to per-childdefaultSize) - Any other v1 layout formats not in binary tree shape
Layout config: remove the outer wrapper
The { type: 'mosaic', nodes: ... } wrapper and LayoutTypes enum are no longer needed.
Before
import {LayoutTypes} from '@sqlrooms/layout-config';
const layout = {
type: LayoutTypes.enum.mosaic,
nodes: {
type: 'split',
direction: 'row',
children: ['data', 'main'],
splitPercentages: [30, 70],
},
};After
import {LayoutConfig} from '@sqlrooms/layout-config';
const layout: LayoutConfig = {
type: 'split',
direction: 'row',
children: [
{type: 'panel', id: 'data', defaultSize: '30%'},
{type: 'panel', id: 'main', defaultSize: '70%'},
],
};splitPercentages replaced by per-node sizing
splitPercentages and savedPercentages on split nodes have been removed. Sizing is now specified on individual child nodes via defaultSize, minSize, maxSize, collapsedSize, and collapsible.
Before
{
type: 'split',
direction: 'row',
children: ['sidebar', 'main'],
splitPercentages: [25, 75],
}After
{
type: 'split',
direction: 'row',
children: [
{type: 'panel', id: 'sidebar', defaultSize: '25%', minSize: '150px'},
'main',
],
}Size values accept CSS units ('200px', '25%', '1rem'). A plain string without a unit suffix is treated as a percentage.
New type: 'panel' leaf node
A new panel node type allows specifying sizing constraints on individual panels:
{
type: 'panel',
id: 'sidebar',
defaultSize: '25%',
minSize: '150px',
maxSize: '50%',
collapsedSize: '0px',
collapsible: true,
}Plain string keys (e.g. 'main') still work as leaf nodes without sizing constraints.
Type renames
All MosaicLayout* types have been renamed to Layout*. The old names are still exported as deprecated aliases.
| Old name | New name |
|---|---|
MosaicLayoutConfig | LayoutConfig |
MosaicLayoutNode | LayoutNode |
MosaicLayoutSplitNode | LayoutSplitNode |
MosaicLayoutTabsNode | LayoutTabsNode |
MosaicLayoutMosaicNode | LayoutMosaicNode |
MosaicLayoutParent | LayoutSplitNode |
MosaicLayoutDirection | LayoutDirection |
MosaicLayoutNodeKey | LayoutNodeKey |
isMosaicLayoutParent() | isLayoutSplitNode() |
isMosaicLayoutSplitNode() | isLayoutSplitNode() |
isMosaicLayoutTabsNode() | isLayoutTabsNode() |
isMosaicLayoutMosaicNode() | isLayoutMosaicNode() |
createDefaultMosaicLayout() | createDefaultLayout() |
DEFAULT_MOSAIC_LAYOUT | (removed) |
LayoutTypes | (removed) |
Deprecated helper renames in @sqlrooms/layout:
| Old name | New name |
|---|---|
makeMosaicStack | makeLayoutStack |
visitMosaicLeafNodes | visitLayoutLeafNodes |
getVisibleMosaicLayoutPanels | getVisibleLayoutPanels |
findMosaicNodePathByKey | findLayoutNodePathByKey |
removeMosaicNodeByKey | removeLayoutNodeByKey |
Panel placement is deprecated
The placement property on panel info ('sidebar', 'main', etc.) is deprecated and no longer used. Panel location is now determined entirely by the layout tree structure, not by a property on the panel definition.
Before
panels: {
data: {title: 'Data', component: DataPanel, placement: 'sidebar'},
}After
panels: {
data: {title: 'Data', component: DataPanel},
}Panel location is controlled by the layout configuration structure (e.g., which split, tabs, or panel node references the panel key).
New LayoutRenderer component
LayoutRenderer is the new top-level renderer that handles all node types (split, tabs, mosaic, panel, and string leaves). MosaicLayout is still available for rendering mosaic-only sub-trees.
Render callbacks API
createLayoutSlice now accepts renderPanel and renderTabStrip callbacks for custom rendering:
createLayoutSlice({
config: {
/* ... */
},
panels: {
/* ... */
},
renderPanel: (context) => {
// Return custom JSX or undefined to use default
},
renderTabStrip: (context) => {
// Return custom tab strip JSX or undefined for default
},
});Panel padding removed from LeafLayoutPanel (breaking)
LeafLayoutPanel no longer applies p-2 padding by default. Panel components must now add their own padding.
Before
Panel content inherited p-2 padding from LeafLayoutPanel:
export const MyPanel: RoomPanelComponent = () => {
return <div>My content</div>;
};After
Add p-2 to your panel component:
export const MyPanel: RoomPanelComponent = () => {
return <div className="p-2">My content</div>;
};Area-based panel management
Named tabs nodes (with an id) act as areas with new management methods:
state.layout.setActivePanel(areaId, panelId);
state.layout.addPanelToArea(areaId, panelId);
state.layout.removePanelFromArea(areaId, panelId);
state.layout.setAreaCollapsed(areaId, collapsed);
state.layout.toggleAreaCollapsed(areaId);
state.layout.getAreaPanels(areaId);
state.layout.getActivePanel(areaId);
state.layout.isAreaCollapsed(areaId);react-mosaic-component removed
react-mosaic-component has been removed and replaced with react-resizable-panels for all layout rendering. The layout tree structure changed from a binary format (first/second) to an n-ary format (children[]). Binary tree layouts are migrated automatically via z.preprocess, but other formats require manual migration (see above).
0.28.0
Tailwind v3 to v4
Tailwind in SQLRooms is now upgraded from v3 to v4.
For the full migration checklist and additional breaking changes, see the official Tailwind upgrade guide: https://tailwindcss.com/docs/upgrade-guide.
You can use the official migration tool directly in your repository:
npx @tailwindcss/upgradeManual steps
The main migration step is moving template/content discovery from tailwind.config.js into your global CSS using @source directives (see examples/query/src/index.css for a complete example).
Step 1
Move content paths from tailwind.config.js to global css index.css. Also, add index.html and pay attention to relative paths since index.css is usually located under src/ folder while tailwind.config.js is in the root.
/* index.css */
@import 'tailwindcss';
@import '@sqlrooms/ui/tailwind-preset.css';
@source '../index.html';
@source './**/*.{ts,tsx}';
@source '../node_modules/@sqlrooms/*/dist/';
/* styles */Step 2
Remove tailwind.config.js
Step 3
Remove @layer base { ... } from index.css
Before:
/* index.css */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
/* ... */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... */
}
}After:
/* index.css */
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
/* ... */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... */
}Step 4: For Vite projects
- Install
@tailwindcss/viteand add it to yourvite.config.jsfile,
pnpm add -D @tailwindcss/vite// vite.config.js
import {defineConfig} from 'vite';
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
});- Remove
autoprefixerandpostcss - Remove
postcss.config.js
Step 4: For NextJS projects
Update postcss.config.js
Before:
// postcss.config.js
const config = {
plugins: ['@tailwindcss/postcss'],
};
export default config;After:
// postcss.config.js
const config = {
plugins: {
'@tailwindcss/postcss': {},
},
};
export default config;0.27.0-rc.0
@sqlrooms/mosaic
useMosaichook removed: UseMosaicSliceanduseMosaicClientinstead
The useMosaic hook has been replaced with a more robust slice-based architecture. You now need to:
- Add
MosaicSliceto your room store - Check connection status via the store
- Use
useMosaicClientfor reactive data queries
Before
import {useMosaic} from '@sqlrooms/mosaic';
function MyComponent() {
const {isMosaicLoading, mosaicConnector} = useMosaic();
if (isMosaicLoading) {
return <div>Loading...</div>;
}
// Use mosaicConnector directly
// ...
}After
Step 1: Add MosaicSlice to your store
import {createMosaicSlice, MosaicSliceState} from '@sqlrooms/mosaic';
import {createRoomStore, RoomShellSliceState} from '@sqlrooms/room-shell';
export type RoomState = RoomShellSliceState & MosaicSliceState;
export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
(set, get, store) => ({
// ... other slices
...createMosaicSlice()(set, get, store),
}),
);Step 2: Check connection status via store
import {useRoomStore} from './store';
function MyComponent() {
const mosaicConn = useRoomStore((state) => state.mosaic.connection);
if (mosaicConn.status === 'loading') {
return <div>Loading...</div>;
}
if (mosaicConn.status === 'error') {
return <div>Error: {mosaicConn.error.message}</div>;
}
// Mosaic is ready when status === 'ready'
// Access connector via mosaicConn.connector if needed
}Step 3: Use useMosaicClient for reactive queries
import {Query, useMosaicClient} from '@sqlrooms/mosaic';
function MapView() {
const {data, isLoading, client} = useMosaicClient({
selectionName: 'brush',
query: (filter: any) => {
return Query.from('earthquakes')
.select('Latitude', 'Longitude', 'Magnitude')
.where(filter);
},
});
if (isLoading) {
return <div>Loading data...</div>;
}
// Use data for visualization
return <div>Data loaded: {data?.numRows} rows</div>;
}For more details, see the Mosaic API documentation and the DeckGL + Mosaic example.
@sqlrooms/ai
Per-session chat + analysis state
AI chat state is now scoped per session (instead of a single global chat instance). This enables multiple sessions to stream concurrently without overwriting each other when you switch sessions.
- Removed global state:
state.ai.prompt,state.ai.isRunning(now per-session) - Breaking method signature changes:
startAnalysis(sendMessage)→startAnalysis(sessionId)cancelAnalysis()→cancelAnalysis(sessionId)
- New per-session accessors:
getPrompt(sessionId)/setPrompt(sessionId, prompt)getIsRunning(sessionId)/setIsRunning(sessionId, isRunning)
- New hook:
useSessionChat(sessionId)for session-scoped chat (replaces legacy single-instance patterns) - Mounting requirement: if you render AI primitives directly (e.g.
QueryControls,AnalysisResultsContainer) you must mount chat providers once viaChat.Root(it mountsSessionChatManager).
Before
const prompt = useRoomStore((s) => s.ai.prompt);
const isRunning = useRoomStore((s) => s.ai.isRunning);
// startAnalysis used to take a sendMessage fn (global chat instance)
await useRoomStore.getState().ai.startAnalysis(sendMessage);After
const currentSession = useRoomStore((s) => s.ai.getCurrentSession());
const sessionId = currentSession?.id;
const prompt = useRoomStore((s) =>
sessionId ? s.ai.getPrompt(sessionId) : '',
);
const isRunning = useRoomStore((s) =>
sessionId ? s.ai.getIsRunning(sessionId) : false,
);
if (sessionId) {
await useRoomStore.getState().ai.startAnalysis(sessionId);
}Recommended UI composition
Use Chat.Root once at the top of your AI UI tree (it mounts SessionChatManager):
import {Chat} from '@sqlrooms/ai';
export function MyAiPanel() {
return (
<Chat.Root>
<Chat.Sessions />
<Chat.Messages />
<Chat.Composer />
</Chat.Root>
);
}0.26.0-rc.5
There's no combined config in the store anymore. We decided to split the config into individual slices' configs to avoid confusion and simplify the store typing.
state.config.title -> state.room.config.title state.config.dataSources -> state.room.config.dataSources state.config.sqlEditor -> state.sqlEditor.config state.config.layout -> state.layout.config ...If you were saving the combined config, make sure to update the persistence logic (check out the examples).
createStore, createSlice now only have one generic type parameter
room.setRoomConfig removed, use .setConfig in all individual slices
RoomState renamed to BaseRoomStoreState (meant to be internal) and RoomStore interface renamed to BaseRoomStore to avoid confusion with RoomState/RoomStore introduced in many of the examples
room.onSaveConfig, hasUnsavedChanges, lastSavedConfig were removed.
0.25.0-rc.1
- createAiSlice init parameters changed:
- Instead of customTools and toolsOptions use tools + createDefaultAiTools(store, toolsOptions)
- getInstructions must be provided, but can use createDefaultAiInstructions(store)
0.24.28-rc.1
- Discuss config separated from RoomConfig to make it easier to persist separately and to simplify typing (
state.discuss.configinstead ofstate.config.discuss)
const discussConfig = useRoomStore((state) => state.discuss.config);After:
const discussConfig = useRoomStore((state) => state.config.discuss);If you were persisting this state, you will likely need a migration.
You should also remove .merge(DiscussSliceConfig) when defining your RoomConfig
0.19.0
We are trying to make the package structure more logical, especially, for new users of the SQLRooms framework. Sorry for the more renaming.
Package
@sqlrooms/core(previously,@sqlrooms/project) renamed to@sqlrooms/room-store.The layout-related state and functions were moved to the new
LayoutSliceadded to@sqlrooms/layoutwhich is namespaced aslayout:panelssetLayouttogglePaneltooglePanelPin
Before:
const togglePanel = useRoomStore((state) => state.room.togglePanel);After:
const togglePanel = useRoomStore((state) => state.layout.togglePanel);0.18.0
QueryHandle returned from .query() is now implementing PromiseLike and can be awaited. So adding .result, which was introduced in 0.16.0, is not necessary anymore.
Old
const result = await connector.query('SELECT * FROM some_table').result;New
const result = await connector.query('SELECT * FROM some_table');0.17.0
This release focuses on standardizing terminology across the codebase and improving the developer experience for new users. We are replacing the concept of "project" with "room" to better align with the SQLRooms name. "Room" is an established concept in collaborative apps and fits well with the overall vision of the project.
Package name changes
@sqlrooms/projectrenamed to@sqlrooms/core(renamed again to@sqlrooms/room-storein 0.19.0, sorry)@sqlrooms/project-configrenamed to@sqlrooms/room-config@sqlrooms/project-builderrenamed to@sqlrooms/room-shell
Component name changes
ProjectBuilderis replaced byRoomShellProjectBuilderProvideris removed (in favor ofRoomShell)ProjectBuilderStaterenamed toRoomShellSliceStatecreateProjectBuilderStorerenamed tocreateRoomStorecreateProjectBuilderSlicerenamed tocreateRoomShellSliceProjectBuilderPanelrenamed toRoomPanelProjectBuilderPanelHeaderrenamed toRoomPanelHeader
Old way to set up a project
<ProjectBuilderProvider projectStore={projectStore}>
<div className="flex h-full w-full">
<div className="bg-muted/50 flex h-full flex-col px-1 py-2">
<ProjectBuilderSidebarButtons />
</div>
<div className="flex h-full w-full flex-col">
<ProjectBuilder />
</div>
</div>
</ProjectBuilderProvider>New
<RoomShell className="h-screen" roomStore={roomStore}>
<RoomShell.Sidebar />
<RoomShell.LayoutComposer />
<RoomShell.LoadingProgress />
<RoomShell.CommandPalette />
</RoomShell>State name changes
state.projectnamespace renamed tostate.room
Old
const dataSources = useProjectStore((state) => state.project.dataSources);New
const dataSources = useRoomStore((state) => state.room.dataSources);0.16.3
@sqlrooms/duckdb
The BaseDuckDbConnector and WasmDuckDbConnector are now provided as factory functions rather than classes. Use createWasmDuckDbConnector() or the generic createDuckDbConnector({type: 'wasm'}) to obtain a connector instance.
Before
import {WasmDuckDbConnector} from '@sqlrooms/duckdb';
const connector = new WasmDuckDbConnector();After
import {createWasmDuckDbConnector} from '@sqlrooms/duckdb';
const connector = createWasmDuckDbConnector();0.16.0
@sqlrooms/duckdb
The DuckDbConnector now supports query cancellation through a unified QueryHandle interface with full composability support. All query methods (execute, query, queryJson) now return a QueryHandle that provides immediate access to cancellation functionality and signal composability. Read more…
Old
const result = await connector.query('SELECT * FROM some_table');New
WARNING
Since 0.18.0 QueryHandle returned from .query() is implementing PromiseLike and can be awaited. So adding .result is not necessary anymore.
const result = await connector.query('SELECT * FROM some_table').result;0.14.0
@sqlrooms/ui
sqlroomsTailwindPresetprefix parameter was removed
0.9.0
@sqlrooms/project-builder
createProjectSlicerenamed intocreateProjectBuilderSlicecreateProjectStorerenamed intocreateProjectBuilderStoreProjectStaterenamed intoProjectBuilderStateprojectIdandsetProjectIdremoved: add custom state if necessaryINITIAL_BASE_PROJECT_STATErenamed intoINITIAL_PROJECT_BUILDER_STATEA number of project store props and moved from
.projectto.db:.tables.addTable.getTable.getTables.getTableRowCount.getTableSchema.getTableSchemas.checkTableExists.dropTable.createTableFromQuery.setTableRowCount.findTableByName.refreshTableSchemas
useBaseProjectStorewas renamed intouseBaseProjectBuilderStore, but it's better to useuseProjectStorereturned bycreateProjectBuilderStoreinsteadprocessDroppedFile()is removed: UseProjectStore.addProjectFiledirectly.ProjectStore.replaceProjectFileis removed: UseProjectStore.addProjectFileinstead.ProjectStore.addProjectFileparameter changes: The function now takes a File or a pathname instead of the result ofprocessDroppedFile().ProjectStore.addProjectFilebehavior changes: The function will no longer attempt to create unique table names, but will overwrite the created table.ProjectStore.areViewsReadyToRenderandonDataUpdatedwere removedProjectStore.setTablesremoved: usestate.db.refreshTableSchemas()instead.ProjectStore.isReadOnlywas removed: passisReadOnlyas a prop to respective components instead
@sqlrooms/duckdb
useDuckDb()now returns an instance ofDuckDbConnectorto enable support for external DuckDBgetDuckDbwas removed: UseuseDuckDb()insteadgetDuckTableSchemaswas removed: useconst getTableSchemas = useProjectStore(state => state.db.getTableSchemas)exportToCsvwas removed: UseuseExportToCsvinstead
@sqlrooms/mosaic
getMosaicConnectorremoved: UseuseMosaicinstead
@sqlrooms/ai
TOOLSis not exported anymore: useuseProjectStore(state => state.ai.tools)instead
0.8.0
@sqlrooms/project-builder
project.configmoved to top level ofProjectStore
This was done to simplify persistence. To migrate you need to pull it up in your slice creation code.
Before:
const {projectStore, useProjectStore} = createProjectStore<
RoomConfig,
RoomState
>(
(set, get, store) => ({
...createProjectSlice<RoomConfig>({
project: {
config: {
...
},
...
}
})
})
);After:
const {projectStore, useProjectStore} = createProjectStore<
RoomConfig,
RoomState
>(
(set, get, store) => ({
...createProjectSlice<RoomConfig>({
config: {
...
},
project: {
...
}
})
})
);Check the AI example store code.
@sqlrooms/ai
- Model provider in
getApiKey
getApiKey property of createAiSlice now takes modelProvider:
...createAiSlice({
getApiKey: (modelProvider: string) => {
return get()?.apiKeys[modelProvider] || '';
},
})(set, get, store),- Combining
useScrollToBottomanduseScrollToBottomButton
useScrollToBottom is now combined with useScrollToBottomButton. useScrollToBottom now takes dataToObserve, containerRef, endRef. When the data changes, the hook will scroll to the bottom of the container.
- Vega Chart Tool is now a custom tool
The Vega Chart Tool is no longer included by default and must be explicitly provided as a custom tool to createAiSlice. You need to import it from @sqlrooms/vega and add it to the customTools object:
import {createVegaChartTool} from '@sqlrooms/vega';
...createAiSlice({
getApiKey: (modelProvider: string) => {
return get()?.apiKeys[modelProvider] || '';
},
// Add custom tools
customTools: {
// Add the VegaChart tool from the vega package
chart: createVegaChartTool(),
// Other custom tools...
},
})(set, get, store),This change allows for more flexibility in configuring the chart tool and reduces bundle size for applications that don't need chart functionality.
