WorkspaceLayoutMixin
Auto-generated @jbrowse/mobx-state-tree API for the current JBrowse release — see pluggable elements for concepts. Built into JBrowse core. View source.
The whole workspace, in one MST tree. There is no second owner, so there is
nothing to reconcile, no event to echo, and no window during which the two
disagree — which is the entire content of useDockviewController.
Four levels, matching what the workspace actually has and what a generic window manager cannot quite express:
branch (a split) > panel (a grid cell) > tab > views (stacked)
dockview models the first three as branch/group/panel and stops there; the
vertical stack of views inside a tab is ours, which is why
panelViewAssignments had to exist alongside dockview's own serialized grid.
Here it is one tree, and a tab simply contains its views.
Every action is tree -> tree through the pure functions in tree.ts, so undo
is applySnapshot on this node and nothing else has to be told.
Properties
| Member | Description |
|---|---|
idid: types.identifier | |
viewIdsviewIds: types.array(types.string) | |
titletitle: types.maybe(types.string) | set only by an explicit rename; otherwise the name is derived from views |
idid: types.identifier | |
sizesize: types.optional(types.number, 1) | |
tabstabs: types.array(LayoutTab) | |
activeTabIdactiveTabId: types.maybe(types.string) | |
idid: types.identifier | |
sizesize: types.optional(types.number, 1) | |
| direction | |
| children | |
layoutlayout: types.optional(LayoutNode, emptyPanel) | |
activePanelIdactivePanelId: types.maybe(types.string) | |
maximizedPanelIdmaximizedPanelId: types.maybe(types.string) | Show only this cell, at the size of the whole workspace. Deliberately HERE and not a maximized flag on PanelNode. On the node it would be inside tree.ts, the half that carries the risk and is proven by a randomised operation sequence asserting canonical form after every step — and every operation would then have to say what it does to the flag: a split of a maximized panel, a drag of its last tab out, a normalize that collapses it into its parent. Beside activePanelId it is the same class of thing as activePanelId, including its failure mode, which livePanelIds already repairs. |
Getters
| Member | Description |
|---|---|
treeLayoutTree | The plain tree the pure functions take.getSnapshot is a keepAlive computed, so this is cached and referentially stable — which also lets MST's reconcile short-circuit on identity when apply writes an untouched subtree back.Uncast on purpose: the models below and the interfaces in tree.ts are two spellings of one shape, and this assignment is the only thing that checks they agree. |
panelsPanelNode[] | |
tabsTabNode[] | |
visibleTreeLayoutTree | What to render: the maximized cell alone, or the whole tree. Sized to 1 rather than handed over as it sits. A pane's size is its share of its siblings, and CSS distributes free space by grow factor only up to a total of 1 — so a cell that was a third of a row, alone in the workspace with flexGrow: 0.33, draws a third of the window and leaves the rest blank. |
Methods
| Member | Description |
|---|---|
hasPanel(panelId: string) => boolean | |
findTab(tabId: string) => TabHome | undefined | |
tabContainingView(viewId: string) => TabHome | undefined | |
panelContainingView(viewId: string) => PanelNode | undefined | |
viewIdsForTab(tabId: string, order: string[]) => string[] | The views a tab renders: its members, in session.views order. |
activeTabOf(panelId: string) => TabNode | undefined | The tab a panel is showing, or its first. |
Actions
| Member | Description |
|---|---|
setActivePanelId(panelId: string | undefined) => void | |
toggleMaximizedPanel(panelId: string) => void | Show one cell at the size of the workspace, or go back. A toggle rather than a pair, because the gesture is a toggle: the strip's double-click and the cell menu's one item both mean "this cell, or not any more". Maximizing a DIFFERENT cell while one is already maximized moves the mode rather than restoring, which is what the menu item on another cell's strip is asking for. Mounts no views that were not mounted — it is the same cell showing the same tab — and unmounts every other cell's, so the WebGL2 context ceiling ( agent-docs/reference/GPU_CONTEXT_BUDGET.md) can only go down. That is the reason it is this and not a display: none over a still-mounted workspace. |
restorePanels() => void | |
setActiveTab(panelId: string, tabId: string) => void | |
renameTab(tabId: string, title: string | undefined) => void | |
| splitPanel | Split a grid cell; the new cell gets one empty tab. |
closePanel(panelId: string) => void | |
addTab(panelId: string, viewIds?: string[]) => TabNode | undefined | "New empty tab": a tab in an existing cell, showing the launcher. |
closeTab(tabId: string) => void | Close a tab, and the cell with it if that was its last. A cell whose tabs are all gone is the state pruneEmptyPanel was written for — "dragging the last tab out of a split and leaving a blank half is the one place an empty panel is clearly not what was meant" — and closing that tab arrives at the identical half by a different gesture. It rendered nothing at all, not even the launcher an empty TAB shows, so the only way back out of it was the +.pruneEmptyPanel carries both guards already: a cell with tabs left stays, and the last cell in the workspace stays whatever happens to it, since there is nowhere for the tree to collapse to. |
addViewToTab(tabId: string, viewId: string) => void | |
| dropTabInPanel | Drop a dragged tab into an existing panel, as a tab. One action, so the tree never exists in a state where the tab is in both panels or neither. The imperative bridge needed an explicit runInAction around the unassign+reassign pair for exactly this, and a comment explaining that without it the reconcile autorun would observe the gap and re-home the view. |
| dropTabInNewSplit | Drop a dragged tab on a panel edge: split, and land in the new half. |
setSizes(branchId: string, sizes: number[]) => void | |
applyLayoutSpec(spec: LayoutSpecNode) => string[] | Arrange the workspace as a spec states. There is no init property and no standing request: the spec is converted and becomes the layout, here and now. init existed only because dockview had to be told, could not be told before it mounted, and had to be told again afterwards — three problems that all came from the layout living somewhere this action could not reach. |
moveViewToNewTab(viewId: string, allViewIds: string[]) => string | undefined | ViewMenu's "move to new tab": the view leaves its tab for a new one.allViewIds is EVERY view in the session, and is required for that reason — homing drops any view the list does not name, so the [viewId] default this used to carry unhomed all the others. |
moveViewToSplitRight(viewId: string, allViewIds: string[]) => string | undefined | ViewMenu's "move to split view": the view leaves for a new cell. allViewIds is every view in the session — see moveViewToNewTab. |
homeUnassignedViews(viewIds: string[]) => void | |
| setPendingMove | Move one view relative to the others. PUBLIC API: an external plugin calls this behind a 'setPendingMove' in session guard (jbrowse-plugin-protein3d, putting a protein view beside its genome view). It survived the last storage change by being kept as sugar, and it survives this one the same way — a capability-detecting caller cannot tell you it lost a capability.allViewIds is therefore OPTIONAL, and has to stay that way. The plugin passes the move alone, because that was the whole signature when its call site was written; requiring the second argument threw undefined.filter out of a launch the plugin does not wrap, and the figure was again the only thing that noticed. Keeping the NAME is half of not breaking a runtime lookup — the call has to keep working as it is spelled. |
tileViews(mode: TileMode, allViewIds: string[]) => void | The whole-workspace re-arrange: every view one cell, in one of four shapes. Restored from the dockview header's four "Global:" commands, which went with that component and were not reimplemented.allViewIds is passed in rather than read off the session for the same reason moveViewToNewTab takes it: this mixin owns the tree and has no view list of its own. Passing session.views order means the arrangement it states is already the order views render in, so unlike a session spec's layout there is nothing for orderViews to apply. |