0
# Layout Restoration
1
2
Persistent layout state management for saving and restoring widget arrangements, positions, and application state across sessions.
3
4
## Capabilities
5
6
### LayoutRestorer Class
7
8
Main implementation for layout restoration providing persistent state management across application sessions.
9
10
```typescript { .api }
11
/**
12
* Layout restoration implementation for persistent widget state management
13
*/
14
class LayoutRestorer implements ILayoutRestorer {
15
constructor(options: LayoutRestorer.IOptions);
16
17
/** Whether restoration is deferred until later */
18
readonly isDeferred: boolean;
19
20
/** Promise that resolves when restoration is complete */
21
readonly restored: Promise<void>;
22
23
/**
24
* Add a widget to be tracked for restoration
25
* @param widget - Widget to track
26
* @param name - Unique name for the widget
27
*/
28
add(widget: Widget, name: string): void;
29
30
/**
31
* Fetch the saved layout from storage
32
* @returns Promise resolving to saved layout or null
33
*/
34
fetch(): Promise<ILabShell.ILayout>;
35
36
/**
37
* Restore a widget tracker with saved state
38
* @param tracker - Widget tracker to restore
39
* @param options - Restoration options
40
* @returns Promise resolving when restoration is complete
41
*/
42
restore(
43
tracker: WidgetTracker,
44
options: IRestorer.IOptions<Widget>
45
): Promise<any>;
46
47
/**
48
* Restore deferred widgets
49
* @returns Promise resolving to main area layout or null
50
*/
51
restoreDeferred(): Promise<ILabShell.IMainArea | null>;
52
53
/**
54
* Save the current layout to storage
55
* @param layout - Layout to save
56
* @returns Promise resolving when save is complete
57
*/
58
save(layout: ILabShell.ILayout): Promise<void>;
59
}
60
```
61
62
**Usage Examples:**
63
64
```typescript
65
import { LayoutRestorer, ILayoutRestorer } from "@jupyterlab/application";
66
import { StateDB } from "@jupyterlab/statedb";
67
import { Widget } from "@lumino/widgets";
68
69
// Create layout restorer
70
const restorer = new LayoutRestorer({
71
connector: new StateDB(),
72
first: Promise.resolve(),
73
registry: commandRegistry
74
});
75
76
// Add widgets for tracking
77
const myWidget = new Widget();
78
myWidget.id = 'my-important-widget';
79
restorer.add(myWidget, 'my-important-widget');
80
81
// Wait for restoration to complete
82
await restorer.restored;
83
console.log('Layout restoration completed');
84
85
// Save current layout
86
const currentLayout = shell.saveLayout();
87
await restorer.save(currentLayout);
88
89
// Restore a widget tracker
90
await restorer.restore(widgetTracker, {
91
command: 'myapp:open-widget',
92
args: (widget) => ({ id: widget.id }),
93
name: (widget) => widget.id
94
});
95
```
96
97
### ILayoutRestorer Interface
98
99
Service interface for layout restoration functionality with dependency injection support.
100
101
```typescript { .api }
102
/**
103
* Layout restoration service interface
104
*/
105
interface ILayoutRestorer extends IRestorer {
106
/** Promise that resolves when restoration is complete */
107
readonly restored: Promise<void>;
108
109
/**
110
* Add a widget to be tracked for restoration
111
* @param widget - Widget to track
112
* @param name - Unique name for the widget
113
*/
114
add(widget: Widget, name: string): void;
115
116
/**
117
* Restore a widget tracker with saved state
118
* @param tracker - Widget tracker to restore
119
* @param options - Restoration options
120
* @returns Promise resolving when restoration is complete
121
*/
122
restore<T extends Widget>(
123
tracker: WidgetTracker<T>,
124
options: IRestorer.IOptions<T>
125
): Promise<any>;
126
}
127
128
/**
129
* Service token for layout restoration
130
*/
131
const ILayoutRestorer: Token<ILayoutRestorer>;
132
```
133
134
**Usage in Plugins:**
135
136
```typescript
137
import { ILayoutRestorer } from "@jupyterlab/application";
138
import { JupyterFrontEndPlugin } from "@jupyterlab/application";
139
import { WidgetTracker } from "@jupyterlab/apputils";
140
141
const plugin: JupyterFrontEndPlugin<void> = {
142
id: 'my-restoration-plugin',
143
autoStart: true,
144
requires: [ILayoutRestorer],
145
activate: (app, restorer: ILayoutRestorer) => {
146
const tracker = new WidgetTracker<MyWidget>({
147
namespace: 'my-widgets'
148
});
149
150
// Restore widgets from previous session
151
restorer.restore(tracker, {
152
command: 'myapp:create-widget',
153
args: (widget) => ({
154
id: widget.id,
155
config: widget.config
156
}),
157
name: (widget) => widget.id,
158
when: app.serviceManager.ready
159
});
160
161
// Add new widgets to restoration
162
tracker.widgetAdded.connect((sender, widget) => {
163
restorer.add(widget, widget.id);
164
});
165
}
166
};
167
```
168
169
### Restoration Options
170
171
Configuration options for controlling how widgets and trackers are restored.
172
173
```typescript { .api }
174
/**
175
* Options for restoring widget trackers (from @jupyterlab/apputils)
176
*/
177
interface IRestorer.IOptions<T extends Widget> {
178
/**
179
* Command to execute when restoring a widget
180
*/
181
command: string;
182
183
/**
184
* Function to extract command arguments from widget
185
* @param widget - Widget to extract args from
186
* @returns Command arguments
187
*/
188
args?: (widget: T) => ReadonlyPartialJSONObject;
189
190
/**
191
* Function to generate unique name for widget
192
* @param widget - Widget to name
193
* @returns Unique name string
194
*/
195
name: (widget: T) => string;
196
197
/**
198
* Promise to wait for before restoration
199
*/
200
when?: Promise<any>;
201
}
202
```
203
204
### Constructor Options
205
206
Configuration for creating LayoutRestorer instances.
207
208
```typescript { .api }
209
namespace LayoutRestorer {
210
/**
211
* Constructor options for LayoutRestorer
212
*/
213
interface IOptions {
214
/**
215
* State storage connector
216
*/
217
connector: IDataConnector<ReadonlyPartialJSONObject>;
218
219
/**
220
* Promise to resolve before restoration begins
221
*/
222
first: Promise<any>;
223
224
/**
225
* Command registry for executing restoration commands
226
*/
227
registry: CommandRegistry;
228
}
229
}
230
```
231
232
**Usage Examples:**
233
234
```typescript
235
import { LayoutRestorer } from "@jupyterlab/application";
236
import { StateDB } from "@jupyterlab/statedb";
237
import { CommandRegistry } from "@lumino/commands";
238
239
// Custom state connector
240
class CustomStateConnector implements IDataConnector<ReadonlyPartialJSONObject> {
241
async fetch(id: string): Promise<ReadonlyPartialJSONObject | undefined> {
242
// Custom fetch implementation
243
return JSON.parse(localStorage.getItem(id) || '{}');
244
}
245
246
async save(id: string, value: ReadonlyPartialJSONObject): Promise<void> {
247
// Custom save implementation
248
localStorage.setItem(id, JSON.stringify(value));
249
}
250
251
async remove(id: string): Promise<void> {
252
localStorage.removeItem(id);
253
}
254
255
async list(query?: string): Promise<any> {
256
// List implementation
257
return {};
258
}
259
}
260
261
// Create restorer with custom options
262
const commands = new CommandRegistry();
263
const restorer = new LayoutRestorer({
264
connector: new CustomStateConnector(),
265
first: Promise.resolve(), // Or wait for some initialization
266
registry: commands
267
});
268
269
// Check if restoration is deferred
270
if (restorer.isDeferred) {
271
console.log('Restoration will happen later');
272
273
// Manually trigger deferred restoration
274
const deferredLayout = await restorer.restoreDeferred();
275
if (deferredLayout) {
276
console.log('Deferred widgets restored');
277
}
278
}
279
```
280
281
### Integration with Shell
282
283
How layout restoration integrates with the shell system for complete state management.
284
285
```typescript { .api }
286
// Integration example showing how restoration works with shell
287
interface ShellRestorationWorkflow {
288
/**
289
* 1. Shell saves its layout periodically or on shutdown
290
*/
291
saveShellLayout(): Promise<void>;
292
293
/**
294
* 2. Layout restorer fetches saved layout on startup
295
*/
296
fetchSavedLayout(): Promise<ILabShell.ILayout | null>;
297
298
/**
299
* 3. Shell restores layout with layout restorer
300
*/
301
restoreShellLayout(layout: ILabShell.ILayout): Promise<void>;
302
303
/**
304
* 4. Individual widgets are restored via trackers
305
*/
306
restoreWidgetTrackers(): Promise<void>;
307
}
308
```
309
310
**Complete Restoration Example:**
311
312
```typescript
313
import {
314
LayoutRestorer,
315
ILayoutRestorer,
316
LabShell,
317
ILabShell
318
} from "@jupyterlab/application";
319
import { StateDB } from "@jupyterlab/statedb";
320
import { DockPanel } from "@lumino/widgets";
321
322
// Complete restoration workflow
323
async function setupLayoutRestoration(
324
shell: LabShell,
325
commands: CommandRegistry
326
) {
327
// 1. Create layout restorer
328
const restorer = new LayoutRestorer({
329
connector: new StateDB(),
330
first: Promise.resolve(),
331
registry: commands
332
});
333
334
// 2. Wait for restoration to complete
335
await restorer.restored;
336
337
// 3. Restore shell layout from saved state
338
const savedLayout = await restorer.fetch();
339
if (savedLayout) {
340
await shell.restoreLayout(
341
DockPanel.Mode.Multiple,
342
restorer,
343
savedLayout
344
);
345
}
346
347
// 4. Set up periodic saving
348
let saveTimer: any;
349
const scheduleSave = () => {
350
clearTimeout(saveTimer);
351
saveTimer = setTimeout(async () => {
352
const currentLayout = shell.saveLayout();
353
await restorer.save(currentLayout);
354
}, 1000); // Save after 1 second of inactivity
355
};
356
357
// Save when layout changes
358
shell.layoutModified.connect(scheduleSave);
359
360
// Save when widgets are added/removed
361
shell.currentChanged.connect(scheduleSave);
362
363
return restorer;
364
}
365
```
366
367
## Error Handling
368
369
Common error scenarios and how to handle them in layout restoration.
370
371
```typescript
372
// Error handling patterns
373
try {
374
await restorer.restore(tracker, options);
375
} catch (error) {
376
if (error instanceof Error) {
377
console.warn('Failed to restore widgets:', error.message);
378
// Fallback to default layout or create new widgets
379
}
380
}
381
382
// Graceful degradation
383
const layout = await restorer.fetch().catch(() => null);
384
if (!layout) {
385
console.log('No saved layout found, using defaults');
386
// Set up default layout
387
}
388
```