1- import { joinURL } from 'ufo'
1+ import { joinURL , withoutTrailingSlash } from 'ufo'
22import { useState , useRuntimeConfig } from '#imports'
33
44export function useStories ( ) {
@@ -14,11 +14,17 @@ export function useStories() {
1414 frameBaseUrl : string | null
1515 } ) ?? { routeBasePath : '/' , frameBasePath : '/-frame' , frameBaseUrl : null }
1616
17+ // app.baseURL is e.g. '/nuxt-stories/' when deployed to a sub-path.
18+ // Iframe src attributes are resolved against the page origin (not the Nuxt router
19+ // base), so we must prepend the app base explicitly for same-origin frame URLs.
20+ const appBase = withoutTrailingSlash ( config . app . baseURL ?? '' )
21+
1722 /** Build the shell URL for a child route path (relative, e.g. 'v-button/default') */
1823 const storiesPath = ( path : string ) => joinURL ( routeBasePath , path )
1924
2025 /** Convert a shell URL to its frame counterpart.
21- * When frameBaseUrl is set (2-process mode) the result is an absolute URL. */
26+ * When frameBaseUrl is set (2-process mode) the result is an absolute URL.
27+ * Otherwise the result is an absolute path from the server root (includes appBase). */
2228 const frameUrl = ( path : string ) => {
2329 const routeRelative = path . startsWith ( routeBasePath )
2430 ? path . slice ( routeBasePath . length )
@@ -32,7 +38,9 @@ export function useStories() {
3238 ? normalizedRelative . slice ( frameBasePath . length )
3339 : normalizedRelative
3440 const framePath = joinURL ( frameBasePath , withoutFramePrefix || '/' )
35- return frameBaseUrl ? frameBaseUrl + framePath : framePath
41+ if ( frameBaseUrl ) return frameBaseUrl + framePath
42+ // Prefix with appBase so the iframe src resolves to the correct server path
43+ return joinURL ( appBase || '/' , framePath )
3644 }
3745
3846 return { storiesUIVisible, storiesPath, frameUrl }
0 commit comments