@@ -14,6 +14,7 @@ import { createRuntimeMonitoringHooks } from "../../src/engine/runtime/index.js"
1414let workspaceController = null ;
1515let headerExpandedState = null ;
1616let runtimeMonitoringHooks = null ;
17+ let bindingRefreshHandlersBound = false ;
1718
1819const HEADER_EXPANDED_STORAGE_KEY = "toolboxaid.toolsPlatform.headerExpanded" ;
1920const HEADER_EXPANDED_FALLBACK_TOOL = "tool-host" ;
@@ -74,6 +75,62 @@ function getManifest() {
7475 return workspaceController ? workspaceController . getManifest ( ) : null ;
7576}
7677
78+ function formatBindingValue ( label , value , fallback = "none" ) {
79+ const safe = String ( value || "" ) . trim ( ) ;
80+ return `${ label } : ${ safe || fallback } ` ;
81+ }
82+
83+ function resolveProjectBindingLabel ( ) {
84+ const manifest = getManifest ( ) ;
85+ if ( ! manifest ) {
86+ return "Workspace: none" ;
87+ }
88+ return manifest . dirty === true ? "Workspace: Unsaved" : "Workspace: Loaded" ;
89+ }
90+
91+ function getToolBindingCompatibility ( toolId ) {
92+ const paletteCapable = new Set ( [
93+ "palette-browser" ,
94+ "sprite-editor" ,
95+ "vector-asset-studio"
96+ ] ) ;
97+ const assetCapable = new Set ( [
98+ "asset-browser" ,
99+ "sprite-editor"
100+ ] ) ;
101+ return {
102+ palette : paletteCapable . has ( toolId ) ,
103+ asset : assetCapable . has ( toolId )
104+ } ;
105+ }
106+
107+ function renderToolBindingBadges ( tool ) {
108+ const palette = readSharedPaletteHandoff ( ) ;
109+ const asset = readSharedAssetHandoff ( ) ;
110+ const compatibility = getToolBindingCompatibility ( tool . id ) ;
111+ const paletteLabel = compatibility . palette
112+ ? formatBindingValue ( "Palette" , palette ?. displayName , "none" )
113+ : "Palette: Not used" ;
114+ const assetLabel = compatibility . asset
115+ ? formatBindingValue ( "Asset" , asset ?. displayName , "none" )
116+ : "Asset: Not used" ;
117+ const projectLabel = resolveProjectBindingLabel ( ) ;
118+ const paletteTitle = compatibility . palette
119+ ? `Updated: ${ escapeHtml ( palette ?. selectedAt || "not-set" ) } `
120+ : "Not used by this tool" ;
121+ const assetTitle = compatibility . asset
122+ ? `Updated: ${ escapeHtml ( asset ?. selectedAt || "not-set" ) } `
123+ : "Not used by this tool" ;
124+
125+ return `
126+ <div class="tools-platform-frame__binding-badges" aria-label="Tool data bindings">
127+ <span class="tools-platform-frame__binding-badge${ compatibility . palette ? " is-active" : " is-muted" } " title="${ paletteTitle } ">${ escapeHtml ( paletteLabel ) } </span>
128+ <span class="tools-platform-frame__binding-badge${ compatibility . asset ? " is-active" : " is-muted" } " title="${ assetTitle } ">${ escapeHtml ( assetLabel ) } </span>
129+ <span class="tools-platform-frame__binding-badge is-project" title="Workspace manifest dirty state">${ escapeHtml ( projectLabel ) } </span>
130+ </div>
131+ ` ;
132+ }
133+
77134function renderToolLinks ( currentToolId ) {
78135 const tools = getToolRegistry ( )
79136 . filter ( ( entry ) => entry . active === true )
@@ -98,7 +155,12 @@ function renderToolLinks(currentToolId) {
98155 ${ bucketTools
99156 . map ( ( tool ) => {
100157 const currentClass = tool . id === currentToolId ? " is-current" : "" ;
101- return `<a class="tools-platform-frame__nav-link${ currentClass } " href="${ escapeHtml ( getRegistryEntryHref ( tool . entryPoint ) ) } ">${ escapeHtml ( tool . displayName ) } </a>` ;
158+ return `
159+ <div class="tools-platform-frame__nav-tool-row">
160+ <a class="tools-platform-frame__nav-link${ currentClass } " href="${ escapeHtml ( getRegistryEntryHref ( tool . entryPoint ) ) } ">${ escapeHtml ( tool . displayName ) } </a>
161+ ${ renderToolBindingBadges ( tool ) }
162+ </div>
163+ ` ;
102164 } )
103165 . join ( "" ) }
104166 </div>
@@ -376,6 +438,25 @@ function renderShell(currentTool) {
376438 bindWorkspaceShellEvents ( currentTool ) ;
377439}
378440
441+ function bindLiveBindingRefresh ( currentTool ) {
442+ if ( bindingRefreshHandlersBound || typeof window === "undefined" ) {
443+ return ;
444+ }
445+ bindingRefreshHandlersBound = true ;
446+
447+ const rerender = ( ) => {
448+ renderShell ( currentTool ) ;
449+ } ;
450+
451+ window . addEventListener ( "storage" , rerender ) ;
452+ window . addEventListener ( "focus" , rerender ) ;
453+ document . addEventListener ( "visibilitychange" , ( ) => {
454+ if ( document . visibilityState === "visible" ) {
455+ rerender ( ) ;
456+ }
457+ } ) ;
458+ }
459+
379460function ensureRuntimeMonitoring ( ) {
380461 if ( runtimeMonitoringHooks ) {
381462 return ;
@@ -482,6 +563,7 @@ function initPlatformShell() {
482563 }
483564
484565 renderShell ( currentTool ) ;
566+ bindLiveBindingRefresh ( currentTool ) ;
485567}
486568
487569initPlatformShell ( ) ;
0 commit comments