@jupyterlab/settingeditor 4.0.0-alpha.9 → 4.0.0-beta.1

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.
@@ -0,0 +1,218 @@
1
+ /* -----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |----------------------------------------------------------------------------*/
5
+
6
+ import { ISettingRegistry, Settings } from '@jupyterlab/settingregistry';
7
+ import { ITranslator } from '@jupyterlab/translation';
8
+ import { IFormRendererRegistry } from '@jupyterlab/ui-components';
9
+ import { ISignal } from '@lumino/signaling';
10
+ import type { Field } from '@rjsf/utils';
11
+ import React, { useEffect, useState } from 'react';
12
+ import { PluginList } from './pluginlist';
13
+ import { SettingsFormEditor } from './SettingsFormEditor';
14
+
15
+ export interface ISettingsPanelProps {
16
+ /**
17
+ * List of Settings objects that provide schema and values
18
+ * of plugins.
19
+ */
20
+ settings: Settings[];
21
+
22
+ /**
23
+ * Form component registry that provides renderers
24
+ * for the form editor.
25
+ */
26
+ editorRegistry: IFormRendererRegistry;
27
+
28
+ /**
29
+ * Handler for when selection change is triggered by scrolling
30
+ * in the SettingsPanel.
31
+ */
32
+ onSelect: (id: string) => void;
33
+
34
+ /**
35
+ * Signal that fires when a selection is made in the plugin list.
36
+ */
37
+ handleSelectSignal: ISignal<PluginList, string>;
38
+
39
+ /**
40
+ * Translator object
41
+ */
42
+ translator: ITranslator;
43
+
44
+ /**
45
+ * Callback to update the plugin list to display plugins with
46
+ * invalid / unsaved settings in red.
47
+ */
48
+ hasError: (id: string, error: boolean) => void;
49
+
50
+ /**
51
+ * Sends the updated dirty state to the parent class.
52
+ */
53
+ updateDirtyState: (dirty: boolean) => void;
54
+
55
+ /**
56
+ * Signal that sends updated filter when search value changes.
57
+ */
58
+ updateFilterSignal: ISignal<
59
+ PluginList,
60
+ (plugin: ISettingRegistry.IPlugin) => string[] | null
61
+ >;
62
+
63
+ /**
64
+ * If the settings editor is created with an initial search query, an initial
65
+ * filter function is passed to the settings panel.
66
+ */
67
+ initialFilter: (item: ISettingRegistry.IPlugin) => string[] | null;
68
+ }
69
+
70
+ /**
71
+ * React component that displays a list of SettingsFormEditor
72
+ * components.
73
+ */
74
+ export const SettingsPanel: React.FC<ISettingsPanelProps> = ({
75
+ settings,
76
+ editorRegistry,
77
+ onSelect,
78
+ handleSelectSignal,
79
+ hasError,
80
+ updateDirtyState,
81
+ updateFilterSignal,
82
+ translator,
83
+ initialFilter
84
+ }: ISettingsPanelProps): JSX.Element => {
85
+ const [expandedPlugin, setExpandedPlugin] = useState<string | null>(null);
86
+ const [filterPlugin, setFilter] = useState<
87
+ (plugin: ISettingRegistry.IPlugin) => string[] | null
88
+ >(() => initialFilter);
89
+
90
+ // Refs used to keep track of "selected" plugin based on scroll location
91
+ const editorRefs: {
92
+ [pluginId: string]: React.RefObject<HTMLDivElement>;
93
+ } = {};
94
+ for (const setting of settings) {
95
+ editorRefs[setting.id] = React.useRef(null);
96
+ }
97
+ const wrapperRef: React.RefObject<HTMLDivElement> = React.useRef(null);
98
+ const editorDirtyStates: React.RefObject<{
99
+ [id: string]: boolean;
100
+ }> = React.useRef({});
101
+
102
+ useEffect(() => {
103
+ const onFilterUpdate = (
104
+ list: PluginList,
105
+ newFilter: (plugin: ISettingRegistry.IPlugin) => string[] | null
106
+ ) => {
107
+ setFilter(() => newFilter);
108
+ for (const pluginSettings of settings) {
109
+ const filtered = newFilter(pluginSettings.plugin);
110
+ if (filtered === null || filtered.length > 0) {
111
+ setExpandedPlugin(pluginSettings.id);
112
+ break;
113
+ }
114
+ }
115
+ };
116
+
117
+ // Set first visible plugin as expanded plugin on initial load.
118
+ for (const pluginSettings of settings) {
119
+ const filtered = filterPlugin(pluginSettings.plugin);
120
+ if (filtered === null || filtered.length > 0) {
121
+ setExpandedPlugin(pluginSettings.id);
122
+ break;
123
+ }
124
+ }
125
+
126
+ // When filter updates, only show plugins that match search.
127
+ updateFilterSignal.connect(onFilterUpdate);
128
+
129
+ const onSelectChange = (list: PluginList, pluginId: string) => {
130
+ setExpandedPlugin(expandedPlugin !== pluginId ? pluginId : null);
131
+ // Scroll to the plugin when a selection is made in the left panel.
132
+ editorRefs[pluginId]?.current?.scrollIntoView(true);
133
+ };
134
+ handleSelectSignal?.connect?.(onSelectChange);
135
+
136
+ return () => {
137
+ updateFilterSignal.disconnect(onFilterUpdate);
138
+ handleSelectSignal?.disconnect?.(onSelectChange);
139
+ };
140
+ }, []);
141
+
142
+ const updateDirtyStates = React.useCallback(
143
+ (id: string, dirty: boolean) => {
144
+ if (editorDirtyStates.current) {
145
+ editorDirtyStates.current[id] = dirty;
146
+ for (const editor in editorDirtyStates.current) {
147
+ if (editorDirtyStates.current[editor]) {
148
+ updateDirtyState(true);
149
+ return;
150
+ }
151
+ }
152
+ }
153
+ updateDirtyState(false);
154
+ },
155
+ [editorDirtyStates, updateDirtyState]
156
+ );
157
+
158
+ const renderers = React.useMemo(
159
+ () =>
160
+ Object.entries(editorRegistry.renderers).reduce<{
161
+ [plugin: string]: { [property: string]: Field };
162
+ }>((agg, [id, renderer]) => {
163
+ const splitPosition = id.lastIndexOf('.');
164
+ const pluginId = id.substring(0, splitPosition);
165
+ const propertyName = id.substring(splitPosition + 1);
166
+ if (!agg[pluginId]) {
167
+ agg[pluginId] = {};
168
+ }
169
+ if (!agg[pluginId][propertyName] && renderer.fieldRenderer) {
170
+ agg[pluginId][propertyName] = renderer.fieldRenderer;
171
+ }
172
+ return agg;
173
+ }, {}),
174
+ [editorRegistry]
175
+ );
176
+
177
+ return (
178
+ <div className="jp-SettingsPanel" ref={wrapperRef}>
179
+ {settings.map(pluginSettings => {
180
+ // Pass filtered results to SettingsFormEditor to only display filtered fields.
181
+ const filtered = filterPlugin(pluginSettings.plugin);
182
+ // If filtered results are an array, only show if the array is non-empty.
183
+ if (filtered !== null && filtered.length === 0) {
184
+ return undefined;
185
+ }
186
+ return (
187
+ <div
188
+ ref={editorRefs[pluginSettings.id]}
189
+ className="jp-SettingsForm"
190
+ key={`${pluginSettings.id}SettingsEditor`}
191
+ >
192
+ <SettingsFormEditor
193
+ isCollapsed={pluginSettings.id !== expandedPlugin}
194
+ onCollapseChange={(willCollapse: boolean) => {
195
+ if (!willCollapse) {
196
+ setExpandedPlugin(pluginSettings.id);
197
+ } else if (pluginSettings.id === expandedPlugin) {
198
+ setExpandedPlugin(null);
199
+ }
200
+ }}
201
+ filteredValues={filtered}
202
+ settings={pluginSettings}
203
+ renderers={renderers}
204
+ hasError={(error: boolean) => {
205
+ hasError(pluginSettings.id, error);
206
+ }}
207
+ updateDirtyState={(dirty: boolean) => {
208
+ updateDirtyStates(pluginSettings.id, dirty);
209
+ }}
210
+ onSelect={onSelect}
211
+ translator={translator}
212
+ />
213
+ </div>
214
+ );
215
+ })}
216
+ </div>
217
+ );
218
+ };
package/src/tokens.ts ADDED
@@ -0,0 +1,33 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ import { IWidgetTracker, MainAreaWidget } from '@jupyterlab/apputils';
5
+ import { Token } from '@lumino/coreutils';
6
+ import { JsonSettingEditor as JSONSettingEditor } from './jsonsettingeditor';
7
+ import { SettingsEditor } from './settingseditor';
8
+
9
+ /**
10
+ * The setting editor tracker token.
11
+ */
12
+ export const ISettingEditorTracker = new Token<ISettingEditorTracker>(
13
+ '@jupyterlab/settingeditor:ISettingEditorTracker'
14
+ );
15
+
16
+ /**
17
+ * The setting editor tracker token.
18
+ */
19
+ export const IJSONSettingEditorTracker = new Token<IJSONSettingEditorTracker>(
20
+ '@jupyterlab/settingeditor:IJSONSettingEditorTracker'
21
+ );
22
+
23
+ /**
24
+ * A class that tracks the setting editor.
25
+ */
26
+ export interface IJSONSettingEditorTracker
27
+ extends IWidgetTracker<MainAreaWidget<JSONSettingEditor>> {}
28
+
29
+ /**
30
+ * A class that tracks the setting editor.
31
+ */
32
+ export interface ISettingEditorTracker
33
+ extends IWidgetTracker<MainAreaWidget<SettingsEditor>> {}
package/style/base.css CHANGED
@@ -18,9 +18,9 @@
18
18
  min-width: 360px;
19
19
  min-height: 240px;
20
20
  background-color: var(--jp-layout-color0);
21
+ color: var(--jp-ui-font-color0);
21
22
  margin-top: -1px;
22
23
  outline: none;
23
- color: var(--jp-content-font-color1) !important;
24
24
 
25
25
  /* This is needed so that all font sizing of children done in ems is
26
26
  * relative to this base size */
@@ -90,19 +90,10 @@
90
90
  right: 0;
91
91
  }
92
92
 
93
- .jp-PluginList .jp-SettingsHeader {
94
- display: flex;
95
- flex-basis: 100%;
96
- }
97
-
98
- .jp-PluginList .jp-SettingsHeader button {
99
- color: var(--jp-private-notebook-selected-color);
100
- white-space: nowrap;
101
- }
102
-
103
93
  .jp-PluginList .jp-PluginList-header {
104
94
  border-bottom: var(--jp-border-width) solid var(--jp-border-color2);
105
95
  border-top: var(--jp-border-width) solid var(--jp-border-color2);
96
+ color: var(--jp-ui-font-color1);
106
97
  }
107
98
 
108
99
  .jp-PluginList .jp-PluginList-noResults,
@@ -116,7 +107,7 @@
116
107
  margin: 10px;
117
108
  border-bottom: var(--jp-border-width) solid var(--jp-border-color2);
118
109
  border-top: var(--jp-border-width) solid var(--jp-border-color2);
119
- color: var(--jp-content-font-color1);
110
+ color: var(--jp-ui-font-color1);
120
111
  }
121
112
 
122
113
  .jp-PluginList .jp-SelectedIndicator {
@@ -134,24 +125,6 @@
134
125
  background-color: var(--jp-error-color0);
135
126
  }
136
127
 
137
- .jp-PluginList button.jp-mod-selected.jp-ErrorPlugin span {
138
- color: var(--jp-error-color0);
139
- }
140
-
141
- .jp-PluginList button.jp-mod-selected span {
142
- font-weight: var(--jp-content-heading-font-weight);
143
- color: var(--jp-brand-color1);
144
- }
145
-
146
- .jp-PluginList button span {
147
- color: var(--jp-content-font-color1);
148
- line-height: var(--jp-cell-collapser-min-height);
149
- }
150
-
151
- .jp-FormComponent li span {
152
- overflow: hidden;
153
- }
154
-
155
128
  .jp-SettingEditor-header {
156
129
  font-size: var(--jp-content-font-size4);
157
130
  font-weight: var(--jp-content-heading-font-weight);
@@ -164,14 +137,6 @@
164
137
  background-color: var(--jp-layout-color0);
165
138
  }
166
139
 
167
- .jp-PluginList-Searcher {
168
- margin: 5px;
169
- }
170
-
171
- ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
172
- background-image: var(--jp-icon-file-selected);
173
- }
174
-
175
140
  .jp-PluginList-icon {
176
141
  display: flex;
177
142
  height: 20px;
@@ -196,19 +161,6 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
196
161
  align-items: center;
197
162
  }
198
163
 
199
- .jp-ToolbarButtonComponent-label
200
- .jp-SettingsRawEditor.jp-mod-error
201
- .jp-Toolbar-item.jp-BugIcon::after {
202
- color: red;
203
- content: '\25CF'; /* Unicode circle character (error dot) */
204
- font-size: 7px;
205
- width: 100%;
206
- height: 100%;
207
- position: absolute;
208
- top: 4px;
209
- left: 6px;
210
- }
211
-
212
164
  .jp-SettingsRawEditor .jp-Inspector {
213
165
  border-top: 2px solid var(--jp-layout-color2);
214
166
  min-height: var(--jp-private-settingeditor-debug-height);
@@ -224,35 +176,8 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
224
176
  text-align: right;
225
177
  }
226
178
 
227
- .jp-SettingsPanel fieldset input,
228
- .jp-SettingsPanel fieldset select,
229
- .jp-SettingsPanel fieldset textarea {
230
- font-size: var(--jp-content-font-size2);
231
- border-color: var(--jp-input-border-color);
232
- border-style: solid;
233
- border-radius: 5px;
234
- border-width: 1px;
235
- padding: 6px 8px;
236
- background: none;
237
- color: var(--jp-content-font-color0);
238
- height: inherit;
239
- }
240
-
241
- .jp-SettingsPanel fieldset input[type='checkbox'] {
242
- position: relative;
243
- top: 2px;
244
- margin-left: 0;
245
- }
246
-
247
- /** copy of `input.jp-mod-styled:focus` style */
248
- .jp-SettingsPanel fieldset input:focus {
249
- border: var(--jp-border-width) solid var(--md-blue-500);
250
- box-shadow: inset 0 0 4px var(--md-blue-300);
251
- }
252
-
253
- .jp-SettingsPanel .checkbox label {
254
- cursor: pointer;
255
- font-size: var(--jp-content-font-size2);
179
+ .jp-SettingsRawEditor .cm-editor {
180
+ height: 100%;
256
181
  }
257
182
 
258
183
  .jp-SettingsPanel .checkbox p {
@@ -264,61 +189,12 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
264
189
  flex-direction: column-reverse;
265
190
  }
266
191
 
267
- .jp-SettingsPanel .checkbox .field-description {
268
- /* Disable default description field for checkbox:
269
- because other widgets do not have description fields,
270
- we add descriptions to each widget on the field level.
271
- */
272
- display: none;
273
- }
274
-
275
- .jp-SettingsPanel button[type='submit'] {
276
- display: none;
277
- }
278
-
279
192
  .jp-SettingsPanel .form-group {
280
193
  display: flex;
281
194
  padding: 4px 8px 4px var(--jp-private-settingeditor-modifier-indent);
282
195
  margin-top: 5px;
283
196
  }
284
197
 
285
- .jp-SettingsPanel .jp-objectFieldWrapper .form-group {
286
- padding: 2px 8px 2px var(--jp-private-settingeditor-modifier-indent);
287
- margin-top: 2px;
288
- }
289
-
290
- .jp-ArrayOperations {
291
- margin-left: 8px;
292
- }
293
-
294
- .jp-SettingsPanel .jp-FormGroup-content {
295
- display: flex;
296
- align-items: center;
297
- flex-wrap: wrap;
298
- }
299
-
300
- .jp-SettingsPanel .jp-FormGroup-contentItem {
301
- margin-left: 7px;
302
- }
303
-
304
- .jp-SettingsPanel .jp-FormGroup-description {
305
- flex-basis: 100%;
306
- padding: 4px 7px;
307
- }
308
-
309
- .jp-SettingsPanel #root__description {
310
- display: none;
311
- }
312
-
313
- .jp-SettingsPanel fieldset {
314
- border: none;
315
- padding: 0;
316
- }
317
-
318
- .jp-SettingsPanel fieldset:not(:first-child) {
319
- margin-left: 7px;
320
- }
321
-
322
198
  .jp-SettingsPanel .jp-SaveSettingsBanner {
323
199
  position: absolute;
324
200
  bottom: 0;
@@ -340,71 +216,6 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
340
216
  color: var(--jp-brand-color0);
341
217
  }
342
218
 
343
- .jp-SettingsPanel .form-group.small-field:hover {
344
- background: var(--jp-border-color3);
345
- }
346
-
347
- .jp-SettingsPanel button.jp-mod-styled {
348
- cursor: pointer;
349
- }
350
-
351
- .jp-SettingsPanel button.jp-mod-styled:disabled {
352
- cursor: not-allowed;
353
- opacity: 0.5;
354
- }
355
-
356
- .jp-SettingsPanel .array-item button {
357
- margin: 2px;
358
- }
359
-
360
- .jp-openJSONSettingsEditor {
361
- position: absolute;
362
- bottom: 0;
363
- left: 0;
364
- }
365
-
366
- .jp-openJSONSettingsEditor button {
367
- border: 1px solid var(--jp-border-color1);
368
- color: var(--jp-ui-font-color0);
369
- padding: 5px;
370
- margin: 5px;
371
- cursor: pointer;
372
- background-color: var(--jp-border-color2);
373
- display: flex;
374
- align-items: center;
375
- }
376
-
377
- .jp-openJSONSettingsEditor button > div {
378
- display: flex;
379
- }
380
-
381
- .jp-openJSONSettingsEditor svg#icon {
382
- height: 1.5em;
383
- }
384
-
385
- .jp-SettingsPanel .array-item {
386
- border: 1px solid var(--jp-border-color2);
387
- border-radius: 4px;
388
- margin: 4px;
389
- }
390
-
391
- .jp-SettingsPanel .field-array-of-string .array-item {
392
- /* Display `jp-ArrayOperations` buttons side-by-side with content except
393
- for small screens where flex-wrap will place them one below the other.
394
- */
395
- display: flex;
396
- align-items: center;
397
- flex-wrap: wrap;
398
- }
399
-
400
- .jp-SettingsPanel .jp-root > fieldset > legend {
401
- display: none;
402
- }
403
-
404
- .jp-SettingsPanel .jp-root > fieldset > p {
405
- display: none;
406
- }
407
-
408
219
  .jp-SettingsPanel .jp-SettingsHeader h2 {
409
220
  font-size: var(--jp-content-font-size3);
410
221
  color: var(--jp-ui-font-color0);
@@ -420,20 +231,6 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
420
231
  line-height: var(--jp-content-font-size3);
421
232
  }
422
233
 
423
- .jp-SettingsPanel legend {
424
- font-size: var(--jp-content-font-size2);
425
- color: var(--jp-ui-font-color0);
426
- flex-basis: 100%;
427
- padding: 4px 0;
428
- font-weight: var(--jp-content-header-font-weight);
429
- border-bottom: 1px solid var(--jp-border-color2);
430
- }
431
-
432
- .jp-SettingsPanel .field-description {
433
- padding: 4px 0;
434
- white-space: pre-wrap;
435
- }
436
-
437
234
  .jp-SettingsPanel .jp-SettingsTitle {
438
235
  display: flex;
439
236
  align-items: center;
@@ -477,6 +274,10 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
477
274
  white-space: nowrap;
478
275
  }
479
276
 
277
+ .jp-PluginList-entry:hover {
278
+ background: var(--jp-layout-color1);
279
+ }
280
+
480
281
  .jp-PluginList-entry li {
481
282
  margin-left: 27px;
482
283
  margin-top: 5px;
@@ -485,14 +286,16 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
485
286
  text-overflow: ellipsis;
486
287
  }
487
288
 
488
- .jp-pluginList-entry-label {
289
+ .jp-PluginList-entry-label {
489
290
  display: flex;
490
291
  }
491
292
 
492
- .jp-pluginList-entry-label span {
293
+ .jp-PluginList-entry-label-text {
493
294
  text-overflow: ellipsis;
494
295
  overflow-x: hidden;
495
296
  white-space: nowrap;
297
+ color: var(--jp-ui-font-color1);
298
+ line-height: var(--jp-cell-collapser-min-height);
496
299
  }
497
300
 
498
301
  .jp-SettingsPanel .jp-SettingsHeader-Name {
@@ -500,32 +303,6 @@ ul.jp-PluginList li.jp-mod-selected span.jp-PluginList-icon.jp-FileIcon {
500
303
  font-size: var(--jp-content-font-size3);
501
304
  }
502
305
 
503
- .jp-SettingsPanel .jp-modifiedIndicator {
504
- width: 5px;
505
- background-color: var(--jp-brand-color2);
506
- margin-top: 0;
507
- margin-left: calc(var(--jp-private-settingeditor-modifier-indent) * -1);
508
- flex-shrink: 0;
509
- }
510
-
511
- .jp-SettingsPanel .jp-FormGroup-fieldLabel {
512
- font-size: var(--jp-content-font-size1);
513
- font-weight: normal;
514
- min-width: 120px;
515
- }
516
-
517
- .jp-SettingsPanel .jp-modifiedIndicator.jp-errorIndicator {
518
- background-color: var(--jp-error-color0);
519
- }
520
-
521
- .jp-SettingsPanel .validationErrors {
522
- color: var(--jp-error-color0);
523
- }
524
-
525
- .jp-SettingsPanel .panel.errors {
526
- display: none;
527
- }
528
-
529
306
  .jp-SettingsPanel .jp-SettingsEditor {
530
307
  padding: 20px;
531
308
  }
@@ -1,13 +0,0 @@
1
- import { ISignal } from '@lumino/signaling';
2
- import { SplitPanel as SPanel } from '@lumino/widgets';
3
- /**
4
- * A deprecated split panel that will be removed when the phosphor split panel
5
- * supports a handle moved signal. See https://github.com/phosphorjs/phosphor/issues/297.
6
- */
7
- export declare class SplitPanel extends SPanel {
8
- /**
9
- * Emits when the split handle has moved.
10
- */
11
- readonly handleMoved: ISignal<any, void>;
12
- handleEvent(event: Event): void;
13
- }
package/lib/splitpanel.js DELETED
@@ -1,26 +0,0 @@
1
- /* -----------------------------------------------------------------------------
2
- | Copyright (c) Jupyter Development Team.
3
- | Distributed under the terms of the Modified BSD License.
4
- |----------------------------------------------------------------------------*/
5
- import { Signal } from '@lumino/signaling';
6
- import { SplitPanel as SPanel } from '@lumino/widgets';
7
- /**
8
- * A deprecated split panel that will be removed when the phosphor split panel
9
- * supports a handle moved signal. See https://github.com/phosphorjs/phosphor/issues/297.
10
- */
11
- export class SplitPanel extends SPanel {
12
- constructor() {
13
- super(...arguments);
14
- /**
15
- * Emits when the split handle has moved.
16
- */
17
- this.handleMoved = new Signal(this);
18
- }
19
- handleEvent(event) {
20
- super.handleEvent(event);
21
- if (event.type === 'mouseup') {
22
- this.handleMoved.emit(undefined);
23
- }
24
- }
25
- }
26
- //# sourceMappingURL=splitpanel.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"splitpanel.js","sourceRoot":"","sources":["../src/splitpanel.ts"],"names":[],"mappings":"AAAA;;;+EAG+E;AAE/E,OAAO,EAAW,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;GAGG;AACH,MAAM,OAAO,UAAW,SAAQ,MAAM;IAAtC;;QACE;;WAEG;QACM,gBAAW,GAAuB,IAAI,MAAM,CAAY,IAAI,CAAC,CAAC;IASzE,CAAC;IAPC,WAAW,CAAC,KAAY;QACtB,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAEzB,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;YAC3B,IAAI,CAAC,WAAiC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SACzD;IACH,CAAC;CACF"}