@jbrowse/app-core 4.2.1 → 5.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.
Files changed (154) hide show
  1. package/esm/AppSession/AppRootModel.d.ts +30 -0
  2. package/esm/AppSession/AppRootModel.js +1 -0
  3. package/esm/AppSession/AppSessionMixin.d.ts +1598 -0
  4. package/esm/AppSession/AppSessionMixin.js +62 -0
  5. package/esm/AppSession/index.d.ts +2 -0
  6. package/esm/AppSession/index.js +1 -0
  7. package/esm/Assemblies/AssembliesMixin.d.ts +225 -0
  8. package/esm/Assemblies/AssembliesMixin.js +149 -0
  9. package/esm/Assemblies/index.d.ts +1 -2
  10. package/esm/Assemblies/index.js +1 -2
  11. package/esm/HistoryManagement/index.d.ts +19 -0
  12. package/esm/HistoryManagement/index.js +42 -5
  13. package/esm/JBrowseConfig/RootConfiguration.d.ts +167 -78
  14. package/esm/JBrowseConfig/RootConfiguration.js +85 -7
  15. package/esm/JBrowseConfig/index.d.ts +169 -84
  16. package/esm/JBrowseConfig/index.js +62 -2
  17. package/esm/JBrowseModel/index.d.ts +176 -95
  18. package/esm/JBrowseModel/index.js +122 -21
  19. package/esm/RootMenu/index.d.ts +77 -2
  20. package/esm/RootMenu/index.js +107 -61
  21. package/esm/SessionSpec/index.d.ts +6 -0
  22. package/esm/SessionSpec/index.js +3 -0
  23. package/esm/SessionSpec/lgvUrlInit.d.ts +79 -0
  24. package/esm/SessionSpec/lgvUrlInit.js +158 -0
  25. package/esm/SessionSpec/loadSessionSpec.d.ts +21 -0
  26. package/esm/SessionSpec/loadSessionSpec.js +311 -0
  27. package/esm/SessionSpec/parseSessionSpecUrl.d.ts +13 -0
  28. package/esm/SessionSpec/parseSessionSpecUrl.js +196 -0
  29. package/esm/SessionSpec/types.d.ts +33 -0
  30. package/esm/SessionSpec/types.js +1 -0
  31. package/esm/WorkspaceLayout/LayoutRenderer.d.ts +24 -0
  32. package/esm/WorkspaceLayout/LayoutRenderer.js +185 -0
  33. package/esm/WorkspaceLayout/PanelView.d.ts +13 -0
  34. package/esm/WorkspaceLayout/PanelView.js +99 -0
  35. package/esm/WorkspaceLayout/TabStrip.d.ts +14 -0
  36. package/esm/WorkspaceLayout/TabStrip.js +226 -0
  37. package/esm/WorkspaceLayout/WorkspaceContainer.d.ts +16 -0
  38. package/esm/WorkspaceLayout/WorkspaceContainer.js +105 -0
  39. package/esm/WorkspaceLayout/WorkspacePanelActions.d.ts +24 -0
  40. package/esm/WorkspaceLayout/WorkspacePanelActions.js +120 -0
  41. package/esm/WorkspaceLayout/WorkspaceTab.d.ts +27 -0
  42. package/esm/WorkspaceLayout/WorkspaceTab.js +109 -0
  43. package/esm/WorkspaceLayout/dockviewTheme.d.ts +69 -0
  44. package/esm/WorkspaceLayout/dockviewTheme.js +82 -0
  45. package/esm/WorkspaceLayout/dropZone.d.ts +105 -0
  46. package/esm/WorkspaceLayout/dropZone.js +113 -0
  47. package/esm/WorkspaceLayout/index.d.ts +8 -0
  48. package/esm/WorkspaceLayout/index.js +4 -0
  49. package/esm/WorkspaceLayout/model.d.ts +240 -0
  50. package/esm/WorkspaceLayout/model.js +527 -0
  51. package/esm/WorkspaceLayout/panelChrome.d.ts +19 -0
  52. package/esm/WorkspaceLayout/panelChrome.js +4 -0
  53. package/esm/WorkspaceLayout/spec.d.ts +58 -0
  54. package/esm/WorkspaceLayout/spec.js +153 -0
  55. package/esm/WorkspaceLayout/splitter.d.ts +25 -0
  56. package/esm/WorkspaceLayout/splitter.js +37 -0
  57. package/esm/WorkspaceLayout/tree.d.ts +158 -0
  58. package/esm/WorkspaceLayout/tree.js +379 -0
  59. package/esm/WorkspaceLayout/useLayoutDrag.d.ts +34 -0
  60. package/esm/WorkspaceLayout/useLayoutDrag.js +232 -0
  61. package/esm/index.d.ts +16 -9
  62. package/esm/index.js +13 -9
  63. package/esm/menus.d.ts +63 -62
  64. package/esm/menus.js +131 -84
  65. package/esm/queryParams.d.ts +9 -0
  66. package/esm/queryParams.js +92 -0
  67. package/esm/sessionModelFactory.d.ts +18 -0
  68. package/esm/sessionModelFactory.js +1 -0
  69. package/esm/ui/App/App.d.ts +3 -14
  70. package/esm/ui/App/App.js +30 -17
  71. package/esm/ui/App/AppFab.d.ts +12 -3
  72. package/esm/ui/App/AppFab.js +15 -8
  73. package/esm/ui/App/AppReadyMarker.d.ts +34 -0
  74. package/esm/ui/App/AppReadyMarker.js +56 -0
  75. package/esm/ui/App/AppToolbar.d.ts +2 -14
  76. package/esm/ui/App/AppToolbar.js +23 -24
  77. package/esm/ui/App/ClassicViewsContainer.d.ts +3 -9
  78. package/esm/ui/App/ClassicViewsContainer.js +9 -8
  79. package/esm/ui/App/DialogQueue.d.ts +17 -1
  80. package/esm/ui/App/DialogQueue.js +30 -1
  81. package/esm/ui/App/Drawer.d.ts +1 -1
  82. package/esm/ui/App/Drawer.js +10 -39
  83. package/esm/ui/App/DrawerControls.d.ts +1 -5
  84. package/esm/ui/App/DrawerControls.js +1 -25
  85. package/esm/ui/App/DrawerHeader.d.ts +2 -3
  86. package/esm/ui/App/DrawerHeader.js +24 -10
  87. package/esm/ui/App/DrawerHeaderHelpButton.d.ts +1 -1
  88. package/esm/ui/App/DrawerHeaderHelpButton.js +3 -3
  89. package/esm/ui/App/DrawerHeaderHelpDialog.d.ts +2 -2
  90. package/esm/ui/App/DrawerHeaderHelpDialog.js +3 -4
  91. package/esm/ui/App/DrawerWidget.d.ts +1 -1
  92. package/esm/ui/App/DrawerWidget.js +9 -21
  93. package/esm/ui/App/JBrowseTabMenu.d.ts +2 -3
  94. package/esm/ui/App/JBrowseTabMenu.js +24 -5
  95. package/esm/ui/App/ViewButtons.d.ts +2 -4
  96. package/esm/ui/App/ViewButtons.js +15 -3
  97. package/esm/ui/App/ViewContainer.d.ts +3 -2
  98. package/esm/ui/App/ViewContainer.js +144 -24
  99. package/esm/ui/App/ViewContainerTitle.d.ts +1 -1
  100. package/esm/ui/App/ViewContainerTitle.js +9 -2
  101. package/esm/ui/App/ViewErrorFallback.d.ts +18 -0
  102. package/esm/ui/App/ViewErrorFallback.js +42 -0
  103. package/esm/ui/App/ViewHeader.d.ts +3 -4
  104. package/esm/ui/App/ViewHeader.js +28 -17
  105. package/esm/ui/App/ViewLauncher.d.ts +2 -8
  106. package/esm/ui/App/ViewLauncher.js +7 -5
  107. package/esm/ui/App/ViewMenu.d.ts +3 -5
  108. package/esm/ui/App/ViewMenu.js +87 -92
  109. package/esm/ui/App/ViewStack.d.ts +7 -0
  110. package/esm/ui/App/ViewStack.js +42 -0
  111. package/esm/ui/App/ViewWrapper.d.ts +15 -1
  112. package/esm/ui/App/ViewWrapper.js +26 -10
  113. package/esm/ui/App/ViewsContainer.d.ts +3 -8
  114. package/esm/ui/App/ViewsContainer.js +9 -6
  115. package/esm/ui/App/copyView.js +1 -1
  116. package/esm/ui/App/index.d.ts +2 -1
  117. package/esm/ui/App/index.js +3 -2
  118. package/esm/ui/App/lazyChunk.d.ts +13 -0
  119. package/esm/ui/App/lazyChunk.js +91 -0
  120. package/esm/ui/App/types.d.ts +12 -3
  121. package/esm/ui/App/types.js +1 -1
  122. package/esm/ui/App/useViewVisibility.d.ts +34 -0
  123. package/esm/ui/App/useViewVisibility.js +87 -0
  124. package/esm/ui/App/viewTitle.d.ts +5 -0
  125. package/esm/ui/App/viewTitle.js +9 -0
  126. package/esm/ui/index.d.ts +1 -1
  127. package/esm/ui/index.js +1 -1
  128. package/package.json +12 -17
  129. package/esm/AppFocus/index.d.ts +0 -5
  130. package/esm/AppFocus/index.js +0 -12
  131. package/esm/Assemblies/SessionAssembliesMixin.d.ts +0 -92
  132. package/esm/Assemblies/SessionAssembliesMixin.js +0 -53
  133. package/esm/Assemblies/TemporaryAssembliesMixin.d.ts +0 -10
  134. package/esm/Assemblies/TemporaryAssembliesMixin.js +0 -37
  135. package/esm/DockviewLayout/index.d.ts +0 -35
  136. package/esm/DockviewLayout/index.js +0 -111
  137. package/esm/ui/App/DockviewContext.d.ts +0 -22
  138. package/esm/ui/App/DockviewContext.js +0 -29
  139. package/esm/ui/App/DockviewLeftHeaderActions.d.ts +0 -2
  140. package/esm/ui/App/DockviewLeftHeaderActions.js +0 -58
  141. package/esm/ui/App/DockviewRightHeaderActions.d.ts +0 -2
  142. package/esm/ui/App/DockviewRightHeaderActions.js +0 -100
  143. package/esm/ui/App/DrawerWidgetSelector.d.ts +0 -5
  144. package/esm/ui/App/DrawerWidgetSelector.js +0 -47
  145. package/esm/ui/App/JBrowseViewPanel.d.ts +0 -8
  146. package/esm/ui/App/JBrowseViewPanel.js +0 -40
  147. package/esm/ui/App/JBrowseViewTab.d.ts +0 -8
  148. package/esm/ui/App/JBrowseViewTab.js +0 -97
  149. package/esm/ui/App/ModalWidget.d.ts +0 -6
  150. package/esm/ui/App/ModalWidget.js +0 -48
  151. package/esm/ui/App/TiledViewsContainer.d.ts +0 -6
  152. package/esm/ui/App/TiledViewsContainer.js +0 -366
  153. package/esm/ui/App/dockviewUtils.d.ts +0 -46
  154. package/esm/ui/App/dockviewUtils.js +0 -55
@@ -0,0 +1,24 @@
1
+ import type { WorkspaceSessionType } from '../ui/App/types.ts';
2
+ import type { WorkspaceLayout } from './model.ts';
3
+ import type { PanelNode } from './tree.ts';
4
+ /**
5
+ * The per-cell buttons: new tab, split, close.
6
+ *
7
+ * Every one of these is a single MST action on the layout, where the dockview
8
+ * versions were an api call plus a session write that had to be kept consistent
9
+ * with it — `handleSplit` was `containerApi.addGroup(...)` followed by
10
+ * `addEmptyTab(newGroup)`, and the gap between those two was where the
11
+ * "resource is already disposed" crash lived.
12
+ */
13
+ export declare const WorkspacePanelActions: ({ panel, session, onClose, }: {
14
+ panel: PanelNode;
15
+ session: WorkspaceSessionType & WorkspaceLayout;
16
+ /**
17
+ * Closing a cell closes the views its tabs held, and the layout does not own
18
+ * views — so the pair is one function at the call site, the same way
19
+ * `WorkspaceTab` takes its `onClose`. Spelled here as well, this file would
20
+ * be the third place stating "and also remove the views", which is one more
21
+ * than the number that can be kept in step.
22
+ */
23
+ onClose: () => void;
24
+ }) => import("react").JSX.Element;
@@ -0,0 +1,120 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import AddIcon from '@mui/icons-material/Add';
5
+ import CloseIcon from '@mui/icons-material/Close';
6
+ import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
7
+ import DynamicFeedIcon from '@mui/icons-material/DynamicFeed';
8
+ import HorizontalSplitIcon from '@mui/icons-material/HorizontalSplit';
9
+ import OpenInFullIcon from '@mui/icons-material/OpenInFull';
10
+ import TabIcon from '@mui/icons-material/Tab';
11
+ import TableRowsIcon from '@mui/icons-material/TableRows';
12
+ import VerticalSplitIcon from '@mui/icons-material/VerticalSplit';
13
+ import ViewColumnIcon from '@mui/icons-material/ViewColumn';
14
+ import ViewModuleIcon from '@mui/icons-material/ViewModule';
15
+ import { IconButton, Tooltip } from '@mui/material';
16
+ import { observer } from 'mobx-react';
17
+ import { dv } from './dockviewTheme.js';
18
+ const useStyles = makeStyles()({
19
+ actions: { display: 'flex', alignItems: 'center', flex: '0 0 auto' },
20
+ // dockview's `.dv-default-tab-action`: inherits the strip's colour, and takes
21
+ // a small rounded chip on hover rather than MUI's circular ripple surface
22
+ button: {
23
+ padding: 4,
24
+ borderRadius: 2,
25
+ color: dv.activeGroupHiddenTabColor,
26
+ '&:hover': { background: dv.iconHoverBackground, borderRadius: 2 },
27
+ },
28
+ icon: { fontSize: 16 },
29
+ });
30
+ /**
31
+ * The per-cell buttons: new tab, split, close.
32
+ *
33
+ * Every one of these is a single MST action on the layout, where the dockview
34
+ * versions were an api call plus a session write that had to be kept consistent
35
+ * with it — `handleSplit` was `containerApi.addGroup(...)` followed by
36
+ * `addEmptyTab(newGroup)`, and the gap between those two was where the
37
+ * "resource is already disposed" crash lived.
38
+ */
39
+ export const WorkspacePanelActions = observer(function WorkspacePanelActions({ panel, session, onClose, }) {
40
+ const { classes } = useStyles();
41
+ const canClose = session.panels.length > 1;
42
+ const maximized = session.maximizedPanelId === panel.id;
43
+ const tile = (mode) => {
44
+ session.tileViews(mode, session.views.map(v => v.id));
45
+ };
46
+ return (_jsxs("div", { className: classes.actions, children: [_jsx(CascadingMenuButton, { menuItems: [
47
+ {
48
+ label: 'New empty tab',
49
+ icon: TabIcon,
50
+ onClick: () => {
51
+ session.addTab(panel.id);
52
+ },
53
+ },
54
+ {
55
+ label: 'New empty split horizontal',
56
+ icon: VerticalSplitIcon,
57
+ onClick: () => {
58
+ session.splitPanel(panel.id, 'row');
59
+ },
60
+ },
61
+ {
62
+ label: 'New empty split vertical',
63
+ icon: HorizontalSplitIcon,
64
+ onClick: () => {
65
+ session.splitPanel(panel.id, 'column');
66
+ },
67
+ },
68
+ // The strip's double-click is the gesture; this is how anyone finds
69
+ // it, and the only way to reach it from the keyboard. Gated on there
70
+ // being another cell to hide, since maximizing the only one is a
71
+ // no-op with a label promising otherwise.
72
+ ...(session.panels.length > 1 || maximized
73
+ ? [
74
+ {
75
+ label: maximized ? 'Restore panel' : 'Maximize panel',
76
+ icon: maximized ? CloseFullscreenIcon : OpenInFullIcon,
77
+ onClick: () => {
78
+ session.toggleMaximizedPanel(panel.id);
79
+ },
80
+ },
81
+ ]
82
+ : []),
83
+ // The whole-workspace commands, kept behind the same "Global:" prefix
84
+ // and the same >1-view gate they had on the dockview header, so a
85
+ // returning user finds them where they were and the label still says
86
+ // that the button in THIS cell rearranges every cell.
87
+ ...(session.views.length > 1
88
+ ? [
89
+ {
90
+ label: 'Global: change layout into set of tabs',
91
+ icon: DynamicFeedIcon,
92
+ onClick: () => {
93
+ tile('tabs');
94
+ },
95
+ },
96
+ {
97
+ label: 'Global: tile horizontally',
98
+ icon: ViewColumnIcon,
99
+ onClick: () => {
100
+ tile('horizontal');
101
+ },
102
+ },
103
+ {
104
+ label: 'Global: tile vertically',
105
+ icon: TableRowsIcon,
106
+ onClick: () => {
107
+ tile('vertical');
108
+ },
109
+ },
110
+ {
111
+ label: 'Global: tile grid',
112
+ icon: ViewModuleIcon,
113
+ onClick: () => {
114
+ tile('grid');
115
+ },
116
+ },
117
+ ]
118
+ : []),
119
+ ], size: "small", className: classes.button, children: _jsx(AddIcon, { className: classes.icon }) }), canClose ? (_jsx(Tooltip, { title: "Close panel", children: _jsx(IconButton, { size: "small", className: classes.button, onClick: onClose, children: _jsx(CloseIcon, { className: classes.icon }) }) })) : null] }));
120
+ });
@@ -0,0 +1,27 @@
1
+ import type { WorkspaceSessionType } from '../ui/App/types.ts';
2
+ import type { WorkspaceLayout } from './model.ts';
3
+ import type { TabNode } from './tree.ts';
4
+ import type { AbstractViewModel } from '@jbrowse/core/util';
5
+ /**
6
+ * A tab's name is **derived** from the views it holds, unless the user set one.
7
+ *
8
+ * `title === undefined` is the sentinel for "not renamed", which is a plain
9
+ * `maybe` on the tab rather than the comparison the dockview version needed:
10
+ * dockview restores an unset title as the panel id, so telling a real title
11
+ * from a restored one meant testing `title !== panelId` and hoping nobody named
12
+ * a tab after a panel.
13
+ */
14
+ export declare function tabDisplayName(tab: TabNode, views: AbstractViewModel[], session: WorkspaceSessionType): string;
15
+ export declare const WorkspaceTab: ({ tab, views, session, layout, onClose, }: {
16
+ tab: TabNode;
17
+ views: AbstractViewModel[];
18
+ session: WorkspaceSessionType;
19
+ layout: WorkspaceLayout;
20
+ /**
21
+ * Closing a tab closes the views it held, and the layout does not own views —
22
+ * so the pair is one function at the call site rather than spelled out here.
23
+ * The strip's middle-click needs the same pair, and two spellings of "and
24
+ * also remove the views" is how one of them comes to be missing it.
25
+ */
26
+ onClose: () => void;
27
+ }) => import("react").JSX.Element;
@@ -0,0 +1,109 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { InputBase, Typography } from '@mui/material';
5
+ import { observer } from 'mobx-react';
6
+ import JBrowseTabMenu from '../ui/App/JBrowseTabMenu.js';
7
+ const useStyles = makeStyles()(theme => ({
8
+ // Only the label and its menu. The background, the selected state and the
9
+ // hover live on the strip's own `[role=tab]` wrapper, which is where the
10
+ // selection is known — two places styling one tab is how they drift.
11
+ tab: {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: 4,
15
+ minWidth: 0,
16
+ },
17
+ // dockview's `.dv-default-tab-content`: takes the space, truncates with an
18
+ // ellipsis, and takes its colour from the tab, which is what encodes
19
+ // active/inactive
20
+ title: {
21
+ flexGrow: 1,
22
+ marginRight: 4,
23
+ overflow: 'hidden',
24
+ textOverflow: 'ellipsis',
25
+ whiteSpace: 'nowrap',
26
+ fontSize: 'inherit',
27
+ color: 'inherit',
28
+ },
29
+ editInput: {
30
+ fontSize: 'inherit',
31
+ padding: '2px 4px',
32
+ color: theme.palette.text.primary,
33
+ backgroundColor: theme.palette.background.paper,
34
+ borderRadius: theme.shape.borderRadius,
35
+ flex: 1,
36
+ },
37
+ }));
38
+ /**
39
+ * A tab's name is **derived** from the views it holds, unless the user set one.
40
+ *
41
+ * `title === undefined` is the sentinel for "not renamed", which is a plain
42
+ * `maybe` on the tab rather than the comparison the dockview version needed:
43
+ * dockview restores an unset title as the panel id, so telling a real title
44
+ * from a restored one meant testing `title !== panelId` and hoping nobody named
45
+ * a tab after a panel.
46
+ */
47
+ export function tabDisplayName(tab, views, session) {
48
+ if (tab.title) {
49
+ return tab.title;
50
+ }
51
+ if (views.length === 0) {
52
+ return 'Empty';
53
+ }
54
+ if (views.length === 1) {
55
+ const view = views[0];
56
+ return (view.displayName ||
57
+ view.assemblyNames
58
+ ?.map(r => session.assemblyManager.getDisplayName(r))
59
+ .join(',') ||
60
+ 'View');
61
+ }
62
+ return `${views.length} views`;
63
+ }
64
+ export const WorkspaceTab = observer(function WorkspaceTab({ tab, views, session, layout, onClose, }) {
65
+ const { classes } = useStyles();
66
+ const [editing, setEditing] = useState(false);
67
+ const [draft, setDraft] = useState('');
68
+ const title = tabDisplayName(tab, views, session);
69
+ const save = () => {
70
+ // An empty box means "go back to the automatic name". `title === undefined`
71
+ // is the sentinel for "not renamed" and `renameTab` takes it, but nothing
72
+ // in the UI passed it: clearing the box just discarded the edit, so a
73
+ // rename could be made and never unmade.
74
+ layout.renameTab(tab.id, draft.trim() || undefined);
75
+ setEditing(false);
76
+ };
77
+ return (_jsx("div", { className: classes.tab, children: editing ? (_jsx(InputBase, { autoFocus: true, className: classes.editInput, value: draft, onChange: e => {
78
+ setDraft(e.target.value);
79
+ }, onFocus: e => {
80
+ e.target.select();
81
+ }, onBlur: save, onKeyDown: e => {
82
+ // The box sits INSIDE the `role="tab"` that owns the strip's roving
83
+ // tabindex, and every key it handles — the arrows, Home, End, Enter
84
+ // and Space — it also preventDefault()s. All of them reach that
85
+ // handler by bubbling out of here, so while the box is open the
86
+ // arrows jumped to the next tab instead of moving the caret and a
87
+ // space never reached the input at all: a tab could not be given a
88
+ // name with a space in it.
89
+ e.stopPropagation();
90
+ if (e.key === 'Enter') {
91
+ save();
92
+ }
93
+ else if (e.key === 'Escape') {
94
+ setEditing(false);
95
+ }
96
+ },
97
+ // the tab strip starts a drag on pointerdown; renaming must not
98
+ onPointerDown: e => {
99
+ e.stopPropagation();
100
+ }, onClick: e => {
101
+ e.stopPropagation();
102
+ } })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { className: classes.title, variant: "body2", onDoubleClick: () => {
103
+ setDraft(title);
104
+ setEditing(true);
105
+ }, children: title }), _jsx(JBrowseTabMenu, { onRename: () => {
106
+ setDraft(title);
107
+ setEditing(true);
108
+ }, onClose: onClose })] })) }));
109
+ });
@@ -0,0 +1,69 @@
1
+ /**
2
+ * dockview's `dockview-theme-dark`, transcribed.
3
+ *
4
+ * We render the workspace ourselves now, but the look is deliberately not a new
5
+ * design — it is the theme we already shipped, so nothing about the chrome
6
+ * changed for users when the engine underneath it did. Every value below is
7
+ * copied from `dockview/dist/styles/dockview.css`'s `.dockview-theme-dark`
8
+ * block (v8.0.0); the names are its CSS custom properties with the `--dv-`
9
+ * prefix dropped.
10
+ *
11
+ * **Fixed, not theme-derived.** These apply in a light JBrowse theme too, the
12
+ * same way the app bar is dark in both — the chrome frames the content rather
13
+ * than matching it. Don't wire them to `theme.palette`: a light strip reads as
14
+ * content, which is exactly what it looked like before this file existed.
15
+ *
16
+ * **This is the chrome only, and the panel BODY is not chrome.** dockview also
17
+ * has a `--dv-group-view-background-color` (`#1e1e1e`) for the surface a tab's
18
+ * content sits on, and transcribing it is what made a light JBrowse theme come
19
+ * up dark below the views — the frame swallowing the thing it frames. The body
20
+ * follows `theme.palette.background.default` in `PanelView` instead, so the
21
+ * cell matches every other surface the app draws content on. It is deliberately
22
+ * absent here rather than present and unused, since an unused colour named
23
+ * after the panel reads as a value someone forgot to wire up.
24
+ *
25
+ * A consequence worth knowing before "fixing" it: dockview's selected tab is
26
+ * `#1e1e1e` **because** the body was, so the tab merged into its content. Ours
27
+ * cannot merge into a light body, and the four values below are kept anyway —
28
+ * darker-than-the-strip still reads as selected, and it is the one cue that
29
+ * survives being the only dark band on screen.
30
+ *
31
+ * Kept in one module because four components draw parts of the same chrome, and
32
+ * the four-way tab colouring below is a rule about the whole, not about any one
33
+ * of them.
34
+ */
35
+ export declare const dv: {
36
+ /** the tab strip itself */
37
+ readonly tabsBackground: '#252526';
38
+ readonly activeGroupVisibleTabBackground: '#1e1e1e';
39
+ readonly activeGroupHiddenTabBackground: '#2d2d2d';
40
+ readonly inactiveGroupVisibleTabBackground: '#1e1e1e';
41
+ readonly inactiveGroupHiddenTabBackground: '#2d2d2d';
42
+ readonly activeGroupVisibleTabColor: 'white';
43
+ readonly activeGroupHiddenTabColor: '#969696';
44
+ readonly inactiveGroupVisibleTabColor: '#8f8f8f';
45
+ readonly inactiveGroupHiddenTabColor: '#626262';
46
+ readonly tabDividerColor: '#1e1e1e';
47
+ readonly separatorBorder: 'rgb(68, 68, 68)';
48
+ readonly iconHoverBackground: 'rgba(90, 93, 94, 0.31)';
49
+ readonly dragOverBackground: 'rgba(83, 89, 93, 0.5)';
50
+ readonly edgeDockIndicatorColor: 'rgba(56, 139, 253, 0.9)';
51
+ readonly tabsHeight: 35;
52
+ readonly tabsFontSize: 13;
53
+ /** the sash is 4px of grab area with a 1px line drawn inside it */
54
+ readonly sashSize: 4;
55
+ };
56
+ /** The background and text a tab takes, given where it sits. */
57
+ export declare function tabColors(groupActive: boolean, visible: boolean): {
58
+ background: "#1e1e1e";
59
+ color: "white";
60
+ } | {
61
+ background: "#2d2d2d";
62
+ color: "#969696";
63
+ } | {
64
+ background: "#1e1e1e";
65
+ color: "#8f8f8f";
66
+ } | {
67
+ background: "#2d2d2d";
68
+ color: "#626262";
69
+ };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * dockview's `dockview-theme-dark`, transcribed.
3
+ *
4
+ * We render the workspace ourselves now, but the look is deliberately not a new
5
+ * design — it is the theme we already shipped, so nothing about the chrome
6
+ * changed for users when the engine underneath it did. Every value below is
7
+ * copied from `dockview/dist/styles/dockview.css`'s `.dockview-theme-dark`
8
+ * block (v8.0.0); the names are its CSS custom properties with the `--dv-`
9
+ * prefix dropped.
10
+ *
11
+ * **Fixed, not theme-derived.** These apply in a light JBrowse theme too, the
12
+ * same way the app bar is dark in both — the chrome frames the content rather
13
+ * than matching it. Don't wire them to `theme.palette`: a light strip reads as
14
+ * content, which is exactly what it looked like before this file existed.
15
+ *
16
+ * **This is the chrome only, and the panel BODY is not chrome.** dockview also
17
+ * has a `--dv-group-view-background-color` (`#1e1e1e`) for the surface a tab's
18
+ * content sits on, and transcribing it is what made a light JBrowse theme come
19
+ * up dark below the views — the frame swallowing the thing it frames. The body
20
+ * follows `theme.palette.background.default` in `PanelView` instead, so the
21
+ * cell matches every other surface the app draws content on. It is deliberately
22
+ * absent here rather than present and unused, since an unused colour named
23
+ * after the panel reads as a value someone forgot to wire up.
24
+ *
25
+ * A consequence worth knowing before "fixing" it: dockview's selected tab is
26
+ * `#1e1e1e` **because** the body was, so the tab merged into its content. Ours
27
+ * cannot merge into a light body, and the four values below are kept anyway —
28
+ * darker-than-the-strip still reads as selected, and it is the one cue that
29
+ * survives being the only dark band on screen.
30
+ *
31
+ * Kept in one module because four components draw parts of the same chrome, and
32
+ * the four-way tab colouring below is a rule about the whole, not about any one
33
+ * of them.
34
+ */
35
+ export const dv = {
36
+ /** the tab strip itself */
37
+ tabsBackground: '#252526',
38
+ // A tab's colours depend on TWO things: whether its panel is the active one,
39
+ // and whether it is the tab that panel is showing. dockview enumerates all
40
+ // four rather than dimming one axis, so the focused panel's selected tab is
41
+ // the only fully-white label on screen.
42
+ activeGroupVisibleTabBackground: '#1e1e1e',
43
+ activeGroupHiddenTabBackground: '#2d2d2d',
44
+ inactiveGroupVisibleTabBackground: '#1e1e1e',
45
+ inactiveGroupHiddenTabBackground: '#2d2d2d',
46
+ activeGroupVisibleTabColor: 'white',
47
+ activeGroupHiddenTabColor: '#969696',
48
+ inactiveGroupVisibleTabColor: '#8f8f8f',
49
+ inactiveGroupHiddenTabColor: '#626262',
50
+ tabDividerColor: '#1e1e1e',
51
+ separatorBorder: 'rgb(68, 68, 68)',
52
+ iconHoverBackground: 'rgba(90, 93, 94, 0.31)',
53
+ dragOverBackground: 'rgba(83, 89, 93, 0.5)',
54
+ edgeDockIndicatorColor: 'rgba(56, 139, 253, 0.9)',
55
+ tabsHeight: 35,
56
+ tabsFontSize: 13,
57
+ /** the sash is 4px of grab area with a 1px line drawn inside it */
58
+ sashSize: 4,
59
+ };
60
+ /** The background and text a tab takes, given where it sits. */
61
+ export function tabColors(groupActive, visible) {
62
+ if (groupActive) {
63
+ return visible
64
+ ? {
65
+ background: dv.activeGroupVisibleTabBackground,
66
+ color: dv.activeGroupVisibleTabColor,
67
+ }
68
+ : {
69
+ background: dv.activeGroupHiddenTabBackground,
70
+ color: dv.activeGroupHiddenTabColor,
71
+ };
72
+ }
73
+ return visible
74
+ ? {
75
+ background: dv.inactiveGroupVisibleTabBackground,
76
+ color: dv.inactiveGroupVisibleTabColor,
77
+ }
78
+ : {
79
+ background: dv.inactiveGroupHiddenTabBackground,
80
+ color: dv.inactiveGroupHiddenTabColor,
81
+ };
82
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Where a pointer sitting over a panel would drop a view.
3
+ *
4
+ * Kept as geometry over a plain rect rather than reading the DOM, because this
5
+ * is the part of drag-and-drop with actual decisions in it — the edge band, the
6
+ * tie-break between two edges, what happens in the middle — and all of it is
7
+ * checkable without rendering anything or synthesising a pointer.
8
+ */
9
+ export type DropZone = 'center' | 'left' | 'right' | 'top' | 'bottom';
10
+ export interface Rect {
11
+ left: number;
12
+ top: number;
13
+ width: number;
14
+ height: number;
15
+ }
16
+ /**
17
+ * Where a drop would land. `zone` alone says which half of a cell; `strip` is
18
+ * set when the pointer is on the tab strip itself, which is a finer answer than
19
+ * `center` — that one appends, this one says where in the order.
20
+ */
21
+ export interface DropTarget {
22
+ zone: DropZone;
23
+ strip?: StripDrop;
24
+ }
25
+ /** A drop on the strip: the position in the tab order, and where to draw it. */
26
+ export interface StripDrop {
27
+ /** insertion index into the target panel's tabs, as they are ordered NOW */
28
+ index: number;
29
+ /** x of that gap, in the same space as the rects it was computed from */
30
+ left: number;
31
+ }
32
+ /**
33
+ * `center` means "add as a tab in this panel"; an edge means "split this panel
34
+ * and put the view in the new half".
35
+ *
36
+ * The bands are proportional rather than a fixed pixel depth so a narrow panel
37
+ * stays droppable — at a fixed 40px a 100px-wide panel would be all edge and
38
+ * have no centre to drop a tab into. `edgeFraction` is capped below 0.5 for the
39
+ * same reason: at 0.5 the centre vanishes entirely.
40
+ *
41
+ * Corners go to whichever edge the pointer is proportionally deeper into, so
42
+ * the diagonal is the tie-break and neither axis silently wins. **This is a
43
+ * deliberate divergence from dockview**, whose `calculateQuadrantAsPercentage`
44
+ * tests left, right, top, bottom in that order and returns the first hit — so
45
+ * in its top-left corner `left` always wins, however far into the top band the
46
+ * pointer is. Its band is 20% where this defaults to 25%.
47
+ */
48
+ export declare function dropZoneAt(rect: Rect, x: number, y: number, edgeFraction?: number): DropZone;
49
+ /**
50
+ * Which gap between tabs a pointer at `x` is nearest — the insertion index a
51
+ * drop there would use, and the x to draw the caret at.
52
+ *
53
+ * The test is each tab's MIDPOINT rather than its edges, so every x belongs to
54
+ * exactly one gap and there is no dead band between tabs where a drop would
55
+ * have to fall back to appending. `rects` are in strip order and `x` is in
56
+ * their coordinate space, whatever that is — this function does not know
57
+ * whether it was handed viewport or panel-relative numbers.
58
+ *
59
+ * An empty strip is index 0, which is the only sensible place for a first tab.
60
+ */
61
+ export declare function stripDropAt(rects: Rect[], x: number): StripDrop;
62
+ /**
63
+ * The split a drop on `zone` asks for: which way the panel divides, and whether
64
+ * the new half goes first. `center` is not a split.
65
+ */
66
+ export declare function splitForZone(zone: DropZone): {
67
+ readonly direction: 'row';
68
+ readonly before: true;
69
+ } | {
70
+ readonly direction: 'row';
71
+ readonly before: false;
72
+ } | {
73
+ readonly direction: 'column';
74
+ readonly before: true;
75
+ } | {
76
+ readonly direction: 'column';
77
+ readonly before: false;
78
+ } | undefined;
79
+ /** Where the drop indicator goes, as CSS percentages of the panel. */
80
+ export declare function indicatorRect(zone: DropZone): {
81
+ readonly left: '0%';
82
+ readonly top: '0%';
83
+ readonly width: '50%';
84
+ readonly height: '100%';
85
+ } | {
86
+ readonly left: '50%';
87
+ readonly top: '0%';
88
+ readonly width: '50%';
89
+ readonly height: '100%';
90
+ } | {
91
+ readonly left: '0%';
92
+ readonly top: '0%';
93
+ readonly width: '100%';
94
+ readonly height: '50%';
95
+ } | {
96
+ readonly left: '0%';
97
+ readonly top: '50%';
98
+ readonly width: '100%';
99
+ readonly height: '50%';
100
+ } | {
101
+ readonly left: '0%';
102
+ readonly top: '0%';
103
+ readonly width: '100%';
104
+ readonly height: '100%';
105
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Where a pointer sitting over a panel would drop a view.
3
+ *
4
+ * Kept as geometry over a plain rect rather than reading the DOM, because this
5
+ * is the part of drag-and-drop with actual decisions in it — the edge band, the
6
+ * tie-break between two edges, what happens in the middle — and all of it is
7
+ * checkable without rendering anything or synthesising a pointer.
8
+ */
9
+ /**
10
+ * `center` means "add as a tab in this panel"; an edge means "split this panel
11
+ * and put the view in the new half".
12
+ *
13
+ * The bands are proportional rather than a fixed pixel depth so a narrow panel
14
+ * stays droppable — at a fixed 40px a 100px-wide panel would be all edge and
15
+ * have no centre to drop a tab into. `edgeFraction` is capped below 0.5 for the
16
+ * same reason: at 0.5 the centre vanishes entirely.
17
+ *
18
+ * Corners go to whichever edge the pointer is proportionally deeper into, so
19
+ * the diagonal is the tie-break and neither axis silently wins. **This is a
20
+ * deliberate divergence from dockview**, whose `calculateQuadrantAsPercentage`
21
+ * tests left, right, top, bottom in that order and returns the first hit — so
22
+ * in its top-left corner `left` always wins, however far into the top band the
23
+ * pointer is. Its band is 20% where this defaults to 25%.
24
+ */
25
+ export function dropZoneAt(rect, x, y, edgeFraction = 0.25) {
26
+ const fraction = Math.min(Math.max(edgeFraction, 0), 0.49);
27
+ if (rect.width <= 0 || rect.height <= 0) {
28
+ return 'center';
29
+ }
30
+ // 0 at the left/top edge, 1 at the right/bottom
31
+ const px = (x - rect.left) / rect.width;
32
+ const py = (y - rect.top) / rect.height;
33
+ // how far into an edge band the pointer is, as a share of that band
34
+ const depths = [
35
+ { zone: 'left', depth: (fraction - px) / fraction },
36
+ { zone: 'right', depth: (px - (1 - fraction)) / fraction },
37
+ { zone: 'top', depth: (fraction - py) / fraction },
38
+ { zone: 'bottom', depth: (py - (1 - fraction)) / fraction },
39
+ ].filter(candidate => candidate.depth > 0);
40
+ if (depths.length === 0) {
41
+ return 'center';
42
+ }
43
+ return depths.reduce((best, candidate) => candidate.depth > best.depth ? candidate : best).zone;
44
+ }
45
+ /**
46
+ * Which gap between tabs a pointer at `x` is nearest — the insertion index a
47
+ * drop there would use, and the x to draw the caret at.
48
+ *
49
+ * The test is each tab's MIDPOINT rather than its edges, so every x belongs to
50
+ * exactly one gap and there is no dead band between tabs where a drop would
51
+ * have to fall back to appending. `rects` are in strip order and `x` is in
52
+ * their coordinate space, whatever that is — this function does not know
53
+ * whether it was handed viewport or panel-relative numbers.
54
+ *
55
+ * An empty strip is index 0, which is the only sensible place for a first tab.
56
+ */
57
+ export function stripDropAt(rects, x) {
58
+ for (const [i, rect] of rects.entries()) {
59
+ if (x < rect.left + rect.width / 2) {
60
+ return { index: i, left: rect.left };
61
+ }
62
+ }
63
+ const last = rects.at(-1);
64
+ return {
65
+ index: rects.length,
66
+ left: last ? last.left + last.width : 0,
67
+ };
68
+ }
69
+ /**
70
+ * What each zone means, in one table.
71
+ *
72
+ * `split` is the split a drop there asks for — which way the panel divides and
73
+ * whether the new half goes first; `center` is not a split. `rect` is where the
74
+ * indicator is drawn, as CSS percentages of the panel.
75
+ *
76
+ * One table rather than two switches because the pair has to agree: the shaded
77
+ * half IS the half the drop lands in, and that is the whole content of the
78
+ * indicator. Written apart, `right` could shade the left half and nothing would
79
+ * be wrong with either function on its own.
80
+ */
81
+ const ZONES = {
82
+ left: {
83
+ split: { direction: 'row', before: true },
84
+ rect: { left: '0%', top: '0%', width: '50%', height: '100%' },
85
+ },
86
+ right: {
87
+ split: { direction: 'row', before: false },
88
+ rect: { left: '50%', top: '0%', width: '50%', height: '100%' },
89
+ },
90
+ top: {
91
+ split: { direction: 'column', before: true },
92
+ rect: { left: '0%', top: '0%', width: '100%', height: '50%' },
93
+ },
94
+ bottom: {
95
+ split: { direction: 'column', before: false },
96
+ rect: { left: '0%', top: '50%', width: '100%', height: '50%' },
97
+ },
98
+ center: {
99
+ split: undefined,
100
+ rect: { left: '0%', top: '0%', width: '100%', height: '100%' },
101
+ },
102
+ };
103
+ /**
104
+ * The split a drop on `zone` asks for: which way the panel divides, and whether
105
+ * the new half goes first. `center` is not a split.
106
+ */
107
+ export function splitForZone(zone) {
108
+ return ZONES[zone].split;
109
+ }
110
+ /** Where the drop indicator goes, as CSS percentages of the panel. */
111
+ export function indicatorRect(zone) {
112
+ return ZONES[zone].rect;
113
+ }
@@ -0,0 +1,8 @@
1
+ export { WorkspaceLayoutMixin, isSessionWithWorkspaceLayout } from './model.ts';
2
+ export { WorkspaceContainer } from './WorkspaceContainer.tsx';
3
+ export { LayoutRenderer } from './LayoutRenderer.tsx';
4
+ export { useLayoutDrag } from './useLayoutDrag.ts';
5
+ export type { WorkspaceLayoutMixinType, WorkspaceLayout } from './model.ts';
6
+ export type { LayoutTree, PanelNode, TabNode, BranchNode, TabHome, } from './tree.ts';
7
+ export type { LayoutSpecNode, PendingMove, TileMode } from './spec.ts';
8
+ export type { DropZone } from './dropZone.ts';
@@ -0,0 +1,4 @@
1
+ export { WorkspaceLayoutMixin, isSessionWithWorkspaceLayout } from './model.js';
2
+ export { WorkspaceContainer } from './WorkspaceContainer.js';
3
+ export { LayoutRenderer } from './LayoutRenderer.js';
4
+ export { useLayoutDrag } from './useLayoutDrag.js';