@@ -22,6 +22,7 @@ class WorkspaceV2ToolStateProducer {
2222 this . backButton = document . getElementById ( "workspaceV2BackButton" ) ;
2323 this . loadFixtureButton = document . getElementById ( "workspaceV2LoadFixtureButton" ) ;
2424 this . launchButton = document . getElementById ( "workspaceV2LaunchButton" ) ;
25+ this . promoteActiveToolStateButton = document . getElementById ( "workspaceV2PromoteActiveToolStateButton" ) ;
2526 this . openAssetManagerButton = document . getElementById ( "workspaceV2OpenAssetManagerButton" ) ;
2627 this . importJsonNode = document . getElementById ( "workspaceV2ImportJson" ) ;
2728 this . importFileNode = document . getElementById ( "workspaceV2ImportFile" ) ;
@@ -101,6 +102,9 @@ class WorkspaceV2ToolStateProducer {
101102 this . launchButton . addEventListener ( "click" , ( ) => {
102103 this . createToolStateAndLaunch ( ) ;
103104 } ) ;
105+ this . promoteActiveToolStateButton . addEventListener ( "click" , ( ) => {
106+ this . promoteActiveToolStateToWorkspaceTools ( ) ;
107+ } ) ;
104108 this . openAssetManagerButton . addEventListener ( "click" , ( ) => {
105109 this . openAssetManagerFromWorkspace ( ) ;
106110 } ) ;
@@ -1275,6 +1279,62 @@ class WorkspaceV2ToolStateProducer {
12751279 return this . readActiveToolStatePayloadForLibraryActions ( ) ;
12761280 }
12771281
1282+ validateToolStatePromotionPayload ( toolStatePayload , toolStatePath ) {
1283+ if ( ! this . isValidToolStatePayload ( toolStatePayload ) ) {
1284+ return { ok : false , message : `${ toolStatePath } is invalid.` , toolId : "" , payloadJson : null } ;
1285+ }
1286+ const payloadValidation = this . validateWorkspaceToolStatePayload ( toolStatePayload , toolStatePath ) ;
1287+ if ( ! payloadValidation . ok ) {
1288+ return { ok : false , message : payloadValidation . message , toolId : "" , payloadJson : null } ;
1289+ }
1290+ const toolId = typeof toolStatePayload . toolId === "string" ? toolStatePayload . toolId . trim ( ) : "" ;
1291+ if ( ! toolId ) {
1292+ return { ok : false , message : `${ toolStatePath } .toolId is required for promotion.` , toolId : "" , payloadJson : null } ;
1293+ }
1294+ if ( toolId === "palette-manager-v2" ) {
1295+ return { ok : false , message : "Promotion blocked. palette-manager-v2 cannot be promoted to tools; palette is workspace-owned at tools.palette-browser." , toolId : "" , payloadJson : null } ;
1296+ }
1297+ if ( ! toolStatePayload . payloadJson || typeof toolStatePayload . payloadJson !== "object" || Array . isArray ( toolStatePayload . payloadJson ) ) {
1298+ return { ok : false , message : `${ toolStatePath } .payloadJson must be an object for promotion.` , toolId : "" , payloadJson : null } ;
1299+ }
1300+ return {
1301+ ok : true ,
1302+ message : "" ,
1303+ toolId,
1304+ payloadJson : this . cloneToolStateValue ( toolStatePayload . payloadJson )
1305+ } ;
1306+ }
1307+
1308+ promoteToolStatePayloadToWorkspaceTools ( toolStatePayload , toolStatePath , successPrefix ) {
1309+ const promotionValidation = this . validateToolStatePromotionPayload ( toolStatePayload , toolStatePath ) ;
1310+ if ( ! promotionValidation . ok ) {
1311+ this . statusNode . textContent = promotionValidation . message ;
1312+ return false ;
1313+ }
1314+ this . workspaceImportedToolEntries [ promotionValidation . toolId ] = promotionValidation . payloadJson ;
1315+ if ( ! this . syncWorkspaceManifestTextarea ( ) ) {
1316+ this . statusNode . textContent = "Promotion failed. Workspace manifest sync failed." ;
1317+ return false ;
1318+ }
1319+ this . renderWorkspaceToolsSummary ( ) ;
1320+ this . statusNode . textContent = `${ successPrefix } promoted to tools.${ promotionValidation . toolId } .` ;
1321+ return true ;
1322+ }
1323+
1324+ promoteActiveToolStateToWorkspaceTools ( ) {
1325+ const activeToolState = this . readActiveToolStatePayloadForLibraryActions ( ) ;
1326+ if ( ! this . isValidToolStatePayload ( activeToolState ) ) {
1327+ this . statusNode . textContent = "Promotion blocked. No active tool state is available." ;
1328+ return ;
1329+ }
1330+ const activeToolId = typeof activeToolState . toolId === "string" ? activeToolState . toolId . trim ( ) : "" ;
1331+ this . promoteToolStatePayloadToWorkspaceTools (
1332+ activeToolState ,
1333+ "tools.workspace-v2.activeToolState" ,
1334+ `Active tool state '${ activeToolId || "unknown" } '`
1335+ ) ;
1336+ }
1337+
12781338 readToolStatePayloadFromRecentToolStateId ( toolStateId ) {
12791339 if ( typeof toolStateId !== "string" || ! toolStateId . trim ( ) ) {
12801340 return null ;
@@ -1700,6 +1760,7 @@ class WorkspaceV2ToolStateProducer {
17001760 const idCode = document . createElement ( "code" ) ;
17011761 const copyIdButton = document . createElement ( "button" ) ;
17021762 const useInLibraryButton = document . createElement ( "button" ) ;
1763+ const promoteToToolsButton = document . createElement ( "button" ) ;
17031764 const loadButton = document . createElement ( "button" ) ;
17041765 const overwriteButton = document . createElement ( "button" ) ;
17051766 const deleteSavedButton = document . createElement ( "button" ) ;
@@ -1726,6 +1787,11 @@ class WorkspaceV2ToolStateProducer {
17261787 useInLibraryButton . addEventListener ( "click" , ( ) => {
17271788 this . useSavedToolStateIdInLibraryInput ( toolStateName ) ;
17281789 } ) ;
1790+ promoteToToolsButton . type = "button" ;
1791+ promoteToToolsButton . textContent = "Promote to Tools" ;
1792+ promoteToToolsButton . addEventListener ( "click" , ( ) => {
1793+ this . promoteSavedToolStateById ( toolStateName ) ;
1794+ } ) ;
17291795 loadButton . type = "button" ;
17301796 loadButton . textContent = "Load" ;
17311797 loadButton . disabled = paletteRowLocked ;
@@ -1743,7 +1809,7 @@ class WorkspaceV2ToolStateProducer {
17431809 deleteSavedButton . addEventListener ( "click" , ( ) => {
17441810 this . deleteSavedToolStateById ( toolStateName ) ;
17451811 } ) ;
1746- item . append ( label , idLine , copyIdButton , useInLibraryButton , loadButton , overwriteButton , deleteSavedButton ) ;
1812+ item . append ( label , idLine , copyIdButton , useInLibraryButton , promoteToToolsButton , loadButton , overwriteButton , deleteSavedButton ) ;
17471813 this . toolStateListNode . appendChild ( item ) ;
17481814 } ) ;
17491815 this . renderToolStateDiffInputs ( ) ;
@@ -1779,6 +1845,31 @@ class WorkspaceV2ToolStateProducer {
17791845 this . setLibraryStatus ( `Saved tool state ID ready for Diff/Merge and Library actions: ${ toolStateId . trim ( ) } ` ) ;
17801846 }
17811847
1848+ promoteSavedToolStateById ( toolStateId ) {
1849+ if ( typeof toolStateId !== "string" || ! toolStateId . trim ( ) ) {
1850+ this . setLibraryStatus ( "Promotion blocked. Enter a saved tool state ID before promoting." ) ;
1851+ return ;
1852+ }
1853+ const library = this . readToolStateLibrary ( ) ;
1854+ if ( library === null ) {
1855+ return ;
1856+ }
1857+ if ( ! Object . prototype . hasOwnProperty . call ( library , toolStateId . trim ( ) ) ) {
1858+ this . setLibraryStatus ( "Promotion blocked. Saved tool state not found." ) ;
1859+ return ;
1860+ }
1861+ const savedToolStatePayload = library [ toolStateId . trim ( ) ] ;
1862+ const promoted = this . promoteToolStatePayloadToWorkspaceTools (
1863+ savedToolStatePayload ,
1864+ `tools.workspace-v2.savedToolStates.${ toolStateId . trim ( ) } ` ,
1865+ `Saved tool state '${ toolStateId . trim ( ) } '`
1866+ ) ;
1867+ if ( ! promoted ) {
1868+ return ;
1869+ }
1870+ this . setLibraryStatus ( `Saved tool state '${ toolStateId . trim ( ) } ' promoted to tools.` ) ;
1871+ }
1872+
17821873 loadSavedToolStateById ( toolStateId ) {
17831874 if ( typeof toolStateId !== "string" || ! toolStateId . trim ( ) ) {
17841875 this . setLibraryStatus ( "Enter a saved tool state ID before loading." ) ;
0 commit comments