99use Magento \Framework \Console \Cli ;
1010use Magento \Framework \Filesystem \DirectoryList ;
1111use Magento \Framework \Filesystem ;
12+ use Magento \Framework \Component \ComponentRegistrar ;
13+ use Magento \Framework \Component \ComponentRegistrarInterface ;
1214use OpenForgeProject \MageForge \Console \Command \AbstractCommand ;
1315use OpenForgeProject \MageForge \Model \ThemeList ;
1416use OpenForgeProject \MageForge \Service \VendorFileMapper ;
@@ -24,7 +26,8 @@ public function __construct(
2426 private readonly ThemeList $ themeList ,
2527 private readonly VendorFileMapper $ vendorFileMapper ,
2628 private readonly Filesystem $ filesystem ,
27- private readonly DirectoryList $ directoryList
29+ private readonly DirectoryList $ directoryList ,
30+ private readonly ComponentRegistrarInterface $ componentRegistrar
2831 ) {
2932 parent ::__construct ();
3033 }
@@ -93,39 +96,43 @@ protected function executeCommand(InputInterface $input, OutputInterface $output
9396 $ this ->io ->error ("Theme not found: $ themeCode " );
9497 return Cli::RETURN_FAILURE ;
9598 }
96-
97- // Use View\Design\ThemeInterface::getFullPath() if available,
98- // fallback to calculating path assuming app/design/frontend structure if needed,
99- // but Theme model normally has getFullPath().
100- // Let's verify what interface we have. We likely have Magento\Theme\Model\Theme which has getFullPath().
101- if (!method_exists ($ theme , 'getFullPath ' )) {
102- // Fallback logic
103- $ themePath = 'app/design/frontend/ ' . $ theme ->getThemePath ();
104- } else {
105- $ themeAbsolutePath = $ theme ->getFullPath (); // This is absolute path
106- // Make relative
107- if (str_starts_with ($ themeAbsolutePath , $ rootPath . '/ ' )) {
108- $ themePath = substr ($ themeAbsolutePath , strlen ($ rootPath ) + 1 );
109- } else {
110- $ themePath = $ themeAbsolutePath ;
111- }
99+
100+ // Use ComponentRegistrar to get absolute path
101+ $ regName = $ theme ->getArea () . '/ ' . $ theme ->getCode ();
102+ $ themePath = $ this ->componentRegistrar ->getPath (ComponentRegistrar::THEME , $ regName );
103+
104+ if (!$ themePath ) {
105+ // Fallback to model path if registrar fails
106+ $ this ->io ->warning ("Theme path not found via ComponentRegistrar for $ regName, falling back to getFullPath() " );
107+ $ themePath = $ theme ->getFullPath ();
112108 }
113109
114110 // 4. Calculate Destination
115111 try {
116- $ destinationRelative = $ this ->vendorFileMapper ->mapToThemePath ($ sourceFile , $ themePath );
112+ $ destinationPath = $ this ->vendorFileMapper ->mapToThemePath ($ sourceFile , $ themePath );
117113 } catch (\Exception $ e ) {
118114 $ this ->io ->error ($ e ->getMessage ());
119115 return Cli::RETURN_FAILURE ;
120116 }
121117
122- $ absoluteDestPath = $ rootPath . '/ ' . $ destinationRelative ;
118+ if (str_starts_with ($ destinationPath , '/ ' )) {
119+ $ absoluteDestPath = $ destinationPath ;
120+ } else {
121+ $ absoluteDestPath = $ rootPath . '/ ' . $ destinationPath ;
122+ }
123+
124+ // Make destination relative for display if it's inside root
125+ $ destinationDisplay = $ absoluteDestPath ;
126+ if (str_starts_with ($ absoluteDestPath , $ rootPath . '/ ' )) {
127+ $ destinationDisplay = substr ($ absoluteDestPath , strlen ($ rootPath ) + 1 );
128+ }
123129
124130 // 5. Preview & Confirm
125131 $ this ->io ->section ('Copy Preview ' );
126132 $ this ->io ->text ([
127133 "Source: <info> $ sourceFile</info> " ,
128- "Target: <info> $ destinationRelative</info> " ,
134+ "Target: <info> $ destinationDisplay</info> " ,
135+ "Absolute Target: <comment> $ absoluteDestPath</comment> "
129136 ]);
130137 $ this ->io ->newLine ();
131138
@@ -156,6 +163,8 @@ protected function executeCommand(InputInterface $input, OutputInterface $output
156163 }
157164
158165 return Cli::RETURN_SUCCESS ;
166+ }
167+
159168 private function fixPromptEnvironment (): void
160169 {
161170 if (getenv ('DDEV_PROJECT ' )) {
0 commit comments