Skip to content

Add persistent docked ChatGXY panel with context awareness#22096

Draft
dannon wants to merge 26 commits intogalaxyproject:devfrom
dannon:chatgxy-panel
Draft

Add persistent docked ChatGXY panel with context awareness#22096
dannon wants to merge 26 commits intogalaxyproject:devfrom
dannon:chatgxy-panel

Conversation

@dannon
Copy link
Copy Markdown
Member

@dannon dannon commented Mar 13, 2026

ChatGXY currently only works as a full-page view. This adds the ability to dock it as a persistent side or bottom panel so you can chat with the assistant while continuing to work in Galaxy.

The more interesting piece is context attachment -- a new composable watches the current route and derives what the user is looking at (which tool form is open, which dataset or workflow they're viewing, which job they're inspecting). When ChatGXY is docked, this context is automatically passed to the backend agent, so the assistant understands your current Galaxy interface state without you having to explain it. The backend formats this into natural language and prepends it to the query, giving the router agent situational awareness of what you're doing.

The panel location (center/right/bottom) and visibility are persisted per-user via the existing useUserLocalStorage pattern, so the preference survives page reloads and sessions. The activity bar item adapts its behavior based on mode -- navigating to the full-page view when in center mode, or toggling the docked panel when docked.

@dannon dannon force-pushed the chatgxy-panel branch 2 times, most recently from 28cde8e to 5afeef8 Compare March 17, 2026 14:39
@dannon dannon moved this to In Progress in GCC Live Planning Board Mar 25, 2026
@dannon dannon changed the title WIP: Add persistent docked ChatGXY panel with unified state Add persistent docked ChatGXY panel with context awareness Mar 26, 2026
@dannon dannon marked this pull request as ready for review March 26, 2026 18:32
@github-actions github-actions Bot added this to the 26.1 milestone Mar 26, 2026
@mvdbeek mvdbeek marked this pull request as draft March 31, 2026 14:56
@mvdbeek mvdbeek moved this from Needs Review to In Progress in Galaxy Dev - weeklies Apr 7, 2026
dannon and others added 9 commits April 29, 2026 12:45
Third modality for ChatGXY alongside center view and scratchbook: a
resizable right-side column that pushes center content rather than
overlaying it. New chatStore tracks docked panel state, FlexPanel gets
a panelId prop to avoid duplicate element IDs, and ChatGXY gains a
docked mode with its own slim header. Dock/undock from center view
header, close from the panel, or open from the left ChatHistoryPanel.
Center /chatgxy route and docked panel are mutually exclusive.
New useActiveContext composable watches the Vue route and detects what
the user is working with (tool form, dataset, workflow editor, etc.)
and surfaces it as a context chip in the docked panel header. The
structured context is JSON-serialized into the existing context string
field and sent with each chat message. On the backend, the API handler
parses JSON context and passes it through to agents, where it gets
formatted as natural language in the prompt so the LLM knows what the
user is looking at without them having to say it.
Activity bar click now navigates to the center view instead of toggling
the bottom panel. Sidebar history items also navigate to /chatgxy/{id}.
The bottom panel remains available as a dock-down target.
Remove onPanelClicked wrapper in ActivityBar (was just forwarding to
toggleSidebar). Consolidate two route watchers in Analysis.vue into one.
Move useChatStore() calls to setup time instead of inline in function
bodies.
Rewrites chatStore as the single source of truth for all chat panel
state. Previously the chat location (center/right/bottom) was managed
across three separate stores and mechanisms. Now chatStore owns
chatLocation (persisted), chatVisible (persisted), and activeChatId,
with computed properties for each panel position. All consumers
(ActivityBar, Analysis, ChatGXY, ChatPanel, ChatHistoryPanel) now go
through the unified API, and the chat fields have been removed from
activityStore.
- openDockedChat: set location to "right" when in center mode so
  the dock button actually does something visible
- onChatGxyClick: only toggle the left sidebar in center mode to
  avoid showing two chat surfaces simultaneously in docked mode
- dockToSide/dockToBottomPanel: only navigate to "/" when currently
  on /chatgxy, preserving the user's current route otherwise
Covers all route-matching branches in useActiveContext (tool, dataset,
workflow editor/run, job) including the upload1 exclusion and tool name
resolution. chatStore tests cover visibility toggling, location changes,
and the computed panel-state predicates.
dannon added 4 commits April 29, 2026 21:53
Don't persist chatVisible to localStorage -- the panel location is a
preference worth keeping, but visibility is session state that shouldn't
survive page reloads. Fix confirmDialogRef to mutate .value instead of
reassigning the ref variable. Restore canPurgeSelection guard so the
purge menu item is hidden when all selected items are already purged.
Fix selectedDbKey reset to include the text property, and clear
selectedTags when tag modals are dismissed. Light up the activity bar
icon when the docked chat panel is open. Sanitize user-supplied context
values in _format_interface_context to strip newlines and cap lengths
before they hit the LLM prompt. Trim confirmDialog.ts down to the
fields actually used by callers and remove verbose JSDoc.
Users can now type @ in the chat input to reference specific datasets
(by HID) or histories from the current session. A floating dropdown
with keyboard navigation helps pick entities, mentions render as
styled chips in message history, and resolved entity metadata gets
passed through to the agent so it can ground its answers on the
specific data the user is asking about.
Deduplicate the mention regex (single source of truth in
useEntityMentions), convert getMenuItems to a computed, hoist store
access to setup scope, collapse redundant key-check branches in
ChatInput, and drop WHAT-not-WHY comments. Fix ChatInput tests for
the hoisted store access.
dannon added 13 commits April 29, 2026 21:53
These fields are declared on the Pydantic model and the TS interface but
never populated or read -- nothing sets them in resolveMentions and
nothing reads them downstream. Remove the noise.
galaxy.util.json.safe_loads already handles the try/except-and-fall-back
pattern -- returns the parsed dict on success, the original string on
failure. Swap it in instead of reimplementing the try/except inline.
Rendering user-authored content through v-html with a hand-rolled
escapeHtml is a fragile pattern -- one forgotten escape step and it's
an XSS hole. The FontAwesome icons were also hardcoded as raw unicode
codepoints that would silently break if the FA version changed.

Parse the message into segments once, render text and mention chips in
the template, and use the FontAwesomeIcon component for the icons.
dockToSide and dockToBottomPanel were identical except for the location
literal. Fold them into one function and pass the target from the
template button handlers.
The separate visible watcher installed autoUpdate, and the combined
watcher ran updatePosition() directly -- so opening the dropdown
computed the position twice (once eagerly, once via autoUpdate's
initial callback). Collapse into one watcher that just installs and
tears down autoUpdate; its initial callback covers the first layout.
chatLocation and chatVisible are persisted through useUserLocalStorage,
so every assignment writes to localStorage and notifies watchers. Guard
each setter against same-value writes so repeated calls (e.g. hideChat
on every /chatgxy route hit, setActiveChatId on every selection change)
don't thrash downstream consumers.
…context

mypy complained about the variable being re-typed: the tool branch
assigns tool_id = _s(...) (str), and the job branch was reassigning
tool_id = ctx.get("toolId") (Any | None) in the same function scope.
Give the job variant a distinct name -- the two mean different things
anyway (tool being viewed vs. tool that produced the job).
Covers _format_interface_context (tool/dataset/etc routes, sanitization,
truncation, unknown types) and _format_entity_context (datasets and
histories from @mentions). The three sanitization tests for
_format_entity_context fail intentionally and demonstrate a prompt
injection vector -- next commit fixes that.
_format_interface_context already runs every user-supplied field through
_sanitize_context_value (strips newlines, caps length); _format_entity_context
was interpolating dataset/history names, extension, and state verbatim.
A renamed dataset like "Mapped reads\n\nNew system instruction:..." would
inject directly into the LLM prompt. Apply the same sanitizer here.
Adds coverage for the previously untested resolveMentions code path:
dataset by hid, dataset by name fragment, unresolved hid fallback,
@history:current, history by id, and missing history. Mocks the two
Pinia stores via vi.mock so the test stays focused on the resolution
logic rather than store internals.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress
Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants