@sqlrooms/room-shell 0.28.0-rc.0 → 0.28.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,704 +1,171 @@
1
- A powerful framework for building and managing data rooms in SQLRooms. This package provides components and utilities for creating, configuring, and managing data rooms with an intuitive user interface.
1
+ Main SQLRooms application shell and default Room slice composition.
2
2
 
3
- ## Features
3
+ `@sqlrooms/room-shell` bundles:
4
4
 
5
- - 🏗️ **Room Structure**: Tools for defining and managing room structure
6
- - 📊 **Data Sources**: Components for connecting to and managing data sources
7
- - 🧩 **Panel System**: Flexible panel-based UI for room components
8
- - 🔄 **State Management**: Robust state management using Zustand
9
- - 📁 **File Handling**: Utilities for processing and managing room files
10
- - 🧰 **Extensible Architecture**: Easily extend with custom components and panels
5
+ - base room lifecycle (`room-store`)
6
+ - DuckDB slice (`@sqlrooms/duckdb`)
7
+ - layout slice (`@sqlrooms/layout`)
8
+ - React shell UI (`RoomShell`, sidebar/layout/loading components)
9
+
10
+ Use this package as the default entry point for most SQLRooms apps.
11
11
 
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- npm install @sqlrooms/room-shell
16
- # or
17
- yarn add @sqlrooms/room-shell
15
+ npm install @sqlrooms/room-shell @sqlrooms/duckdb @sqlrooms/ui
18
16
  ```
19
17
 
20
- ## Basic Usage
21
-
22
- ### Creating a Room Builder
23
-
24
- ```tsx
25
- import {RoomShell} from '@sqlrooms/room-shell';
26
-
27
- function MyApp() {
28
- return (
29
- <RoomShell className="h-screen" roomStore={roomStore}>
30
- <RoomShell.Sidebar />
31
- <RoomShell.LayoutComposer />
32
- <RoomShell.LoadingProgress />
33
- </RoomShell>
34
- );
35
- }
36
- ```
37
-
38
- ### Working with Room State
39
-
40
- The room-shell package uses Zustand for state management. You can create a custom store with room-specific state and actions.
18
+ ## Quick start
41
19
 
42
20
  ```tsx
43
21
  import {
44
22
  createRoomShellSlice,
45
23
  createRoomStore,
24
+ LayoutTypes,
25
+ RoomShell,
46
26
  RoomShellSliceState,
47
27
  } from '@sqlrooms/room-shell';
28
+ import {DatabaseIcon} from 'lucide-react';
48
29
 
49
- // Define your custom app state
50
- type MyRoomState = RoomShellSliceState & {
51
- myFeatureData: any[];
52
- addItem: (item: any) => void;
53
- removeItem: (id: string) => void;
54
- };
30
+ function DataPanel() {
31
+ return <div className="p-2">Data panel</div>;
32
+ }
33
+
34
+ function MainPanel() {
35
+ return <div className="p-2">Main panel</div>;
36
+ }
55
37
 
56
- // Create a room store with custom state
57
- export const {roomStore, useRoomStore} = createRoomStore<MyRoomState>(
38
+ type RoomState = RoomShellSliceState;
39
+
40
+ export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
58
41
  (set, get, store) => ({
59
- // Base room slice with initial configuration
60
42
  ...createRoomShellSlice({
61
43
  config: {
62
- title: 'My Room',
63
- dataSources: [],
44
+ title: 'My SQLRooms App',
45
+ dataSources: [
46
+ {
47
+ type: 'url',
48
+ tableName: 'earthquakes',
49
+ url: 'https://huggingface.co/datasets/sqlrooms/earthquakes/resolve/main/earthquakes.parquet',
50
+ },
51
+ ],
64
52
  },
65
53
  layout: {
66
54
  config: {
67
- /* layout configuration */
55
+ type: LayoutTypes.enum.mosaic,
56
+ nodes: {
57
+ direction: 'row',
58
+ first: 'data',
59
+ second: 'main',
60
+ splitPercentage: 28,
61
+ },
68
62
  },
69
63
  panels: {
70
- /* panel definitions */
64
+ data: {
65
+ title: 'Data',
66
+ icon: DatabaseIcon,
67
+ component: DataPanel,
68
+ placement: 'sidebar',
69
+ },
70
+ main: {
71
+ title: 'Main',
72
+ icon: () => null,
73
+ component: MainPanel,
74
+ placement: 'main',
75
+ },
71
76
  },
72
77
  },
73
78
  })(set, get, store),
74
-
75
- // Custom state and actions
76
- myFeatureData: [],
77
- addItem: (item) =>
78
- set((state) => ({
79
- myFeatureData: [...state.myFeatureData, item],
80
- })),
81
- removeItem: (id) =>
82
- set((state) => ({
83
- myFeatureData: state.myFeatureData.filter((item) => item.id !== id),
84
- })),
85
79
  }),
86
80
  );
87
81
 
88
- // Use the store in a component with selector for better performance
89
- function MyComponent() {
90
- // Use selectors for better performance
91
- const myFeatureData = useRoomStore((state) => state.myFeatureData);
92
- const addItem = useRoomStore((state) => state.addItem);
93
-
82
+ export function App() {
94
83
  return (
95
- <div>
96
- <h2>My Items ({myFeatureData.length})</h2>
97
- <button onClick={() => addItem({id: Date.now(), name: 'New Item'})}>
98
- Add Item
99
- </button>
100
- {/* Render items */}
101
- </div>
84
+ <RoomShell roomStore={roomStore} className="h-screen">
85
+ <RoomShell.Sidebar />
86
+ <RoomShell.LayoutComposer />
87
+ <RoomShell.LoadingProgress />
88
+ <RoomShell.CommandPalette />
89
+ </RoomShell>
102
90
  );
103
91
  }
104
92
  ```
105
93
 
106
- ### Persisting Room Configuration
107
-
108
- The room configuration is designed to be persisted between sessions. You can use the `persistSliceConfigs` helper to save the configuration to localStorage or any other storage:
109
-
110
- ```tsx
111
- import {
112
- createRoomShellSlice,
113
- createRoomStore,
114
- RoomShellSliceState,
115
- BaseRoomConfig,
116
- LayoutConfig,
117
- persistSliceConfigs,
118
- } from '@sqlrooms/room-shell';
119
-
120
- // Define your custom app state
121
- type MyRoomState = RoomShellSliceState & {
122
- myFeatureData: any[];
123
- addItem: (item: any) => void;
124
- };
125
-
126
- // Create a store with persistence
127
- export const {roomStore, useRoomStore} = createRoomStore<MyRoomState>(
128
- persistSliceConfigs(
129
- {
130
- name: 'my-room-storage',
131
- sliceConfigSchemas: {
132
- room: BaseRoomConfig,
133
- layout: LayoutConfig,
134
- },
135
- },
136
- (set, get, store) => ({
137
- // Base room slice
138
- ...createRoomShellSlice({
139
- config: {
140
- title: 'My Room',
141
- dataSources: [],
142
- },
143
- layout: {
144
- config: {
145
- /* layout configuration */
146
- },
147
- panels: {
148
- /* panel definitions */
149
- },
150
- },
151
- })(set, get, store),
152
-
153
- // Custom state and actions
154
- myFeatureData: [],
155
- addItem: (item) =>
156
- set((state) => ({
157
- myFeatureData: [...state.myFeatureData, item],
158
- })),
159
- }),
160
- ),
161
- );
162
- ```
163
-
164
- ### Integrating Multiple Feature Slices
165
-
166
- For larger applications, you can organize your state into feature slices:
167
-
168
- ```tsx
169
- import {
170
- createRoomShellSlice,
171
- createRoomStore,
172
- RoomShellSliceState,
173
- } from '@sqlrooms/room-shell';
174
- import {createMyFeatureSlice, MyFeatureState} from './myFeatureSlice';
175
- import {
176
- createAnotherFeatureSlice,
177
- AnotherFeatureState,
178
- } from './anotherFeatureSlice';
179
-
180
- // Combined app state type
181
- type MyRoomState = RoomShellSliceState & MyFeatureState & AnotherFeatureState;
182
-
183
- // Create a store with multiple slices
184
- export const {roomStore, useRoomStore} = createRoomStore<MyRoomState>(
185
- (set, get, store) => ({
186
- // Base room slice
187
- ...createRoomShellSlice({
188
- config: {
189
- /* initial config */
190
- },
191
- layout: {
192
- config: {
193
- /* layout configuration */
194
- },
195
- panels: {
196
- /* panel definitions */
197
- },
198
- },
199
- })(set, get, store),
200
-
201
- // Feature slices
202
- ...createMyFeatureSlice()(set, get, store),
203
- ...createAnotherFeatureSlice({
204
- // Feature-specific options
205
- customOption: 'value',
206
- })(set, get, store),
207
- }),
208
- );
209
- ```
210
-
211
- ### Managing Data Sources
94
+ ## Common room actions
212
95
 
213
96
  ```tsx
214
- import {
215
- FileDataSourcesPanel,
216
- TablesListPanel,
217
- DataSourceType,
218
- } from '@sqlrooms/room-shell';
97
+ import {useRoomStore} from './store';
98
+ import {Button} from '@sqlrooms/ui';
219
99
 
220
- function DataSourcesSection() {
221
- // Use selectors for better performance
100
+ function RoomActions() {
101
+ const setRoomTitle = useRoomStore((state) => state.room.setRoomTitle);
222
102
  const addDataSource = useRoomStore((state) => state.room.addDataSource);
103
+ const removeDataSource = useRoomStore((state) => state.room.removeDataSource);
223
104
  const addRoomFile = useRoomStore((state) => state.room.addRoomFile);
224
105
 
225
- const handleFileDrop = async (files) => {
226
- for (const file of files) {
227
- await addRoomFile(file);
228
- }
229
- };
230
-
231
- const handleAddCsvUrl = (url) => {
232
- addDataSource({
233
- type: DataSourceType.url,
234
- url,
235
- tableName: 'data_from_url',
236
- });
237
- };
238
-
239
- const handleAddSqlQuery = (query) => {
240
- addDataSource({
241
- type: DataSourceType.sqlQuery,
242
- query,
243
- tableName: 'query_results',
244
- });
245
- };
246
-
247
106
  return (
248
- <div className="grid grid-cols-2 gap-4">
249
- <FileDataSourcesPanel onFileDrop={handleFileDrop} />
250
- <TablesListPanel />
251
- <button onClick={() => handleAddCsvUrl('https://example.com/data.csv')}>
252
- Add CSV from URL
253
- </button>
107
+ <div className="flex gap-2">
108
+ <Button onClick={() => setRoomTitle('Updated title')}>Rename room</Button>
109
+ <Button
110
+ onClick={() =>
111
+ void addDataSource({
112
+ type: 'sql',
113
+ tableName: 'top_quakes',
114
+ sqlQuery:
115
+ 'SELECT * FROM earthquakes ORDER BY Magnitude DESC LIMIT 100',
116
+ })
117
+ }
118
+ >
119
+ Add SQL data source
120
+ </Button>
121
+ <Button onClick={() => void removeDataSource('top_quakes')}>
122
+ Remove SQL data source
123
+ </Button>
124
+ <Button
125
+ onClick={async () => {
126
+ const file = new File(['id,name\n1,Alice'], 'people.csv', {
127
+ type: 'text/csv',
128
+ });
129
+ await addRoomFile(file);
130
+ }}
131
+ >
132
+ Add file
133
+ </Button>
254
134
  </div>
255
135
  );
256
136
  }
257
137
  ```
258
138
 
259
- ### Creating Custom Panels
260
-
261
- ```tsx
262
- import {RoomPanel, RoomPanelHeader} from '@sqlrooms/room-shell';
263
-
264
- function CustomPanel({title, children}) {
265
- return (
266
- <RoomPanel>
267
- <RoomPanelHeader title={title} />
268
- <div className="p-4">{children}</div>
269
- </RoomPanel>
270
- );
271
- }
272
- ```
273
-
274
- ## RoomStore API Reference
275
-
276
- The RoomStore is the core of the room-shell package. It provides a comprehensive set of properties and methods for managing room state.
277
-
278
- ### State Properties
279
-
280
- #### `room.config`
281
-
282
- The room configuration, which can be persisted between sessions.
283
-
284
- ```tsx
285
- const config = useRoomStore((state) => state.room.config);
286
- console.log(config.title); // Access room title
287
- ```
288
-
289
- #### `schema`
290
-
291
- The database schema name used for the room.
292
-
293
- ```tsx
294
- const schema = useRoomStore((state) => state.room.schema);
295
- ```
296
-
297
- #### `tasksProgress`
298
-
299
- A record of task progress information, useful for displaying loading indicators.
300
-
301
- ```tsx
302
- const tasksProgress = useRoomStore((state) => state.room.tasksProgress);
303
- // Example: { "init-db": { message: "Initializing database...", progress: 0.5 } }
304
- ```
305
-
306
- #### `roomId`
307
-
308
- The unique identifier for the room, undefined for new rooms.
309
-
310
- ```tsx
311
- const roomId = useRoomStore((state) => state.room.roomId);
312
- ```
313
-
314
- #### `panels`
315
-
316
- A record of panel information, including title, icon, component, and placement.
317
-
318
- ```tsx
319
- const panels = useRoomStore((state) => state.room.panels);
320
- // Example: { "data-sources": { title: "Data Sources", icon: DatabaseIcon, ... } }
321
- ```
322
-
323
- #### `isReadOnly`
324
-
325
- Whether the room is in read-only mode.
326
-
327
- ```tsx
328
- const isReadOnly = useRoomStore((state) => state.room.isReadOnly);
329
- ```
330
-
331
- #### `tables`
332
-
333
- An array of data tables available in the room.
334
-
335
- ```tsx
336
- const tables = useRoomStore((state) => state.room.tables);
337
- // Access table schemas and metadata
338
- ```
339
-
340
- #### `roomFiles`
341
-
342
- An array of room file information.
343
-
344
- ```tsx
345
- const roomFiles = useRoomStore((state) => state.room.roomFiles);
346
- ```
347
-
348
- #### `roomFilesProgress`
349
-
350
- A record of file processing progress information.
351
-
352
- ```tsx
353
- const roomFilesProgress = useRoomStore((state) => state.room.roomFilesProgress);
354
- ```
355
-
356
- #### `lastSavedConfig`
357
-
358
- The last saved room configuration, used to check for unsaved changes.
359
-
360
- ```tsx
361
- const lastSavedConfig = useRoomStore((state) => state.room.lastSavedConfig);
362
- ```
363
-
364
- #### `initialized`
365
-
366
- Whether the room has been initialized.
367
-
368
- ```tsx
369
- const initialized = useRoomStore((state) => state.room.initialized);
370
- ```
371
-
372
- #### `isDataAvailable`
373
-
374
- Whether the room data has been loaded.
375
-
376
- ```tsx
377
- const isDataAvailable = useRoomStore((state) => state.room.isDataAvailable);
378
- ```
379
-
380
- #### `dataSourceStates`
381
-
382
- A record of data source states by table name.
383
-
384
- ```tsx
385
- const dataSourceStates = useRoomStore((state) => state.room.dataSourceStates);
386
- ```
387
-
388
- #### `tableRowCounts`
389
-
390
- A record of row counts by table name.
391
-
392
- ```tsx
393
- const tableRowCounts = useRoomStore((state) => state.room.tableRowCounts);
394
- ```
395
-
396
- ### Methods
397
-
398
- #### `initialize()`
399
-
400
- Initialize the room state.
401
-
402
- ```tsx
403
- const initialize = useRoomStore((state) => state.room.initialize);
404
- await initialize();
405
- ```
406
-
407
- #### `setTaskProgress(id, taskProgress)`
408
-
409
- Set the progress of a task.
410
-
411
- ```tsx
412
- const setTaskProgress = useRoomStore((state) => state.room.setTaskProgress);
413
- setTaskProgress('my-task', {message: 'Processing...', progress: 0.5});
414
- ```
415
-
416
- #### `getLoadingProgress()`
417
-
418
- Get the current loading progress.
419
-
420
- ```tsx
421
- const getLoadingProgress = useRoomStore(
422
- (state) => state.room.getLoadingProgress,
423
- );
424
- const progress = getLoadingProgress();
425
- ```
426
-
427
- #### `setRoomConfig(config)`
428
-
429
- Set the room configuration.
430
-
431
- ```tsx
432
- const setRoomConfig = useRoomStore((state) => state.room.setRoomConfig);
433
- const config = useRoomStore((state) => state.config);
434
- setRoomConfig({...config, title: 'New Title'});
435
- ```
436
-
437
- #### `setLastSavedConfig(config)`
438
-
439
- Set the last saved room configuration.
440
-
441
- ```tsx
442
- const setLastSavedConfig = useRoomStore(
443
- (state) => state.room.setLastSavedConfig,
444
- );
445
- const config = useRoomStore((state) => state.config);
446
- setLastSavedConfig(config);
447
- ```
448
-
449
- #### `hasUnsavedChanges()`
450
-
451
- Check if the room has unsaved changes.
452
-
453
- ```tsx
454
- const hasUnsavedChanges = useRoomStore((state) => state.room.hasUnsavedChanges);
455
- if (hasUnsavedChanges()) {
456
- // Prompt user to save changes
457
- }
458
- ```
459
-
460
- #### `setLayout(layout)`
461
-
462
- Set the room layout configuration.
463
-
464
- ```tsx
465
- const setLayout = useRoomStore((state) => state.room.setLayout);
466
- setLayout(newLayout);
467
- ```
468
-
469
- #### `togglePanel(panel, show)`
470
-
471
- Toggle the visibility of a panel.
472
-
473
- ```tsx
474
- const togglePanel = useRoomStore((state) => state.room.togglePanel);
475
- togglePanel('data-sources', true); // Show the data sources panel
476
- ```
477
-
478
- #### `togglePanelPin(panel)`
479
-
480
- Toggle the pin state of a panel.
481
-
482
- ```tsx
483
- const togglePanelPin = useRoomStore((state) => state.room.togglePanelPin);
484
- togglePanelPin('data-sources');
485
- ```
486
-
487
- #### `addOrUpdateSqlQueryDataSource(tableName, query, oldTableName)`
488
-
489
- Add or update a SQL query data source.
490
-
491
- ```tsx
492
- const addOrUpdateSqlQueryDataSource = useRoomStore(
493
- (state) => state.room.addOrUpdateSqlQueryDataSource,
494
- );
495
- await addOrUpdateSqlQueryDataSource(
496
- 'filtered_data',
497
- 'SELECT * FROM data WHERE value > 10',
498
- );
499
- ```
500
-
501
- #### `removeSqlQueryDataSource(tableName)`
502
-
503
- Remove a SQL query data source.
504
-
505
- ```tsx
506
- const removeSqlQueryDataSource = useRoomStore(
507
- (state) => state.room.removeSqlQueryDataSource,
508
- );
509
- await removeSqlQueryDataSource('filtered_data');
510
- ```
511
-
512
- #### `addRoomFile(info, desiredTableName)`
513
-
514
- Add a room file.
515
-
516
- ```tsx
517
- const addRoomFile = useRoomStore((state) => state.room.addRoomFile);
518
- const dataTable = await addRoomFile(file, 'my_data');
519
- ```
520
-
521
- #### `removeRoomFile(pathname)`
522
-
523
- Remove a room file.
524
-
525
- ```tsx
526
- const removeRoomFile = useRoomStore((state) => state.room.removeRoomFile);
527
- removeRoomFile('/path/to/file.csv');
528
- ```
529
-
530
- #### `maybeDownloadDataSources()`
531
-
532
- Download data sources if needed.
533
-
534
- ```tsx
535
- const maybeDownloadDataSources = useRoomStore(
536
- (state) => state.room.maybeDownloadDataSources,
537
- );
538
- await maybeDownloadDataSources();
539
- ```
540
-
541
- #### `setRoomFiles(info)`
542
-
543
- Set the room files.
544
-
545
- ```tsx
546
- const setRoomFiles = useRoomStore((state) => state.room.setRoomFiles);
547
- setRoomFiles(fileInfoArray);
548
- ```
549
-
550
- #### `setRoomFileProgress(pathname, fileState)`
551
-
552
- Set the progress of a room file.
553
-
554
- ```tsx
555
- const setRoomFileProgress = useRoomStore(
556
- (state) => state.room.setRoomFileProgress,
557
- );
558
- setRoomFileProgress('/path/to/file.csv', {status: 'processing'});
559
- ```
560
-
561
- #### `addTable(tableName, data)`
562
-
563
- Add a table to the room.
564
-
565
- ```tsx
566
- const addTable = useRoomStore((state) => state.db.addTable);
567
- await addTable('my_table', records);
568
- ```
569
-
570
- #### `addDataSource(dataSource, status)`
571
-
572
- Add a data source to the room.
573
-
574
- ```tsx
575
- const addDataSource = useRoomStore((state) => state.room.addDataSource);
576
- await addDataSource({
577
- type: 'url',
578
- url: 'https://example.com/data.csv',
579
- tableName: 'external_data',
580
- });
581
- ```
582
-
583
- #### `getTable(tableName)`
584
-
585
- Get a table by name.
586
-
587
- ```tsx
588
- const getTable = useRoomStore((state) => state.room.getTable);
589
- const table = getTable('my_table');
590
- ```
591
-
592
- #### `setTables(dataTable)`
593
-
594
- Set the room tables.
595
-
596
- ```tsx
597
- const setTables = useRoomStore((state) => state.room.setTables);
598
- await setTables(tableArray);
599
- ```
600
-
601
- #### `setTableRowCount(tableName, rowCount)`
602
-
603
- Set the row count for a table.
604
-
605
- ```tsx
606
- const setTableRowCount = useRoomStore((state) => state.room.setTableRowCount);
607
- setTableRowCount('my_table', 1000);
608
- ```
609
-
610
- #### `setRoomTitle(title)`
611
-
612
- Set the room title.
613
-
614
- ```tsx
615
- const setRoomTitle = useRoomStore((state) => state.room.setRoomTitle);
616
- setRoomTitle('My Awesome Room');
617
- ```
618
-
619
- #### `setDescription(description)`
620
-
621
- Set the room description.
622
-
623
- ```tsx
624
- const setDescription = useRoomStore((state) => state.room.setDescription);
625
- setDescription('This is a description of my room');
626
- ```
627
-
628
- #### `areDatasetsReady()`
629
-
630
- Check if all datasets are ready.
631
-
632
- ```tsx
633
- const areDatasetsReady = useRoomStore((state) => state.room.areDatasetsReady);
634
- if (areDatasetsReady()) {
635
- // Proceed with data operations
636
- }
637
- ```
638
-
639
- #### `findTableByName(tableName)`
139
+ ## Persistence
640
140
 
641
- Find a table by name.
141
+ Use `persistSliceConfigs` with schemas:
642
142
 
643
143
  ```tsx
644
- const findTableByName = useRoomStore((state) => state.room.findTableByName);
645
- const table = findTableByName('my_table');
646
- ```
647
-
648
- #### `updateReadyDataSources()`
649
-
650
- Update the status of all data sources based on the current tables.
651
-
652
- ```tsx
653
- const updateReadyDataSources = useRoomStore(
654
- (state) => state.room.updateReadyDataSources,
655
- );
656
- await updateReadyDataSources();
657
- ```
658
-
659
- #### `onDataUpdated()`
660
-
661
- Called when data has been updated.
662
-
663
- ```tsx
664
- const onDataUpdated = useRoomStore((state) => state.room.onDataUpdated);
665
- await onDataUpdated();
666
- ```
667
-
668
- #### `areViewsReadyToRender()`
669
-
670
- Check if views are ready to render.
671
-
672
- ```tsx
673
- const areViewsReadyToRender = useRoomStore(
674
- (state) => state.room.areViewsReadyToRender,
675
- );
676
- if (areViewsReadyToRender()) {
677
- // Render views
678
- }
679
- ```
680
-
681
- #### `refreshTableSchemas()`
144
+ import {
145
+ BaseRoomConfig,
146
+ LayoutConfig,
147
+ createRoomStore,
148
+ persistSliceConfigs,
149
+ } from '@sqlrooms/room-shell';
682
150
 
683
- Refresh table schemas from the database.
151
+ const persistence = {
152
+ name: 'my-room-storage',
153
+ sliceConfigSchemas: {
154
+ room: BaseRoomConfig,
155
+ layout: LayoutConfig,
156
+ },
157
+ };
684
158
 
685
- ```tsx
686
- const refreshTableSchemas = useRoomStore(
687
- (state) => state.room.refreshTableSchemas,
159
+ createRoomStore(
160
+ persistSliceConfigs(persistence, (set, get, store) => ({
161
+ // compose slices here
162
+ })),
688
163
  );
689
- const updatedTables = await refreshTableSchemas();
690
164
  ```
691
165
 
692
- ## Advanced Features
166
+ ## Related packages
693
167
 
694
- - **Custom State Slices**: Extend the room state with custom slices
695
- - **Task Management**: Built-in task progress tracking
696
- - **Panel Configuration**: Configure and arrange panels dynamically
697
- - **Data Source Integration**: Connect to various data sources
698
- - **File Processing**: Process and transform data files
699
-
700
- For more information, visit the SQLRooms documentation.
701
-
702
- ```
703
-
704
- ```
168
+ - `@sqlrooms/sql-editor`
169
+ - `@sqlrooms/ai`
170
+ - `@sqlrooms/mosaic`
171
+ - `@sqlrooms/vega`