@@ -225,7 +225,7 @@ class WorkspaceV2SessionProducer {
225225 }
226226
227227 selectedSessionName ( ) {
228- return typeof this . sessionNameNode . value === "string" ? this . sessionNameNode . value . trim ( ) : "" ;
228+ return typeof this . sessionNameNode . value === "string" ? this . sessionNameNode . value : "" ;
229229 }
230230
231231 currentNavMode ( ) {
@@ -285,6 +285,22 @@ class WorkspaceV2SessionProducer {
285285 }
286286 }
287287
288+ createProducerPayloadForTool ( toolId ) {
289+ if ( toolId === "palette-manager-v2" ) {
290+ return {
291+ version : "v2" ,
292+ toolId : "palette-manager-v2" ,
293+ paletteJson : {
294+ swatches : [ ]
295+ }
296+ } ;
297+ }
298+ return this . withSessionVersion ( {
299+ toolId,
300+ payloadJson : { }
301+ } ) ;
302+ }
303+
288304 initializeWorkspaceProducerSession ( ) {
289305 if ( this . isValidSessionPayload ( this . currentSessionPayload ) && this . currentHostContextId ) {
290306 return ;
@@ -294,10 +310,7 @@ class WorkspaceV2SessionProducer {
294310 this . statusNode . textContent = "Workspace V2 initialization blocked: default tool is missing." ;
295311 return ;
296312 }
297- const initialPayload = this . withSessionVersion ( {
298- toolId : selectedToolId ,
299- payloadJson : { }
300- } ) ;
313+ const initialPayload = this . createProducerPayloadForTool ( selectedToolId ) ;
301314 const sizeValidation = this . validateSessionPayloadSize ( initialPayload ) ;
302315 if ( ! sizeValidation . ok ) {
303316 this . statusNode . textContent = sizeValidation . message ;
@@ -307,7 +320,7 @@ class WorkspaceV2SessionProducer {
307320 sessionStorage . setItem ( hostContextId , sizeValidation . metrics . serializedPayload ) ;
308321 this . currentHostContextId = hostContextId ;
309322 this . setCurrentSessionPayload ( initialPayload , "workspace-v2-init" ) ;
310- this . importJsonNode . value = JSON . stringify ( initialPayload , null , 2 ) ;
323+ this . syncWorkspaceManifestTextarea ( ) ;
311324 this . statusNode . textContent = `Workspace V2 initialized.\nTool: ${ selectedToolId } \nHostContextId: ${ hostContextId } \nSession is active for Save Session.` ;
312325 }
313326
@@ -323,22 +336,21 @@ class WorkspaceV2SessionProducer {
323336 if ( typeof sessionId !== "string" ) {
324337 return false ;
325338 }
326- const trimmed = sessionId . trim ( ) ;
327- if ( ! trimmed ) {
339+ if ( ! sessionId ) {
328340 return false ;
329341 }
330- return / ^ [ a - z 0 - 9 ] [ a - z 0 - 9 - _ ] { 1 , 63 } $ / i . test ( trimmed ) ;
342+ return / ^ [ A - Z a - z 0 - 9 _ - ] + $ / . test ( sessionId ) ;
331343 }
332344
333345 savedSessionIdExists ( sessionId ) {
334- if ( typeof sessionId !== "string" || ! sessionId . trim ( ) ) {
346+ if ( typeof sessionId !== "string" || ! sessionId ) {
335347 return false ;
336348 }
337349 const library = this . readSessionLibrary ( ) ;
338350 if ( library === null ) {
339351 return false ;
340352 }
341- return Object . prototype . hasOwnProperty . call ( library , sessionId . trim ( ) ) ;
353+ return Object . prototype . hasOwnProperty . call ( library , sessionId ) ;
342354 }
343355
344356 updateSessionLibraryActionState ( ) {
@@ -348,7 +360,7 @@ class WorkspaceV2SessionProducer {
348360 return ;
349361 }
350362 if ( ! model . libraryIdValid ) {
351- this . libraryStatusNode . textContent = "Enter a valid new session ID before saving ." ;
363+ this . libraryStatusNode . textContent = "Invalid session ID. Use letters, numbers, hyphen, or underscore only ." ;
352364 return ;
353365 }
354366 if ( model . librarySavedSessionExists ) {
@@ -2789,6 +2801,78 @@ class WorkspaceV2SessionProducer {
27892801 this . diagnosticsPayloadNode . textContent = snapshot . payloadPreview ;
27902802 }
27912803
2804+ normalizePaletteFixtureSwatches ( paletteJson ) {
2805+ if ( ! paletteJson || typeof paletteJson !== "object" || Array . isArray ( paletteJson ) ) {
2806+ return { ok : false , message : "Fixture is invalid. paletteJson must be an object for palette-manager-v2." , value : null } ;
2807+ }
2808+ if ( Object . prototype . hasOwnProperty . call ( paletteJson , "colors" ) && ! Array . isArray ( paletteJson . swatches ) ) {
2809+ if ( ! Array . isArray ( paletteJson . colors ) ) {
2810+ return { ok : false , message : "Fixture is invalid. paletteJson.colors must be an array when swatches is missing." , value : null } ;
2811+ }
2812+ const convertedSwatches = [ ] ;
2813+ for ( let index = 0 ; index < paletteJson . colors . length ; index += 1 ) {
2814+ const colorEntry = paletteJson . colors [ index ] ;
2815+ if ( typeof colorEntry === "string" ) {
2816+ if ( ! / ^ # ( [ A - F a - f 0 - 9 ] { 6 } | [ A - F a - f 0 - 9 ] { 8 } ) $ / . test ( colorEntry ) ) {
2817+ return { ok : false , message : `Fixture is invalid. paletteJson.colors[${ index } ] must be #RRGGBB or #RRGGBBAA.` , value : null } ;
2818+ }
2819+ convertedSwatches . push ( {
2820+ symbol : String . fromCharCode ( 65 + ( index % 26 ) ) ,
2821+ hex : colorEntry ,
2822+ name : `Color ${ index + 1 } `
2823+ } ) ;
2824+ continue ;
2825+ }
2826+ if ( ! colorEntry || typeof colorEntry !== "object" || Array . isArray ( colorEntry ) ) {
2827+ return { ok : false , message : `Fixture is invalid. paletteJson.colors[${ index } ] must be a string or object.` , value : null } ;
2828+ }
2829+ if ( typeof colorEntry . hex !== "string" || ! / ^ # ( [ A - F a - f 0 - 9 ] { 6 } | [ A - F a - f 0 - 9 ] { 8 } ) $ / . test ( colorEntry . hex ) ) {
2830+ return { ok : false , message : `Fixture is invalid. paletteJson.colors[${ index } ].hex must be #RRGGBB or #RRGGBBAA.` , value : null } ;
2831+ }
2832+ const symbol = typeof colorEntry . symbol === "string" && colorEntry . symbol . length === 1
2833+ ? colorEntry . symbol
2834+ : String . fromCharCode ( 65 + ( index % 26 ) ) ;
2835+ const name = typeof colorEntry . name === "string" && colorEntry . name . trim ( )
2836+ ? colorEntry . name
2837+ : `Color ${ index + 1 } ` ;
2838+ convertedSwatches . push ( {
2839+ symbol,
2840+ hex : colorEntry . hex ,
2841+ name
2842+ } ) ;
2843+ }
2844+ const normalizedPalette = { ...paletteJson } ;
2845+ delete normalizedPalette . colors ;
2846+ normalizedPalette . swatches = convertedSwatches ;
2847+ return { ok : true , message : "" , value : normalizedPalette } ;
2848+ }
2849+ if ( ! Array . isArray ( paletteJson . swatches ) ) {
2850+ return { ok : false , message : "Fixture is invalid. paletteJson.swatches must be an array for palette-manager-v2." , value : null } ;
2851+ }
2852+ return { ok : true , message : "" , value : paletteJson } ;
2853+ }
2854+
2855+ normalizeFixtureSessionContext ( toolId , sessionContext ) {
2856+ if ( ! this . isValidSessionPayload ( sessionContext ) ) {
2857+ return { ok : false , message : "Fixture is invalid. Missing sessionContext object." , value : null } ;
2858+ }
2859+ const normalizedSession = this . withSessionVersion ( this . cloneSessionValue ( sessionContext ) ) ;
2860+ if ( toolId === "palette-manager-v2" ) {
2861+ if ( Object . prototype . hasOwnProperty . call ( normalizedSession , "payloadJson" ) ) {
2862+ return { ok : false , message : "Fixture is invalid. payloadJson is not supported for palette-manager-v2." , value : null } ;
2863+ }
2864+ if ( ! Object . prototype . hasOwnProperty . call ( normalizedSession , "paletteJson" ) ) {
2865+ return { ok : false , message : "Fixture is invalid. paletteJson is required for palette-manager-v2." , value : null } ;
2866+ }
2867+ const normalizedPalette = this . normalizePaletteFixtureSwatches ( normalizedSession . paletteJson ) ;
2868+ if ( ! normalizedPalette . ok ) {
2869+ return { ok : false , message : normalizedPalette . message , value : null } ;
2870+ }
2871+ normalizedSession . paletteJson = normalizedPalette . value ;
2872+ }
2873+ return { ok : true , message : "" , value : normalizedSession } ;
2874+ }
2875+
27922876 async loadSelectedFixture ( ) {
27932877 const toolId = this . selectedToolId ( ) ;
27942878 if ( ! toolId ) {
@@ -2808,15 +2892,29 @@ class WorkspaceV2SessionProducer {
28082892 this . setCurrentSessionPayload ( null , "" ) ;
28092893 return ;
28102894 }
2811- if ( ! this . isValidSessionPayload ( fixture . sessionContext ) ) {
2812- this . statusNode . textContent = "Fixture is invalid. Missing sessionContext object." ;
2895+ const normalizedFixtureSession = this . normalizeFixtureSessionContext ( toolId , fixture . sessionContext ) ;
2896+ if ( ! normalizedFixtureSession . ok ) {
2897+ this . statusNode . textContent = normalizedFixtureSession . message ;
28132898 this . setCurrentSessionPayload ( null , "" ) ;
28142899 return ;
28152900 }
2816- this . setCurrentSessionPayload ( fixture . sessionContext , `fixture:${ toolId } ` ) ;
2817- this . currentHostContextId = "" ;
2901+ const fixtureHostContextId = typeof fixture . hostContextId === "string" && fixture . hostContextId . trim ( )
2902+ ? fixture . hostContextId . trim ( )
2903+ : this . createHostContextId ( toolId ) ;
2904+ const sizeValidation = this . validateSessionPayloadSize ( normalizedFixtureSession . value ) ;
2905+ if ( ! sizeValidation . ok ) {
2906+ this . statusNode . textContent = sizeValidation . message ;
2907+ this . setCurrentSessionPayload ( null , "" ) ;
2908+ return ;
2909+ }
2910+ sessionStorage . setItem ( fixtureHostContextId , sizeValidation . metrics . serializedPayload ) ;
2911+ this . setCurrentSessionPayload ( normalizedFixtureSession . value , `fixture:${ toolId } ` ) ;
2912+ this . currentHostContextId = fixtureHostContextId ;
28182913 this . renderDiagnosticsPanel ( ) ;
2819- this . importJsonNode . value = JSON . stringify ( fixture . sessionContext , null , 2 ) ;
2914+ if ( ! this . syncWorkspaceManifestTextarea ( ) ) {
2915+ this . statusNode . textContent = "Fixture loaded but workspace manifest sync failed." ;
2916+ return ;
2917+ }
28202918 this . statusNode . textContent = `Fixture loaded for ${ toolId } .\nSession payload is ready for launch, export, share, or library save.` ;
28212919 } catch ( error ) {
28222920 this . setCurrentSessionPayload ( null , "" ) ;
@@ -2924,6 +3022,19 @@ class WorkspaceV2SessionProducer {
29243022 }
29253023 }
29263024
3025+ syncWorkspaceManifestTextarea ( ) {
3026+ const workspaceSchemaDocument = this . buildWorkspaceSchemaDocument ( ) ;
3027+ if ( ! workspaceSchemaDocument ) {
3028+ return false ;
3029+ }
3030+ const validation = this . validateWorkspaceSchemaDocument ( workspaceSchemaDocument ) ;
3031+ if ( ! validation . ok ) {
3032+ return false ;
3033+ }
3034+ this . workspaceJsonNode . value = JSON . stringify ( workspaceSchemaDocument , null , 2 ) ;
3035+ return true ;
3036+ }
3037+
29273038 buildWorkspaceSchemaDocument ( ) {
29283039 const activePayload = this . resolveActiveSessionPayloadForWorkspaceManifest ( ) ;
29293040 if ( ! this . isValidSessionPayload ( activePayload ) ) {
@@ -3241,7 +3352,7 @@ class WorkspaceV2SessionProducer {
32413352 return ;
32423353 }
32433354 if ( ! overwriteExisting && ! this . isValidNewSessionId ( sessionName ) ) {
3244- this . setLibraryStatus ( "Enter a valid new session ID before saving ." ) ;
3355+ this . setLibraryStatus ( "Invalid session ID. Use letters, numbers, hyphen, or underscore only ." ) ;
32453356 return ;
32463357 }
32473358 const library = this . readSessionLibrary ( ) ;
0 commit comments