@@ -32,6 +32,61 @@ function escapeHtml(value) {
3232 . replaceAll ( "'" , "'" ) ;
3333}
3434
35+ function toStandaloneToolHref ( entryPoint ) {
36+ const normalized = String ( entryPoint || "" ) . replace ( / ^ \. ? \/ * / , "" ) ;
37+ return normalized ? `/tools/${ encodeURI ( normalized ) } ` : "" ;
38+ }
39+
40+ function shouldUsePresetRoundtrip ( sample , toolId ) {
41+ const sampleId = normalize ( sample ?. id ) ;
42+ const samplePhase = normalize ( sample ?. phase ) ;
43+ if ( ! sampleId || ! samplePhase ) {
44+ return false ;
45+ }
46+ if ( sampleId === "1208" && samplePhase === "12" ) {
47+ return toolId === "tile-map-editor"
48+ || toolId === "parallax-editor"
49+ || toolId === "vector-asset-studio" ;
50+ }
51+ if ( toolId !== "parallax-editor" ) {
52+ return false ;
53+ }
54+ return ( sampleId === "0306" && samplePhase === "03" )
55+ || ( sampleId === "1204" && samplePhase === "12" )
56+ || ( sampleId === "1205" && samplePhase === "12" ) ;
57+ }
58+
59+ function buildRoundtripLinks ( sample , toolRegistryMap ) {
60+ const orderedToolHints = asArray ( sample ?. toolHints )
61+ . map ( ( entry ) => normalizeToken ( entry ) )
62+ . filter ( Boolean )
63+ . filter ( ( toolId ) => toolId !== "workspace-manager" ) ;
64+ const dedupedToolHints = [ ...new Set ( orderedToolHints ) ] ;
65+ const links = [ ] ;
66+
67+ dedupedToolHints . forEach ( ( toolId ) => {
68+ const tool = toolRegistryMap . get ( toolId ) ;
69+ if ( ! tool ) {
70+ return ;
71+ }
72+ const baseHref = toStandaloneToolHref ( tool . entryPoint ) ;
73+ if ( ! baseHref ) {
74+ return ;
75+ }
76+
77+ let href = baseHref ;
78+ let label = `Open ${ normalize ( tool . displayName ) || normalize ( tool . name ) || toolId } ` ;
79+ if ( shouldUsePresetRoundtrip ( sample , toolId ) ) {
80+ const presetPath = `/samples/phase-${ sample . phase } /${ sample . id } /${ toolId } -sample-${ sample . id } .json` ;
81+ href = `${ baseHref } ?sampleId=${ encodeURIComponent ( sample . id ) } &samplePresetPath=${ encodeURIComponent ( presetPath ) } ` ;
82+ }
83+
84+ links . push ( { toolId, href, label } ) ;
85+ } ) ;
86+
87+ return links ;
88+ }
89+
3590function readPinnedSet ( ) {
3691 try {
3792 const raw = window . localStorage . getItem ( PINNED_KEY ) ;
@@ -82,7 +137,7 @@ function buildToolTokens(toolHints, toolLabelMap) {
82137 . sort ( ( a , b ) => a . label . localeCompare ( b . label , undefined , { sensitivity : "base" } ) ) ;
83138}
84139
85- function buildSampleRows ( metadata , pinnedSet , toolLabelMap ) {
140+ function buildSampleRows ( metadata , pinnedSet , toolLabelMap , toolRegistryMap ) {
86141 const phaseInfoMap = new Map (
87142 asArray ( metadata ?. phases )
88143 . map ( ( phase ) => {
@@ -116,6 +171,7 @@ function buildSampleRows(metadata, pinnedSet, toolLabelMap) {
116171 const tags = asArray ( sample ?. tags ) . map ( ( tag ) => normalizeTag ( tag ) ) . filter ( Boolean ) ;
117172 const classTokens = buildClassTokens ( sample ?. classValues , sample ?. engineClassesUsed ) ;
118173 const toolTokens = buildToolTokens ( sample ?. toolHints , toolLabelMap ) ;
174+ const roundtripLinks = buildRoundtripLinks ( { id, phase, toolHints : sample ?. toolHints } , toolRegistryMap ) ;
119175 const previewSrc = normalize ( sample ?. thumbnail ) || normalize ( sample ?. preview ) || "" ;
120176 return {
121177 id,
@@ -128,6 +184,7 @@ function buildSampleRows(metadata, pinnedSet, toolLabelMap) {
128184 tags,
129185 classTokens,
130186 toolTokens,
187+ roundtripLinks,
131188 previewSrc,
132189 pinned : pinnedSet . has ( id )
133190 } ;
@@ -253,6 +310,18 @@ function buildSampleCard(sample) {
253310 card . appendChild ( title ) ;
254311 card . appendChild ( previewWrap ) ;
255312 card . appendChild ( description ) ;
313+ if ( Array . isArray ( sample . roundtripLinks ) && sample . roundtripLinks . length > 0 ) {
314+ const roundtripSection = document . createElement ( "section" ) ;
315+ roundtripSection . className = "sample-tool-roundtrip" ;
316+ roundtripSection . innerHTML = `
317+ <h4>Tool Roundtrip Links</h4>
318+ <p>Use these to validate tool to sample and sample back to tool workflows.</p>
319+ <ul>
320+ ${ sample . roundtripLinks . map ( ( entry ) => `<li><a href="${ escapeHtml ( entry . href ) } ">${ escapeHtml ( entry . label ) } </a></li>` ) . join ( "" ) }
321+ </ul>
322+ ` ;
323+ card . appendChild ( roundtripSection ) ;
324+ }
256325 return card ;
257326}
258327
@@ -339,14 +408,21 @@ export async function initSamplesIndex() {
339408 }
340409 const metadata = await response . json ( ) ;
341410 let pinnedSet = readPinnedSet ( ) ;
411+ const toolRegistry = getToolRegistry ( ) ;
342412 const toolLabelMap = new Map (
343- getToolRegistry ( )
413+ toolRegistry
344414 . filter ( ( tool ) => tool . id !== "workspace-manager" )
345415 . map ( ( tool ) => [ normalizeToken ( tool . id ) , normalize ( tool . displayName ) || normalize ( tool . name ) || normalize ( tool . id ) ] )
346416 . filter ( ( entry ) => entry [ 0 ] && entry [ 1 ] )
347417 ) ;
418+ const toolRegistryMap = new Map (
419+ toolRegistry
420+ . filter ( ( tool ) => tool . id !== "workspace-manager" )
421+ . map ( ( tool ) => [ normalizeToken ( tool . id ) , tool ] )
422+ . filter ( ( entry ) => entry [ 0 ] && entry [ 1 ] )
423+ ) ;
348424
349- const model = buildSampleRows ( metadata , pinnedSet , toolLabelMap ) ;
425+ const model = buildSampleRows ( metadata , pinnedSet , toolLabelMap , toolRegistryMap ) ;
350426 setSelectOptions ( phaseSelect , model . phaseOptions . map ( ( entry ) => entry . value ) , ( value ) => {
351427 const found = model . phaseOptions . find ( ( entry ) => entry . value === value ) ;
352428 return found ?. label || `Phase ${ value } ` ;
@@ -366,7 +442,7 @@ export async function initSamplesIndex() {
366442 }
367443
368444 const render = ( ) => {
369- const nextModel = buildSampleRows ( metadata , pinnedSet , toolLabelMap ) ;
445+ const nextModel = buildSampleRows ( metadata , pinnedSet , toolLabelMap , toolRegistryMap ) ;
370446 const filterState = {
371447 phase : normalize ( phaseSelect . value ) ,
372448 className : normalize ( classSelect . value ) ,
0 commit comments