Galaxy Window Manager System - Research Report
Overview
Galaxy’s window manager allows opening multiple views in floating windows within the SPA, using WinBox.js v0.2.82. The system operates as a layer above Vue Router — when enabled, routes with titles render in floating windows instead of navigating, so users can view multiple content streams without losing context.
Key insight: The window manager is an opt-in toggle. When inactive, all navigation is standard Vue Router. When active, router.push() calls with a title option get intercepted and opened in WinBox floating windows (iframes) instead.
Core API: WindowManager Class
File: client/src/entry/analysis/window-manager.js
State
counter: Tracks number of open windows (used for staggering positions)active: Boolean toggle (enabled/disabled)zIndexInitialized: Ensures first window gets z-index 850 (below masthead at 900)
Public Methods
getTab() — Returns a masthead tab config for the toggle button:
{
id: "enable-window-manager",
icon: faTh,
tooltip: "Enable/Disable Window Manager",
visible: true,
onclick: () => { this.active = !this.active; }
}
add(options, layout = 10, margin = 20) — Creates a floating window:
add({ title: "1: sample.fasta", url: "/datasets/123" })
- Appends
hide_panels=trueandhide_masthead=trueto URL - Positions windows in staggered grid:
x = counter * margin,y = (counter % layout) * margin - Creates
iframe-focus-overlaydiv for click handling on unfocused windows - Calls
WinBox.new({ title, url, x, y, index, onclose })
beforeUnload() — Returns true if windows are open (for “leave page?” prompt)
Router Push Integration
File: client/src/entry/analysis/router-push.js
Monkey-patches VueRouter.prototype.push to intercept navigation:
VueRouter.prototype.push = function push(location, options = {}) {
const { title, force, preventWindowManager } = options;
const Galaxy = getGalaxyInstance();
if (title && !preventWindowManager && Galaxy.frame && Galaxy.frame.active) {
Galaxy.frame.add({ title, url: location });
return; // Don't route — open window instead
}
return originalPush.call(this, location).catch(...);
};
RouterPushOptions Interface
File: client/src/components/History/Content/router-push-options.ts
export interface RouterPushOptions {
title?: string; // Window title (required to open in window)
force?: boolean; // Force reload via __vkey__ timestamp
preventWindowManager?: boolean; // Bypass WM even if active
}
Decision logic: If Galaxy.frame.active AND title provided AND preventWindowManager not set → open window. Otherwise → normal route.
App.vue Integration
File: client/src/entry/analysis/App.vue
created() {
if (!this.embedded) {
this.windowManager = new WindowManager();
window.onbeforeunload = () => {
if (this.confirmation || this.windowManager.beforeUnload()) {
return "Are you sure you want to leave the page?";
}
};
}
}
mounted() {
if (!this.embedded) {
this.Galaxy = getGalaxyInstance();
this.Galaxy.frame = this.windowManager; // Global access point
}
}
Passes windowManager.getTab() to Masthead as windowTab prop.
Component Usage Patterns
Pattern 1: ContentItem.vue (History Item Display)
File: client/src/components/History/Content/ContentItem.vue
The canonical example of window manager integration:
function onDisplay() {
const Galaxy = getGalaxyInstance();
const isWindowManagerActive = Galaxy.frame && Galaxy.frame.active;
let displayUrl = itemUrls.value.display;
// Add displayOnly param when windowed
if (isWindowManagerActive && displayUrl) {
displayUrl += displayUrl.includes("?") ? "&displayOnly=true" : "?displayOnly=true";
}
const hidInfo = props.item.hid ? `${props.item.hid}: ` : "";
const options = {
force: true,
preventWindowManager: !isWindowManagerActive,
title: isWindowManagerActive ? `${hidInfo} ${props.name}` : undefined
};
router.push(displayUrl, options);
}
Pattern 2: displayOnly Prop
Components conditionally hide UI when windowed:
<!-- DatasetView.vue -->
<header v-if="!displayOnly"> ... </header>
<BNav v-if="!displayOnly" pills> ... </BNav>
<div class="content"> ... </div>
Router passes displayOnly from query param:
{
props: (route) => ({
displayOnly: route.query.displayOnly === "true"
})
}
Pattern 3: Panel Hiding
File: client/src/composables/usePanels.ts
Windows automatically hide panels via hide_panels=true query param appended by _build_url().
CSS/Layout
File: client/src/style/scss/windows.scss
Key styles:
.winbox—margin-top: calc($masthead-height + 1px), border-radius, box-shadow.winbox.max— Maximized: no margin/border/shadow.winbox.min— Minimized: collapsed.wb-header—background: $brand-primary.iframe-focus-overlay— Absolute positioned overlay for click handling.winbox.focus .iframe-focus-overlay—pointer-events: none(lets focused window receive clicks).wb-full—display: none !important(Galaxy hides WinBox fullscreen button)
Call Flow: Opening Content in a Window
1. Component calls router.push(url, { title: "...", preventWindowManager: false })
↓
2. router-push.js interceptor checks: title && !preventWindowManager && Galaxy.frame.active
↓ (if true)
3. Galaxy.frame.add({ title, url })
├─ Build URL: append hide_panels=true, hide_masthead=true
├─ Calculate position: staggered grid
├─ WinBox.new() creates floating iframe window
└─ counter++
↓
4. Iframe loads URL with displayOnly=true
├─ Component mounts with displayOnly prop
├─ Edit UI hidden (v-if="!displayOnly")
└─ Content displayed in read-only mode
Key Files Reference
| File | Purpose |
|---|---|
client/src/entry/analysis/window-manager.js | WindowManager class (WinBox wrapper) |
client/src/entry/analysis/router-push.js | Patches router.push for window interception |
client/src/entry/analysis/router-push.test.js | Unit tests for router interception |
client/src/entry/analysis/App.vue | Mounts WindowManager, attaches to Galaxy.frame |
client/src/components/History/Content/router-push-options.ts | RouterPushOptions interface |
client/src/components/History/Content/ContentItem.vue | Example: dataset display with WM |
client/src/style/scss/windows.scss | WinBox CSS customization |
client/src/composables/usePanels.ts | Panel visibility via route query |
client/src/utils/navigation/navigation.yml | Selector: masthead.window_manager |
Adding Window Manager Support for History Notebooks
To integrate:
- Accept
displayOnlyprop inHistoryNotebookView.vue - Hide edit UI when windowed — toolbar (save/back), editor; show read-only rendered content
- Router config — pass
displayOnlyfromroute.query.displayOnly === "true" - Trigger from HistoryOptions — call
router.push()withtitleandpreventWindowManager: false - Notebook picker — if history has multiple notebooks, need to choose which one (or open most recent)
Important: The window manager loads content in an iframe via URL. The component must be fully functional when loaded standalone at its URL with ?hide_panels=true&hide_masthead=true&displayOnly=true.