Dashboard

Component Window Manager

Floating window system using WinBox.js, intercepting router.push to render iframe overlays

Raw
Revised:
2026-04-22
Revision:
2
Related Notes:
Component - Agents UX

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=true and hide_masthead=true to URL
  • Positions windows in staggered grid: x = counter * margin, y = (counter % layout) * margin
  • Creates iframe-focus-overlay div 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:

  • .winboxmargin-top: calc($masthead-height + 1px), border-radius, box-shadow
  • .winbox.max — Maximized: no margin/border/shadow
  • .winbox.min — Minimized: collapsed
  • .wb-headerbackground: $brand-primary
  • .iframe-focus-overlay — Absolute positioned overlay for click handling
  • .winbox.focus .iframe-focus-overlaypointer-events: none (lets focused window receive clicks)
  • .wb-fulldisplay: 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

FilePurpose
client/src/entry/analysis/window-manager.jsWindowManager class (WinBox wrapper)
client/src/entry/analysis/router-push.jsPatches router.push for window interception
client/src/entry/analysis/router-push.test.jsUnit tests for router interception
client/src/entry/analysis/App.vueMounts WindowManager, attaches to Galaxy.frame
client/src/components/History/Content/router-push-options.tsRouterPushOptions interface
client/src/components/History/Content/ContentItem.vueExample: dataset display with WM
client/src/style/scss/windows.scssWinBox CSS customization
client/src/composables/usePanels.tsPanel visibility via route query
client/src/utils/navigation/navigation.ymlSelector: masthead.window_manager

Adding Window Manager Support for History Notebooks

To integrate:

  1. Accept displayOnly prop in HistoryNotebookView.vue
  2. Hide edit UI when windowed — toolbar (save/back), editor; show read-only rendered content
  3. Router config — pass displayOnly from route.query.displayOnly === "true"
  4. Trigger from HistoryOptions — call router.push() with title and preventWindowManager: false
  5. 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.

Incoming References (1)

  • Component Agents Ux related note — Four agent UX surfaces: ChatGXY full-page chat, GalaxyWizard error widget, tool generator, Jupyternaut proxy