dockview-core 1.13.1 → 1.14.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.
Files changed (48) hide show
  1. package/README.md +1 -21
  2. package/dist/cjs/api/dockviewGroupPanelApi.js +4 -5
  3. package/dist/cjs/api/dockviewPanelApi.js +4 -6
  4. package/dist/cjs/dockview/dockviewComponent.js +13 -76
  5. package/dist/cjs/dockview/dockviewPanel.js +1 -10
  6. package/dist/cjs/dockview/options.d.ts +1 -1
  7. package/dist/cjs/dockview/types.d.ts +5 -10
  8. package/dist/cjs/dockview/validate.d.ts +0 -0
  9. package/dist/cjs/dockview/validate.js +76 -0
  10. package/dist/cjs/events.js +3 -3
  11. package/dist/cjs/index.d.ts +4 -3
  12. package/dist/cjs/index.js +5 -3
  13. package/dist/dockview-core.amd.js +38 -53
  14. package/dist/dockview-core.amd.js.map +1 -1
  15. package/dist/dockview-core.amd.min.js +2 -2
  16. package/dist/dockview-core.amd.min.js.map +1 -1
  17. package/dist/dockview-core.amd.min.noStyle.js +2 -2
  18. package/dist/dockview-core.amd.min.noStyle.js.map +1 -1
  19. package/dist/dockview-core.amd.noStyle.js +37 -52
  20. package/dist/dockview-core.amd.noStyle.js.map +1 -1
  21. package/dist/dockview-core.cjs.js +38 -53
  22. package/dist/dockview-core.cjs.js.map +1 -1
  23. package/dist/dockview-core.esm.js +26 -41
  24. package/dist/dockview-core.esm.js.map +1 -1
  25. package/dist/dockview-core.esm.min.js +2 -2
  26. package/dist/dockview-core.esm.min.js.map +1 -1
  27. package/dist/dockview-core.js +38 -53
  28. package/dist/dockview-core.js.map +1 -1
  29. package/dist/dockview-core.min.js +2 -2
  30. package/dist/dockview-core.min.js.map +1 -1
  31. package/dist/dockview-core.min.noStyle.js +2 -2
  32. package/dist/dockview-core.min.noStyle.js.map +1 -1
  33. package/dist/dockview-core.noStyle.js +37 -52
  34. package/dist/dockview-core.noStyle.js.map +1 -1
  35. package/dist/esm/api/dockviewGroupPanelApi.js +3 -4
  36. package/dist/esm/api/dockviewPanelApi.js +4 -6
  37. package/dist/esm/dockview/components/titlebar/tabsContainer.js +1 -1
  38. package/dist/esm/dockview/dockviewComponent.js +13 -76
  39. package/dist/esm/dockview/dockviewPanel.js +1 -10
  40. package/dist/esm/dockview/options.d.ts +1 -1
  41. package/dist/esm/dockview/types.d.ts +5 -10
  42. package/dist/esm/dockview/validate.d.ts +0 -0
  43. package/dist/esm/dockview/validate.js +76 -0
  44. package/dist/esm/events.js +3 -3
  45. package/dist/esm/index.d.ts +4 -3
  46. package/dist/esm/index.js +4 -3
  47. package/dist/styles/dockview.css +4 -4
  48. package/package.json +1 -1
@@ -2,8 +2,7 @@ import { positionToDirection } from '../dnd/droptarget';
2
2
  import { Emitter } from '../events';
3
3
  import { MutableDisposable } from '../lifecycle';
4
4
  import { GridviewPanelApiImpl } from './gridviewPanelApi';
5
- // TODO find a better way to initialize and avoid needing null checks
6
- const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized';
5
+ const NOT_INITIALIZED_MESSAGE = 'dockview: DockviewGroupPanelApiImpl not initialized';
7
6
  export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
8
7
  get location() {
9
8
  if (!this._group) {
@@ -76,14 +75,14 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
76
75
  }
77
76
  }
78
77
  initialize(group) {
79
- this._group = group;
80
78
  /**
81
- * TODO: Annoying initialization order caveat
79
+ * TODO: Annoying initialization order caveat, find a better way to initialize and avoid needing null checks
82
80
  *
83
81
  * Due to the order on initialization we know that the model isn't defined until later in the same stack-frame of setup.
84
82
  * By queuing a microtask we can ensure the setup is completed within the same stack-frame, but after everything else has
85
83
  * finished ensuring the `model` is defined.
86
84
  */
85
+ this._group = group;
87
86
  queueMicrotask(() => {
88
87
  this._mutableDisposable.value =
89
88
  this._group.model.onDidActivePanelChange((event) => {
@@ -88,12 +88,10 @@ export class DockviewPanelApiImpl extends GridviewPanelApiImpl {
88
88
  var _a;
89
89
  let _trackGroupActive = (_a = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _a !== void 0 ? _a : false; // prevent duplicate events with same state
90
90
  this.groupEventsDisposable.value = new CompositeDisposable(this.group.api.onDidVisibilityChange((event) => {
91
- if (!event.isVisible && this.isVisible) {
92
- this._onDidVisibilityChange.fire(event);
93
- }
94
- else if (event.isVisible &&
95
- !this.isVisible &&
96
- this.group.model.isPanelActive(this.panel)) {
91
+ const hasBecomeHidden = !event.isVisible && this.isVisible;
92
+ const hasBecomeVisible = event.isVisible && !this.isVisible;
93
+ const isActivePanel = this.group.model.isPanelActive(this.panel);
94
+ if (hasBecomeHidden || (hasBecomeVisible && isActivePanel)) {
97
95
  this._onDidVisibilityChange.fire(event);
98
96
  }
99
97
  }), this.group.api.onDidLocationChange((event) => {
@@ -3,7 +3,7 @@ import { addDisposableListener, Emitter } from '../../../events';
3
3
  import { Tab } from '../tab/tab';
4
4
  import { VoidContainer } from './voidContainer';
5
5
  import { toggleClass } from '../../../dom';
6
- import { WillShowOverlayLocationEvent, } from '../../dockviewGroupPanelModel';
6
+ import { WillShowOverlayLocationEvent } from '../../dockviewGroupPanelModel';
7
7
  import { getPanelData } from '../../../dnd/dataTransfer';
8
8
  export class TabsContainer extends CompositeDisposable {
9
9
  get panels() {
@@ -58,66 +58,6 @@ function getDockviewTheme(element) {
58
58
  }
59
59
  return theme;
60
60
  }
61
- function typeValidate3(data, path) {
62
- if (typeof data.id !== 'string') {
63
- throw new Error(`${path}.id must be a string`);
64
- }
65
- if (typeof data.activeView !== 'string' ||
66
- typeof data.activeView !== 'undefined') {
67
- throw new Error(`${path}.activeView must be a string of undefined`);
68
- }
69
- }
70
- function typeValidate2(data, path) {
71
- if (typeof data.size !== 'number' && typeof data.size !== 'undefined') {
72
- throw new Error(`${path}.size must be a number or undefined`);
73
- }
74
- if (typeof data.visible !== 'boolean' &&
75
- typeof data.visible !== 'undefined') {
76
- throw new Error(`${path}.visible must be a boolean or undefined`);
77
- }
78
- if (data.type === 'leaf') {
79
- if (typeof data.data !== 'object' ||
80
- data.data === null ||
81
- Array.isArray(data.data)) {
82
- throw new Error('object must be a non-null object');
83
- }
84
- typeValidate3(data.data, `${path}.data`);
85
- }
86
- else if (data.type === 'branch') {
87
- if (!Array.isArray(data.data)) {
88
- throw new Error(`${path}.data must be an array`);
89
- }
90
- }
91
- else {
92
- throw new Error(`${path}.type must be onew of {'branch', 'leaf'}`);
93
- }
94
- }
95
- function typeValidate(data) {
96
- if (typeof data !== 'object' || data === null) {
97
- throw new Error('object must be a non-null object');
98
- }
99
- const { grid, panels, activeGroup, floatingGroups } = data;
100
- if (typeof grid !== 'object' || grid === null) {
101
- throw new Error("'.grid' must be a non-null object");
102
- }
103
- if (typeof grid.height !== 'number') {
104
- throw new Error("'.grid.height' must be a number");
105
- }
106
- if (typeof grid.width !== 'number') {
107
- throw new Error("'.grid.width' must be a number");
108
- }
109
- if (typeof grid.root !== 'object' || grid.root === null) {
110
- throw new Error("'.grid.root' must be a non-null object");
111
- }
112
- if (grid.root.type !== 'branch') {
113
- throw new Error(".grid.root.type must be of type 'branch'");
114
- }
115
- if (grid.orientation !== Orientation.HORIZONTAL &&
116
- grid.orientation !== Orientation.VERTICAL) {
117
- throw new Error(`'.grid.width' must be one of {${Orientation.HORIZONTAL}, ${Orientation.VERTICAL}}`);
118
- }
119
- typeValidate2(grid.root, '.grid.root');
120
- }
121
61
  export class DockviewComponent extends BaseGrid {
122
62
  get orientation() {
123
63
  return this.gridview.orientation;
@@ -328,7 +268,7 @@ export class DockviewComponent extends BaseGrid {
328
268
  return element.getBoundingClientRect();
329
269
  }
330
270
  const box = getBox();
331
- const groupId = (_b = (_a = options === null || options === void 0 ? void 0 : options.overridePopoutGroup) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : this.getNextGroupId(); //item.id;
271
+ const groupId = (_b = (_a = options === null || options === void 0 ? void 0 : options.overridePopoutGroup) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : this.getNextGroupId();
332
272
  if (itemToPopout.api.location.type === 'grid') {
333
273
  itemToPopout.api.setVisible(false);
334
274
  }
@@ -444,24 +384,22 @@ export class DockviewComponent extends BaseGrid {
444
384
  });
445
385
  }
446
386
  }
447
- else {
448
- if (this.getPanel(group.id)) {
449
- const removedGroup = this.doRemoveGroup(group, {
450
- skipDispose: true,
451
- skipActive: true,
452
- });
453
- removedGroup.model.renderContainer =
454
- this.overlayRenderContainer;
455
- removedGroup.model.location = { type: 'grid' };
456
- returnedGroup = removedGroup;
457
- }
387
+ else if (this.getPanel(group.id)) {
388
+ const removedGroup = this.doRemoveGroup(group, {
389
+ skipDispose: true,
390
+ skipActive: true,
391
+ });
392
+ removedGroup.model.renderContainer =
393
+ this.overlayRenderContainer;
394
+ removedGroup.model.location = { type: 'grid' };
395
+ returnedGroup = removedGroup;
458
396
  }
459
397
  }));
460
398
  this._popoutGroups.push(value);
461
399
  this.updateWatermark();
462
400
  })
463
401
  .catch((err) => {
464
- console.error(err);
402
+ console.error('dockview: failed to create popout window', err);
465
403
  });
466
404
  }
467
405
  addFloatingGroup(item, coord, options) {
@@ -504,7 +442,7 @@ export class DockviewComponent extends BaseGrid {
504
442
  this.doRemoveGroup(item, {
505
443
  skipDispose: true,
506
444
  skipPopoutReturn: true,
507
- skipPopoutAssociated: !!popoutReferenceGroup,
445
+ skipPopoutAssociated: false,
508
446
  });
509
447
  }
510
448
  }
@@ -866,7 +804,6 @@ export class DockviewComponent extends BaseGrid {
866
804
  clear() {
867
805
  const groups = Array.from(this._groups.values()).map((_) => _.value);
868
806
  const hasActiveGroup = !!this.activeGroup;
869
- const hasActivePanel = !!this.activePanel;
870
807
  for (const group of groups) {
871
808
  // remove the group will automatically remove the panels
872
809
  this.removeGroup(group, { skipActive: true });
@@ -1372,7 +1309,7 @@ export class DockviewComponent extends BaseGrid {
1372
1309
  }
1373
1310
  let id = options === null || options === void 0 ? void 0 : options.id;
1374
1311
  if (id && this._groups.has(options.id)) {
1375
- console.warn(`Duplicate group id ${options === null || options === void 0 ? void 0 : options.id}. reassigning group id to avoid errors`);
1312
+ console.warn(`dockview: Duplicate group id ${options === null || options === void 0 ? void 0 : options.id}. reassigning group id to avoid errors`);
1376
1313
  id = undefined;
1377
1314
  }
1378
1315
  if (!id) {
@@ -65,12 +65,6 @@ export class DockviewPanel extends CompositeDisposable {
65
65
  const didTitleChange = title !== this.title;
66
66
  if (didTitleChange) {
67
67
  this._title = title;
68
- this.view.update({
69
- params: {
70
- params: this._params,
71
- title: this.title,
72
- },
73
- });
74
68
  this.api._onDidTitleChange.fire({ title });
75
69
  }
76
70
  }
@@ -98,10 +92,7 @@ export class DockviewPanel extends CompositeDisposable {
98
92
  }
99
93
  // update the view with the updated props
100
94
  this.view.update({
101
- params: {
102
- params: this._params,
103
- title: this.title,
104
- },
95
+ params: this._params,
105
96
  });
106
97
  }
107
98
  updateParentGroup(group, options) {
@@ -109,7 +109,7 @@ type AddPanelFloatingGroupUnion = {
109
109
  position: never;
110
110
  };
111
111
  type AddPanelPositionUnion = {
112
- floating: false | never;
112
+ floating: false;
113
113
  position: AddPanelPositionOptions;
114
114
  };
115
115
  type AddPanelOptionsUnion = AddPanelFloatingGroupUnion | AddPanelPositionUnion;
@@ -1,4 +1,3 @@
1
- import { IDockviewComponent } from './dockviewComponent';
2
1
  import { DockviewPanelApi } from '../api/dockviewPanelApi';
3
2
  import { PanelInitParameters, IPanel } from '../panel/types';
4
3
  import { DockviewApi } from '../api/component.api';
@@ -16,25 +15,20 @@ export interface WatermarkRendererInitParameters {
16
15
  containerApi: DockviewApi;
17
16
  group?: IDockviewGroupPanel;
18
17
  }
19
- export interface IWatermarkRenderer extends Optional<Omit<IPanel, 'id' | 'init'>, 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'> {
18
+ type RendererMethodOptionalList = 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus';
19
+ export interface IWatermarkRenderer extends Optional<Omit<IPanel, 'id' | 'init'>, RendererMethodOptionalList> {
20
20
  readonly element: HTMLElement;
21
21
  init: (params: WatermarkRendererInitParameters) => void;
22
22
  updateParentGroup(group: DockviewGroupPanel, visible: boolean): void;
23
23
  }
24
- export interface ITabRenderer extends Optional<Omit<IPanel, 'id'>, 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'> {
24
+ export interface ITabRenderer extends Optional<Omit<IPanel, 'id'>, RendererMethodOptionalList> {
25
25
  readonly element: HTMLElement;
26
26
  init(parameters: GroupPanelPartInitParameters): void;
27
27
  }
28
- export interface IContentRenderer extends Optional<Omit<IPanel, 'id'>, 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'> {
28
+ export interface IContentRenderer extends Optional<Omit<IPanel, 'id'>, RendererMethodOptionalList> {
29
29
  readonly element: HTMLElement;
30
30
  init(parameters: GroupPanelPartInitParameters): void;
31
31
  }
32
- export interface WatermarkPartInitParameters {
33
- accessor: IDockviewComponent;
34
- }
35
- export interface WatermarkConstructor {
36
- new (): IWatermarkRenderer;
37
- }
38
32
  export interface IGroupPanelInitParameters extends PanelInitParameters, HeaderPartInitParameters {
39
33
  }
40
34
  export interface GroupviewPanelState {
@@ -47,3 +41,4 @@ export interface GroupviewPanelState {
47
41
  [key: string]: any;
48
42
  };
49
43
  }
44
+ export {};
File without changes
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ // import { SerializedGridObject } from '../gridview/gridview';
3
+ // import { Orientation } from '../splitview/splitview';
4
+ // import { SerializedDockview } from './dockviewComponent';
5
+ // import { GroupPanelViewState } from './dockviewGroupPanelModel';
6
+ // function typeValidate3(data: GroupPanelViewState, path: string): void {
7
+ // if (typeof data.id !== 'string') {
8
+ // throw new Error(`${path}.id must be a string`);
9
+ // }
10
+ // if (
11
+ // typeof data.activeView !== 'string' ||
12
+ // typeof data.activeView !== 'undefined'
13
+ // ) {
14
+ // throw new Error(`${path}.activeView must be a string of undefined`);
15
+ // }
16
+ // }
17
+ // function typeValidate2(
18
+ // data: SerializedGridObject<GroupPanelViewState>,
19
+ // path: string
20
+ // ): void {
21
+ // if (typeof data.size !== 'number' && typeof data.size !== 'undefined') {
22
+ // throw new Error(`${path}.size must be a number or undefined`);
23
+ // }
24
+ // if (
25
+ // typeof data.visible !== 'boolean' &&
26
+ // typeof data.visible !== 'undefined'
27
+ // ) {
28
+ // throw new Error(`${path}.visible must be a boolean or undefined`);
29
+ // }
30
+ // if (data.type === 'leaf') {
31
+ // if (
32
+ // typeof data.data !== 'object' ||
33
+ // data.data === null ||
34
+ // Array.isArray(data.data)
35
+ // ) {
36
+ // throw new Error('object must be a non-null object');
37
+ // }
38
+ // typeValidate3(data.data, `${path}.data`);
39
+ // } else if (data.type === 'branch') {
40
+ // if (!Array.isArray(data.data)) {
41
+ // throw new Error(`${path}.data must be an array`);
42
+ // }
43
+ // } else {
44
+ // throw new Error(`${path}.type must be onew of {'branch', 'leaf'}`);
45
+ // }
46
+ // }
47
+ // function typeValidate(data: SerializedDockview): void {
48
+ // if (typeof data !== 'object' || data === null) {
49
+ // throw new Error('object must be a non-null object');
50
+ // }
51
+ // const { grid, panels, activeGroup, floatingGroups } = data;
52
+ // if (typeof grid !== 'object' || grid === null) {
53
+ // throw new Error("'.grid' must be a non-null object");
54
+ // }
55
+ // if (typeof grid.height !== 'number') {
56
+ // throw new Error("'.grid.height' must be a number");
57
+ // }
58
+ // if (typeof grid.width !== 'number') {
59
+ // throw new Error("'.grid.width' must be a number");
60
+ // }
61
+ // if (typeof grid.root !== 'object' || grid.root === null) {
62
+ // throw new Error("'.grid.root' must be a non-null object");
63
+ // }
64
+ // if (grid.root.type !== 'branch') {
65
+ // throw new Error(".grid.root.type must be of type 'branch'");
66
+ // }
67
+ // if (
68
+ // grid.orientation !== Orientation.HORIZONTAL &&
69
+ // grid.orientation !== Orientation.VERTICAL
70
+ // ) {
71
+ // throw new Error(
72
+ // `'.grid.width' must be one of {${Orientation.HORIZONTAL}, ${Orientation.VERTICAL}}`
73
+ // );
74
+ // }
75
+ // typeValidate2(grid.root, '.grid.root');
76
+ // }
@@ -50,7 +50,7 @@ class Stacktrace {
50
50
  this.value = value;
51
51
  }
52
52
  print() {
53
- console.warn(this.value);
53
+ console.warn('dockview: stacktrace', this.value);
54
54
  }
55
55
  }
56
56
  class Listener {
@@ -92,7 +92,7 @@ export class Emitter {
92
92
  }
93
93
  else if (Emitter.ENABLE_TRACKING) {
94
94
  // console.warn(
95
- // `Listener already disposed`,
95
+ // `dockview: listener already disposed`,
96
96
  // Stacktrace.create().print()
97
97
  // );
98
98
  }
@@ -120,7 +120,7 @@ export class Emitter {
120
120
  var _a;
121
121
  // don't check until stack of execution is completed to allow for out-of-order disposals within the same execution block
122
122
  for (const listener of this._listeners) {
123
- console.warn((_a = listener.stacktrace) === null || _a === void 0 ? void 0 : _a.print());
123
+ console.warn('dockview: stacktrace', (_a = listener.stacktrace) === null || _a === void 0 ? void 0 : _a.print());
124
124
  }
125
125
  });
126
126
  }
@@ -1,10 +1,11 @@
1
1
  export * from './dnd/dataTransfer';
2
2
  /**
3
- * Events, Emitters and Disposables are very common concepts that most codebases will contain.
4
- * We export them with a 'Dockview' prefix here to prevent accidental use by others.
3
+ * Events, Emitters and Disposables are very common concepts that many codebases will contain, however we need
4
+ * to export them for dockview framework packages to use.
5
+ * To be a good citizen these are exported with a `Dockview` prefix to prevent accidental use by others.
5
6
  */
6
7
  export { Emitter as DockviewEmitter, Event as DockviewEvent } from './events';
7
- export { IDisposable as IDockviewDisposable, MutableDisposable as DockviewMutableDisposable, CompositeDisposable as DockviewCompositeDisposable, } from './lifecycle';
8
+ export { IDisposable as IDockviewDisposable, MutableDisposable as DockviewMutableDisposable, CompositeDisposable as DockviewCompositeDisposable, Disposable as DockviewDisposable, } from './lifecycle';
8
9
  export * from './panel/types';
9
10
  export * from './panel/componentFactory';
10
11
  export * from './splitview/splitview';
package/dist/esm/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  export * from './dnd/dataTransfer';
2
2
  /**
3
- * Events, Emitters and Disposables are very common concepts that most codebases will contain.
4
- * We export them with a 'Dockview' prefix here to prevent accidental use by others.
3
+ * Events, Emitters and Disposables are very common concepts that many codebases will contain, however we need
4
+ * to export them for dockview framework packages to use.
5
+ * To be a good citizen these are exported with a `Dockview` prefix to prevent accidental use by others.
5
6
  */
6
7
  export { Emitter as DockviewEmitter, Event as DockviewEvent } from './events';
7
- export { MutableDisposable as DockviewMutableDisposable, CompositeDisposable as DockviewCompositeDisposable, } from './lifecycle';
8
+ export { MutableDisposable as DockviewMutableDisposable, CompositeDisposable as DockviewCompositeDisposable, Disposable as DockviewDisposable, } from './lifecycle';
8
9
  export * from './panel/types';
9
10
  export * from './panel/componentFactory';
10
11
  export * from './splitview/splitview';
@@ -438,16 +438,16 @@
438
438
  .dv-dockview .dv-overlay-render-container {
439
439
  position: relative;
440
440
  }
441
- .dv-dockview .split-view-container.horizontal > .view-container > .view:not(:last-child) {
441
+ .dv-dockview .split-view-container.horizontal > .view-container > .view:not(:last-child) .groupview {
442
442
  border-right: var(--dv-group-gap-size) solid transparent;
443
443
  }
444
- .dv-dockview .split-view-container.horizontal > .view-container > .view:not(:first-child) {
444
+ .dv-dockview .split-view-container.horizontal > .view-container > .view:not(:first-child) .groupview {
445
445
  border-left: var(--dv-group-gap-size) solid transparent;
446
446
  }
447
- .dv-dockview .split-view-container.vertical > .view-container > .view:not(:last-child) {
447
+ .dv-dockview .split-view-container.vertical > .view-container > .view:not(:last-child) .groupview {
448
448
  border-bottom: var(--dv-group-gap-size) solid transparent;
449
449
  }
450
- .dv-dockview .split-view-container.vertical > .view-container > .view:not(:first-child) {
450
+ .dv-dockview .split-view-container.vertical > .view-container > .view:not(:first-child) .groupview {
451
451
  border-top: var(--dv-group-gap-size) solid transparent;
452
452
  }
453
453
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dockview-core",
3
- "version": "1.13.1",
3
+ "version": "1.14.1",
4
4
  "description": "Zero dependency layout manager supporting tabs, grids and splitviews",
5
5
  "keywords": [
6
6
  "splitview",