@@ -37,6 +37,7 @@ type EdgeFilterKey = "dataFlow" | "mapped" | "callStack";
3737type EdgeFilters = Record < EdgeFilterKey , boolean > ;
3838type FocusMode = "none" | "upstream" | "downstream" | "neighborhood" ;
3939type SidePanel = "inspector" | "add_model" | "initializations" | "mapping_code" | null ;
40+ type GraphViewMode = "overview" | "detail" ;
4041
4142type PendingMappingConnection = {
4243 sourceNode : GraphNodeData ;
@@ -118,6 +119,7 @@ const layoutLabels: Record<LayoutMode, string> = {
118119 compact : "Compact" ,
119120 scale_grouped : "Scale grouped" ,
120121 call_stack : "Call stack" ,
122+ overview : "Overview" ,
121123} ;
122124
123125const valueTypeChoices = [ "float" , "integer" , "boolean" , "symbol" , "string" , "nothing" , "julia" ] ;
@@ -146,10 +148,14 @@ export default function App() {
146148 const [ showRequiredPanel , setShowRequiredPanel ] = useState ( false ) ;
147149 const [ showWarningsPanel , setShowWarningsPanel ] = useState ( false ) ;
148150 const [ showOpenPanel , setShowOpenPanel ] = useState ( false ) ;
151+ const [ showRelationshipsPanel , setShowRelationshipsPanel ] = useState ( false ) ;
152+ const [ showScalesPanel , setShowScalesPanel ] = useState ( false ) ;
149153 const [ showSearchResults , setShowSearchResults ] = useState ( false ) ;
150154 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
151155 const [ layoutMode , setLayoutMode ] = useState < LayoutMode > ( "data_flow" ) ;
152156 const [ focusMode , setFocusMode ] = useState < FocusMode > ( "neighborhood" ) ;
157+ const [ viewMode , setViewMode ] = useState < GraphViewMode > ( ( ) => defaultGraphViewMode ( loadInitialGraph ( ) ) ) ;
158+ const [ viewModeTouched , setViewModeTouched ] = useState ( false ) ;
153159 const [ edgeFilters , setEdgeFilters ] = useState < EdgeFilters > ( defaultEdgeFilters ) ;
154160 const [ collapsedScales , setCollapsedScales ] = useState < Set < string > > ( ( ) => new Set ( ) ) ;
155161 const [ pinnedFocus , setPinnedFocus ] = useState < FocusState | null > ( null ) ;
@@ -305,6 +311,10 @@ export default function App() {
305311 return ( ) => window . clearTimeout ( timeout ) ;
306312 } , [ activePanel , highlightAddModelPanel , addModelFocusRequest ] ) ;
307313
314+ useEffect ( ( ) => {
315+ if ( ! viewModeTouched ) setViewMode ( defaultGraphViewMode ( graph ) ) ;
316+ } , [ graph , viewModeTouched ] ) ;
317+
308318 useEffect ( ( ) => {
309319 const nextNodes = visibleNodeData . map ( ( node ) => ( {
310320 id : node . id ,
@@ -323,14 +333,15 @@ export default function App() {
323333 setActivePort,
324334 setCandidatePopover : toggleCandidatePopover ,
325335 removeGraphModel,
336+ viewMode,
326337 } ) ,
327338 } ) ) ;
328339 const nextEdges = visibleEdgeData . map ( ( edge ) => flowEdge ( edge , new Set < string > ( ) , new Set < string > ( ) , false , false ) ) ;
329- layoutGraph ( nextNodes , nextEdges , layoutMode ) . then ( ( layouted ) => {
340+ layoutGraph ( nextNodes , nextEdges , effectiveLayoutMode ( viewMode , layoutMode ) ) . then ( ( layouted ) => {
330341 setNodes ( layouted ) ;
331342 setEdges ( nextEdges ) ;
332343 } ) ;
333- } , [ activeCandidatePortId , candidatePortIds , graph . cycleNodes , layoutMode , removeGraphModel , requiredInputPortIds , setEdges , setNodes , toggleCandidatePopover , visibleEdgeData , visibleNodeData ] ) ;
344+ } , [ activeCandidatePortId , candidatePortIds , graph . cycleNodes , layoutMode , removeGraphModel , requiredInputPortIds , setEdges , setNodes , toggleCandidatePopover , viewMode , visibleEdgeData , visibleNodeData ] ) ;
334345
335346 useEffect ( ( ) => {
336347 const focusEdges = focus . active ? focus . edges : new Set < string > ( ) ;
@@ -349,10 +360,11 @@ export default function App() {
349360 setActivePort,
350361 setCandidatePopover : toggleCandidatePopover ,
351362 removeGraphModel,
363+ viewMode,
352364 } ) ,
353365 } ) ) ) ;
354366 setEdges ( ( current ) => current . map ( ( edge ) => edge . data ? flowEdge ( edge . data , hoverHighlight . edges , focusEdges , Boolean ( activePort ) , focus . active ) : edge ) ) ;
355- } , [ activeCandidatePortId , activePort , candidatePortIds , focus , graph . cycleNodes , hoverHighlight . edges , hoverHighlight . ports , removeGraphModel , requiredInputPortIds , setEdges , setNodes , toggleCandidatePopover ] ) ;
367+ } , [ activeCandidatePortId , activePort , candidatePortIds , focus , graph . cycleNodes , hoverHighlight . edges , hoverHighlight . ports , removeGraphModel , requiredInputPortIds , setEdges , setNodes , toggleCandidatePopover , viewMode ] ) ;
356368
357369 useEffect ( ( ) => {
358370 if ( candidatePopover && ! candidatePortIds . has ( candidatePopover . portId ) ) setCandidatePopover ( null ) ;
@@ -377,8 +389,8 @@ export default function App() {
377389 } , [ editorConnected , portById ] ) ;
378390
379391 const relayout = useCallback ( ( ) => {
380- layoutGraph ( nodes , edges , layoutMode ) . then ( setNodes ) ;
381- } , [ edges , layoutMode , nodes , setNodes ] ) ;
392+ layoutGraph ( nodes , edges , effectiveLayoutMode ( viewMode , layoutMode ) ) . then ( setNodes ) ;
393+ } , [ edges , layoutMode , nodes , setNodes , viewMode ] ) ;
382394
383395 const focusNode = useCallback ( ( node : GraphNodeData , port ?: GraphPort | null ) => {
384396 setPinnedFocus ( null ) ;
@@ -474,7 +486,7 @@ export default function App() {
474486 } , [ flowInstance , focusEdge , focusNode , graph . edges , nodeById , nodes , portById ] ) ;
475487
476488 return (
477- < main className = { `app-shell ${ candidatePopover ? "has-candidate-popover" : "" } ` } >
489+ < main className = { `app-shell ${ viewMode === "overview" ? "overview-mode" : "detail-mode" } ${ candidatePopover ? "has-candidate-popover" : "" } ` } >
478490 < section className = "graph-panel" >
479491 < div className = "topbar graph-workbench" >
480492 < button
@@ -552,10 +564,22 @@ export default function App() {
552564 </ div >
553565
554566 < div className = "toolbar-group" >
567+ < button
568+ className = { `metric-button view-mode-button ${ viewMode === "overview" ? "active overview-cta" : "" } ` }
569+ onClick = { ( ) => {
570+ setViewModeTouched ( true ) ;
571+ setViewMode ( ( mode ) => mode === "overview" ? "detail" : "overview" ) ;
572+ } }
573+ title = { viewMode === "overview" ? "Show full model inputs and outputs" : "Show compact cards for large graphs" }
574+ >
575+ { viewMode === "overview" ? "Overview Mode - Show Detailed View" : "Show Overview" }
576+ </ button >
555577 < label className = "select-control" title = "Choose how the graph should be arranged" >
556578 < Network size = { 14 } />
557579 < select value = { layoutMode } onChange = { ( event ) => setLayoutMode ( event . target . value as LayoutMode ) } >
558- { ( Object . keys ( layoutLabels ) as LayoutMode [ ] ) . map ( ( mode ) => < option key = { mode } value = { mode } > { layoutLabels [ mode ] } </ option > ) }
580+ { ( Object . keys ( layoutLabels ) as LayoutMode [ ] )
581+ . filter ( ( mode ) => mode !== "overview" )
582+ . map ( ( mode ) => < option key = { mode } value = { mode } > { layoutLabels [ mode ] } </ option > ) }
559583 </ select >
560584 </ label >
561585 < label className = "select-control" title = "Dim graph context around the current selection" >
@@ -569,11 +593,32 @@ export default function App() {
569593 </ button >
570594 </ div >
571595
596+ < div className = "toolbar-group graph-filter-buttons" >
597+ < button
598+ className = { `metric-button ${ showRelationshipsPanel ? "active" : "" } ` }
599+ onClick = { ( ) => setShowRelationshipsPanel ( ( open ) => ! open ) }
600+ title = "Show relationship filters"
601+ >
602+ < Filter size = { 14 } /> Relationships
603+ </ button >
604+ < button
605+ className = { `metric-button ${ showScalesPanel ? "active" : "" } ` }
606+ onClick = { ( ) => setShowScalesPanel ( ( open ) => ! open ) }
607+ title = "Show scale visibility controls"
608+ >
609+ < Network size = { 14 } /> Scales { collapsedScales . size > 0 ? `${ graph . scales . length - collapsedScales . size } /${ graph . scales . length } ` : graph . scales . length }
610+ </ button >
611+ </ div >
612+
572613 < div className = "toolbar-group panel-switch" >
573614 < button className = { `metric-button ${ activePanel === "inspector" ? "active" : "" } ` } onClick = { ( ) => togglePanel ( "inspector" ) } > Inspector</ button >
574- < button className = { `metric-button ${ activePanel === "add_model" ? "active" : "" } ` } onClick = { openAddModelPanel } > Add model</ button >
575- < button className = { `metric-button ${ activePanel === "initializations" ? "active" : "" } ` } onClick = { ( ) => togglePanel ( "initializations" ) } > Initializations</ button >
576- < button className = { `metric-button ${ activePanel === "mapping_code" ? "active" : "" } ` } onClick = { ( ) => togglePanel ( "mapping_code" ) } > Mapping code</ button >
615+ { editorSocket && (
616+ < >
617+ < button className = { `metric-button ${ activePanel === "add_model" ? "active" : "" } ` } onClick = { openAddModelPanel } > Add model</ button >
618+ < button className = { `metric-button ${ activePanel === "initializations" ? "active" : "" } ` } onClick = { ( ) => togglePanel ( "initializations" ) } > Initializations</ button >
619+ < button className = { `metric-button ${ activePanel === "mapping_code" ? "active" : "" } ` } onClick = { ( ) => togglePanel ( "mapping_code" ) } > Mapping code</ button >
620+ </ >
621+ ) }
577622 </ div >
578623
579624 { editorSocket && (
@@ -591,8 +636,8 @@ export default function App() {
591636 </ div >
592637 ) }
593638
594- < RelationshipLegend filters = { edgeFilters } onToggle = { toggleEdgeFilter } />
595- < ScaleControls scales = { graph . scales } collapsedScales = { collapsedScales } onToggle = { toggleScale } onExpandAll = { expandAllScales } />
639+ { showRelationshipsPanel && < RelationshipLegend filters = { edgeFilters } onToggle = { toggleEdgeFilter } /> }
640+ { showScalesPanel && < ScaleControls scales = { graph . scales } collapsedScales = { collapsedScales } onToggle = { toggleScale } onExpandAll = { expandAllScales } /> }
596641
597642 { showRequiredPanel && (
598643 < FloatingPanel className = "required-panel" title = "Required Initializations" subtitle = { `${ requiredInputs . length } inputs` } onClose = { ( ) => setShowRequiredPanel ( false ) } >
@@ -631,6 +676,8 @@ export default function App() {
631676 setShowSearchResults ( false ) ;
632677 setCandidatePopover ( null ) ;
633678 setShowOpenPanel ( false ) ;
679+ setShowRelationshipsPanel ( false ) ;
680+ setShowScalesPanel ( false ) ;
634681 } }
635682 onEdgeClick = { ( _ , edge ) => {
636683 if ( edge . data ) {
@@ -647,7 +694,7 @@ export default function App() {
647694 setSelected ( node . data ) ;
648695 } }
649696 fitView
650- fitViewOptions = { { padding : 0. 08, minZoom : 0.03 , maxZoom : 1 } }
697+ fitViewOptions = { { padding : viewMode === "overview" ? 0.14 : 0. 08, minZoom : 0.03 , maxZoom : viewMode === "overview" ? 1.25 : 1 } }
651698 minZoom = { 0.03 }
652699 maxZoom = { 2 }
653700 >
@@ -1938,6 +1985,14 @@ function loadInitialGraph() {
19381985 return fromWindow ?? sampleGraph ;
19391986}
19401987
1988+ function defaultGraphViewMode ( graph : DependencyGraphView ) : GraphViewMode {
1989+ return graph . nodes . length > 45 || graph . edges . length > 110 ? "overview" : "detail" ;
1990+ }
1991+
1992+ function effectiveLayoutMode ( viewMode : GraphViewMode , layoutMode : LayoutMode ) : LayoutMode {
1993+ return viewMode === "overview" ? "overview" : layoutMode === "overview" ? "data_flow" : layoutMode ;
1994+ }
1995+
19411996function loadEditorConfig ( ) {
19421997 const embedded = document . getElementById ( "pse-editor-config" ) ;
19431998 if ( ! embedded ?. textContent ) return null ;
@@ -1959,10 +2014,12 @@ function runtimeNodeData(
19592014 setActivePort : ( port : GraphPort | null ) => void ;
19602015 setCandidatePopover : ( port : GraphPort , anchor : { x : number ; y : number } ) => void ;
19612016 removeGraphModel : ( node : GraphNodeData ) => void ;
2017+ viewMode : GraphViewMode ;
19622018 } ,
19632019) : RuntimeGraphNodeData {
19642020 return {
19652021 ...node ,
2022+ viewMode : options . viewMode ,
19662023 cyclic : options . cycleNodeIds . has ( node . id ) ,
19672024 activePortId : options . activePort ?. id ?? null ,
19682025 highlightedPortIds : [ ...options . highlightedPortIds ] ,
0 commit comments