-
Notifications
You must be signed in to change notification settings - Fork 1
#vfb-229 - POC: add Neuroglass viewer component and associated state management; integrate into toolbar and layout. #214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
1f4f2e7
33cf519
d9b1b33
e86ff68
fca42a4
da5d8f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import React, { useState, useEffect, useMemo } from 'react'; | ||
| import { useSelector } from 'react-redux'; | ||
| import { Box, Typography } from '@mui/material'; | ||
| import { getNeuroglassState, hasNeuroglassState } from '../utils/neuroglassStateConfig'; | ||
|
|
||
| const NEUROGLASS_URL = import.meta.env.VITE_NEUROGLASS_URL || 'https://www.research.neuroglass.dev.metacell.us'; | ||
|
|
||
| export default function NeuroglassViewer() { | ||
| const [iframeSrc, setIframeSrc] = useState(''); | ||
|
|
||
| const focusedInstance = useSelector(state => state.instances?.focusedInstance); | ||
|
|
||
| const iframeSrcUrl = useMemo(() => { | ||
| if (!focusedInstance?.metadata?.Id) return ''; | ||
|
|
||
| // Priority 1: Predefined state for focused instance (if available) | ||
| let stateToUse = null; | ||
| if (hasNeuroglassState(focusedInstance.metadata.Id)) { | ||
| stateToUse = getNeuroglassState(focusedInstance.metadata.Id); | ||
| } | ||
| // Priority 2: Default to template | ||
| else { | ||
| stateToUse = getNeuroglassState('VFB_00101567'); | ||
| } | ||
|
|
||
| if (!stateToUse) return ''; | ||
|
|
||
| const stateStr = JSON.stringify(stateToUse); | ||
| const encodedState = encodeURIComponent(stateStr); | ||
| return `${NEUROGLASS_URL}/new#!${encodedState}`; | ||
| }, [focusedInstance?.metadata?.Id]); | ||
|
|
||
| useEffect(() => { | ||
| if (iframeSrcUrl) { | ||
| setIframeSrc(iframeSrcUrl); | ||
| } | ||
| }, [iframeSrcUrl]); | ||
jrmartin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return ( | ||
| <Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}> | ||
| {iframeSrc ? ( | ||
| <Box sx={{ flex: 1, border: '1px solid #ccc', borderRadius: 1, overflow: 'hidden' }}> | ||
| <iframe | ||
| src={iframeSrc} | ||
| style={{ | ||
| width: '100%', | ||
| height: '100%', | ||
| border: 'none', | ||
| backgroundColor: '#000', | ||
| }} | ||
| title="Neuroglass Viewer" | ||
| sandbox="allow-scripts" | ||
| /> | ||
| </Box> | ||
| ) : ( | ||
| <Box sx={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: '#fafafa' }}> | ||
| <Typography color="textSecondary">Loading Neuroglass viewer...</Typography> | ||
| </Box> | ||
| )} | ||
| </Box> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -242,6 +242,14 @@ export const toolbarMenu = (autoSaveLayout) => { return { | |
| parameters: [widgets?.stackViewerWidget?.id] | ||
| } | ||
| }, | ||
| { | ||
| label: "Neuroglass Viewer", | ||
| icon: "fa fa-brain", | ||
| action: { | ||
| handlerAction: ACTIONS.SHOW_WIDGET, | ||
| parameters: [widgets?.neuroglassViewerWidget?.id] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This menu item assumes Useful? React with 👍 / 👎. |
||
| } | ||
| }, | ||
| { | ||
| label: "Template ROI Browser", | ||
| icon: "fa fa-indent", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /** | ||
| * Neuroglass Widget Actions | ||
| * Controls visibility and state of the Neuroglass viewer widget | ||
| */ | ||
|
|
||
| import { setWidgetVisible, WidgetStatus } from '@metacell/geppetto-meta-client/common/layout/actions'; | ||
| import { widgetsIDs } from '../layout/widgets'; | ||
jrmartin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import { hasNeuroglassState, getNeuroglassState, NEUROGLASS_STATES_MAP } from './neuroglassStateConfig'; | ||
|
|
||
|
|
||
| /** | ||
| * Show the Neuroglass viewer widget | ||
| * @param {Object} store - Redux store instance | ||
| */ | ||
| export const showNeuroglassViewer = (store) => { | ||
| store.dispatch(setWidgetVisible(widgetsIDs.neuroglassViewerWidgetID, true)); | ||
| }; | ||
|
|
||
| /** | ||
| * Hide the Neuroglass viewer widget | ||
| * @param {Object} store - Redux store instance | ||
| */ | ||
| export const hideNeuroglassViewer = (store) => { | ||
| store.dispatch(setWidgetVisible(widgetsIDs.neuroglassViewerWidgetID, false)); | ||
| }; | ||
|
|
||
| /** | ||
| * Toggle Neuroglass viewer widget visibility | ||
| * @param {Object} store - Redux store instance | ||
| */ | ||
| export const toggleNeuroglassViewer = (store) => { | ||
| const state = store.getState(); | ||
| const widgets = state.widgets || {}; | ||
| const neuroglassWidget = widgets[widgetsIDs.neuroglassViewerWidgetID]; | ||
|
|
||
| const isVisible = neuroglassWidget?.status === WidgetStatus.ACTIVE; | ||
| store.dispatch(setWidgetVisible(widgetsIDs.neuroglassViewerWidgetID, !isVisible)); | ||
| }; | ||
|
|
||
| /** | ||
| * Check if an instance has Neuroglass data available | ||
| * @param {string} instanceId - VFB instance ID | ||
| * @returns {boolean} True if Neuroglass data exists | ||
| */ | ||
| export const hasNeuroglassData = (instanceId) => { | ||
| return hasNeuroglassState(instanceId); | ||
| }; | ||
|
|
||
| /** | ||
| * Get the Neuroglass viewer state for a specific instance | ||
| * @param {string} instanceId - VFB instance ID | ||
| * @returns {Object|null} Neuroglass state or null if not available | ||
| */ | ||
| export const getNeuroglassStateForInstance = (instanceId) => { | ||
| return getNeuroglassState(instanceId); | ||
| }; | ||
|
|
||
| /** | ||
| * Auto-show Neuroglass widget when a compatible instance is loaded | ||
| * @param {Object} store - Redux store instance | ||
| */ | ||
| export const autoShowNeuroglass = (store) => { | ||
| const state = store.getState(); | ||
| const loadedInstances = state.instances.allLoadedInstances || []; | ||
|
|
||
| const hasCompatibleInstance = loadedInstances.some( | ||
| instance => hasNeuroglassData(instance.metadata?.Id) | ||
| ); | ||
|
|
||
| if (hasCompatibleInstance) { | ||
| showNeuroglassViewer(store); | ||
| } | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The early return on missing focus ID (
return '') prevents the component from ever reaching the default-template fallback branch, so opening this widget before an instance is focused leavesiframeSrcempty and the panel stuck on the loading placeholder. That breaks the intended “focused instance or default template” behavior in startup/empty-focus states.Useful? React with 👍 / 👎.