Skip to content
This repository was archived by the owner on Feb 17, 2026. It is now read-only.

Commit 5fb337f

Browse files
authored
Fix thumbnails on AI Style Selection breaking issue (#137)
* fix an issue where thumbnails on AI Style Selection breaking due to double slash * normalize baseURL to ensure a trailing slash
1 parent 87eccd8 commit 5fb337f

File tree

9 files changed

+313
-285
lines changed

9 files changed

+313
-285
lines changed

packages/plugin-ai-audio-generation-web/src/elevenlabs/ElevenMultilingualV2.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
type Provider,
33
type AudioOutput,
44
CommonProviderConfiguration,
5-
getPanelId
5+
getPanelId,
6+
normalizeBaseURL
67
} from '@imgly/plugin-ai-generation-web';
78
import CreativeEditorSDK from '@cesdk/cesdk-js';
89
import schema from './ElevenMultilingualV2.json';
@@ -45,9 +46,11 @@ function getProvider(
4546
cesdk: CreativeEditorSDK,
4647
config: ProviderConfiguration
4748
): Provider<'audio', ElevenlabsInput, AudioOutput> {
48-
const baseURL =
49+
// Normalize baseURL to ensure exactly one trailing slash
50+
const baseURL = normalizeBaseURL(
4951
config.baseURL ??
50-
'https://cdn.img.ly/assets/plugins/plugin-ai-audio-generation-web/v1/elevenlabs/';
52+
'https://cdn.img.ly/assets/plugins/plugin-ai-audio-generation-web/v1/elevenlabs/'
53+
);
5154

5255
const prefix = 'ly.img.ai.audio-generation.speech.elevenlabs';
5356
const voiceSelectionPanelId = `${prefix}.voiceSelection`;

packages/plugin-ai-generation-web/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export {
7979
getThumbnailForVideo,
8080
getLabelFromId,
8181
isAsyncGenerator,
82-
addIconSetOnce
82+
addIconSetOnce,
83+
normalizeBaseURL
8384
} from './utils/utils';
8485

8586
export { checkAiPluginVersion } from './utils/checkAiPluginVersion';

packages/plugin-ai-generation-web/src/utils/utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,18 @@ export function addIconSetOnce(
312312
cesdk.ui.experimental.setGlobalStateValue(globalStateIconSetAddedId, true);
313313
}
314314
}
315+
316+
/**
317+
* Normalizes a base URL to ensure it has exactly one trailing slash.
318+
*
319+
* @param url - The base URL to normalize
320+
* @returns The normalized URL with exactly one trailing slash
321+
*
322+
* @example
323+
* normalizeBaseURL('https://example.com') // 'https://example.com/'
324+
* normalizeBaseURL('https://example.com/') // 'https://example.com/'
325+
* normalizeBaseURL('https://example.com//') // 'https://example.com/'
326+
*/
327+
export function normalizeBaseURL(url: string): string {
328+
return `${url.replace(/\/+$/, '')}/`;
329+
}

packages/plugin-ai-image-generation-web/src/fal-ai/Recraft20b.constants.ts

Lines changed: 94 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -226,101 +226,101 @@ export const STYLE_ICON_DEFAULT = STYLES_ICON[0];
226226

227227
// prettier-ignore
228228
const STYLE_THUMBNAILS: (baseURL: string) => ({ [key in StyleId]?: string }) = (baseURL) => ({
229-
'realistic_image': `${baseURL}/realistic_image.webp`,
230-
'digital_illustration': `${baseURL}/digital_illustration.webp`,
231-
'realistic_image/b_and_w': `${baseURL}/realistic_image_black_&_white.webp`,
232-
'realistic_image/hard_flash': `${baseURL}/realistic_image_hard_flash.webp`,
233-
'realistic_image/hdr': `${baseURL}/realistic_image_hdr.webp`,
234-
'realistic_image/natural_light': `${baseURL}/realistic_image_natural_light.webp`,
235-
'realistic_image/studio_portrait': `${baseURL}/realistic_image_studio_portrait.webp`,
236-
'realistic_image/enterprise': `${baseURL}/realistic_image_enterprise.webp`,
237-
'realistic_image/motion_blur': `${baseURL}/realistic_image_motion_blur.webp`,
238-
'digital_illustration/pixel_art': `${baseURL}/digital_illustration_pixel_art.webp`,
239-
'digital_illustration/hand_drawn': `${baseURL}/digital_illustration_hand_drawn.webp`,
240-
'digital_illustration/grain': `${baseURL}/digital_illustration_grain.webp`,
241-
'digital_illustration/infantile_sketch': `${baseURL}/digital_illustration_infantile_sketch.webp`,
242-
'digital_illustration/2d_art_poster': `${baseURL}/digital_illustration_2d_art_poster.webp`,
243-
'digital_illustration/handmade_3d': `${baseURL}/digital_illustration_handmade_3d.webp`,
244-
'digital_illustration/hand_drawn_outline': `${baseURL}/digital_illustration_handdrawn_outline.webp`,
245-
'digital_illustration/engraving_color': `${baseURL}/digital_illustration_engraving_color.webp`,
246-
'digital_illustration/2d_art_poster_2': `${baseURL}/digital_illustration_2d_artposter_2.webp`,
247-
'vector_illustration': `${baseURL}/vector_illustration.svg`,
248-
'vector_illustration/bold_stroke': `${baseURL}/vector_illustration_bold_stroke.svg`,
249-
'vector_illustration/chemistry': `${baseURL}/vector_illustration_chemistry.svg`,
250-
'vector_illustration/colored_stencil': `${baseURL}/vector_illustration_colored_stencil.svg`,
251-
'vector_illustration/contour_pop_art': `${baseURL}/vector_illustration_contour_pop_art.svg`,
252-
'vector_illustration/cosmics': `${baseURL}/vector_illustration_cosmics.svg`,
253-
'vector_illustration/cutout': `${baseURL}/vector_illustration_cutout.svg`,
254-
'vector_illustration/depressive': `${baseURL}/vector_illustration_depressive.svg`,
255-
'vector_illustration/editorial': `${baseURL}/vector_illustration_editorial.svg`,
256-
'vector_illustration/emotional_flat': `${baseURL}/vector_illustration_emotional_flat.svg`,
257-
'vector_illustration/infographical': `${baseURL}/vector_illustration_infographical.svg`,
258-
'vector_illustration/marker_outline': `${baseURL}/vector_illustration_marker_outline.svg`,
259-
'vector_illustration/mosaic': `${baseURL}/vector_illustration_mosaic.svg`,
260-
'vector_illustration/naivector': `${baseURL}/vector_illustration_naivector.svg`,
261-
'vector_illustration/roundish_flat': `${baseURL}/vector_illustration_roundish_flat.svg`,
262-
'vector_illustration/segmented_colors': `${baseURL}/vector_illustration_segmented_colors.svg`,
263-
'vector_illustration/sharp_contrast': `${baseURL}/vector_illustration_sharp_contrast.svg`,
264-
'vector_illustration/thin': `${baseURL}/vector_illustration_thin.svg`,
265-
'vector_illustration/vector_photo': `${baseURL}/vector_illustration_vector_photo.svg`,
266-
'vector_illustration/vivid_shapes': `${baseURL}/vector_illustration_vivid_shapes.svg`,
267-
'vector_illustration/engraving': `${baseURL}/vector_illustration_engraving.svg`,
268-
'vector_illustration/line_art': `${baseURL}/vector_illustration_line_art.svg`,
269-
'vector_illustration/line_circuit': `${baseURL}/vector_illustration_line_circuit.svg`,
270-
'vector_illustration/linocut': `${baseURL}/vector_illustration_linocut.svg`,
271-
'realistic_image/evening_light': `${baseURL}/realistic_image_evening_light.webp`,
272-
'realistic_image/faded_nostalgia': `${baseURL}/realistic_image_faded_nostalgia.webp`,
273-
'realistic_image/forest_life': `${baseURL}/realistic_image_forest_life.webp`,
274-
'realistic_image/mystic_naturalism': `${baseURL}/realistic_image_mystic_naturalism.webp`,
275-
'realistic_image/natural_tones': `${baseURL}/realistic_image_natural_tones.webp`,
276-
'realistic_image/organic_calm': `${baseURL}/realistic_image_organic_calm.webp`,
277-
'realistic_image/real_life_glow': `${baseURL}/realistic_image_real_life_glow.webp`,
278-
'realistic_image/retro_realism': `${baseURL}/realistic_image_retro_realism.webp`,
279-
'realistic_image/retro_snapshot': `${baseURL}/realistic_image_retro_snapshot.webp`,
280-
'realistic_image/urban_drama': `${baseURL}/realistic_image_urban_drama.webp`,
281-
'realistic_image/village_realism': `${baseURL}/realistic_image_village_realism.webp`,
282-
'realistic_image/warm_folk': `${baseURL}/realistic_image_warm_folk.webp`,
283-
'digital_illustration/antiquarian': `${baseURL}/digital_illustration_antiquarian.webp`,
284-
'digital_illustration/bold_fantasy': `${baseURL}/digital_illustration_bold_fantasy.webp`,
285-
'digital_illustration/child_book': `${baseURL}/digital_illustration_child_book.webp`,
286-
'digital_illustration/child_books': `${baseURL}/digital_illustration_child_books.webp`,
287-
'digital_illustration/cover': `${baseURL}/digital_illustration_cover.webp`,
288-
'digital_illustration/crosshatch': `${baseURL}/digital_illustration_crosshatch.webp`,
289-
'digital_illustration/digital_engraving': `${baseURL}/digital_illustration_digital_engraving.webp`,
290-
'digital_illustration/expressionism': `${baseURL}/digital_illustration_expressionism.webp`,
291-
'digital_illustration/freehand_details': `${baseURL}/digital_illustration_freehand_details.webp`,
292-
'digital_illustration/grain_20': `${baseURL}/digital_illustration_grain_20.webp`,
293-
'digital_illustration/graphic_intensity': `${baseURL}/digital_illustration_graphic_intensity.webp`,
294-
'digital_illustration/hard_comics': `${baseURL}/digital_illustration_hard_comics.webp`,
295-
'digital_illustration/long_shadow': `${baseURL}/digital_illustration_long_shadow.webp`,
296-
'digital_illustration/modern_folk': `${baseURL}/digital_illustration_modern_folk.webp`,
297-
'digital_illustration/multicolor': `${baseURL}/digital_illustration_multicolor.webp`,
298-
'digital_illustration/neon_calm': `${baseURL}/digital_illustration_neon_calm.webp`,
299-
'digital_illustration/noir': `${baseURL}/digital_illustration_noir.webp`,
300-
'digital_illustration/nostalgic_pastel': `${baseURL}/digital_illustration_nostalgic_pastel.webp`,
301-
'digital_illustration/outline_details': `${baseURL}/digital_illustration_outline_details.webp`,
302-
'digital_illustration/pastel_gradient': `${baseURL}/digital_illustration_pastel_gradient.webp`,
303-
'digital_illustration/pastel_sketch': `${baseURL}/digital_illustration_pastel_sketch.webp`,
304-
'digital_illustration/pop_art': `${baseURL}/digital_illustration_pop_art.webp`,
305-
'digital_illustration/pop_renaissance': `${baseURL}/digital_illustration_pop_renaissance.webp`,
306-
'digital_illustration/street_art': `${baseURL}/digital_illustration_street_art.webp`,
307-
'digital_illustration/tablet_sketch': `${baseURL}/digital_illustration_tablet_sketch.webp`,
308-
'digital_illustration/urban_glow': `${baseURL}/digital_illustration_urban_glow.webp`,
309-
'digital_illustration/urban_sketching': `${baseURL}/digital_illustration_urban_sketching.webp`,
310-
'digital_illustration/vanilla_dreams': `${baseURL}/digital_illustration_vanilla_dreams.webp`,
311-
'digital_illustration/young_adult_book': `${baseURL}/digital_illustration_young_adult_book.webp`,
312-
'digital_illustration/young_adult_book_2': `${baseURL}/digital_illustration_young_adult_book_2.webp`,
229+
'realistic_image': `${baseURL}realistic_image.webp`,
230+
'digital_illustration': `${baseURL}digital_illustration.webp`,
231+
'realistic_image/b_and_w': `${baseURL}realistic_image_black_&_white.webp`,
232+
'realistic_image/hard_flash': `${baseURL}realistic_image_hard_flash.webp`,
233+
'realistic_image/hdr': `${baseURL}realistic_image_hdr.webp`,
234+
'realistic_image/natural_light': `${baseURL}realistic_image_natural_light.webp`,
235+
'realistic_image/studio_portrait': `${baseURL}realistic_image_studio_portrait.webp`,
236+
'realistic_image/enterprise': `${baseURL}realistic_image_enterprise.webp`,
237+
'realistic_image/motion_blur': `${baseURL}realistic_image_motion_blur.webp`,
238+
'digital_illustration/pixel_art': `${baseURL}digital_illustration_pixel_art.webp`,
239+
'digital_illustration/hand_drawn': `${baseURL}digital_illustration_hand_drawn.webp`,
240+
'digital_illustration/grain': `${baseURL}digital_illustration_grain.webp`,
241+
'digital_illustration/infantile_sketch': `${baseURL}digital_illustration_infantile_sketch.webp`,
242+
'digital_illustration/2d_art_poster': `${baseURL}digital_illustration_2d_art_poster.webp`,
243+
'digital_illustration/handmade_3d': `${baseURL}digital_illustration_handmade_3d.webp`,
244+
'digital_illustration/hand_drawn_outline': `${baseURL}digital_illustration_handdrawn_outline.webp`,
245+
'digital_illustration/engraving_color': `${baseURL}digital_illustration_engraving_color.webp`,
246+
'digital_illustration/2d_art_poster_2': `${baseURL}digital_illustration_2d_artposter_2.webp`,
247+
'vector_illustration': `${baseURL}vector_illustration.svg`,
248+
'vector_illustration/bold_stroke': `${baseURL}vector_illustration_bold_stroke.svg`,
249+
'vector_illustration/chemistry': `${baseURL}vector_illustration_chemistry.svg`,
250+
'vector_illustration/colored_stencil': `${baseURL}vector_illustration_colored_stencil.svg`,
251+
'vector_illustration/contour_pop_art': `${baseURL}vector_illustration_contour_pop_art.svg`,
252+
'vector_illustration/cosmics': `${baseURL}vector_illustration_cosmics.svg`,
253+
'vector_illustration/cutout': `${baseURL}vector_illustration_cutout.svg`,
254+
'vector_illustration/depressive': `${baseURL}vector_illustration_depressive.svg`,
255+
'vector_illustration/editorial': `${baseURL}vector_illustration_editorial.svg`,
256+
'vector_illustration/emotional_flat': `${baseURL}vector_illustration_emotional_flat.svg`,
257+
'vector_illustration/infographical': `${baseURL}vector_illustration_infographical.svg`,
258+
'vector_illustration/marker_outline': `${baseURL}vector_illustration_marker_outline.svg`,
259+
'vector_illustration/mosaic': `${baseURL}vector_illustration_mosaic.svg`,
260+
'vector_illustration/naivector': `${baseURL}vector_illustration_naivector.svg`,
261+
'vector_illustration/roundish_flat': `${baseURL}vector_illustration_roundish_flat.svg`,
262+
'vector_illustration/segmented_colors': `${baseURL}vector_illustration_segmented_colors.svg`,
263+
'vector_illustration/sharp_contrast': `${baseURL}vector_illustration_sharp_contrast.svg`,
264+
'vector_illustration/thin': `${baseURL}vector_illustration_thin.svg`,
265+
'vector_illustration/vector_photo': `${baseURL}vector_illustration_vector_photo.svg`,
266+
'vector_illustration/vivid_shapes': `${baseURL}vector_illustration_vivid_shapes.svg`,
267+
'vector_illustration/engraving': `${baseURL}vector_illustration_engraving.svg`,
268+
'vector_illustration/line_art': `${baseURL}vector_illustration_line_art.svg`,
269+
'vector_illustration/line_circuit': `${baseURL}vector_illustration_line_circuit.svg`,
270+
'vector_illustration/linocut': `${baseURL}vector_illustration_linocut.svg`,
271+
'realistic_image/evening_light': `${baseURL}realistic_image_evening_light.webp`,
272+
'realistic_image/faded_nostalgia': `${baseURL}realistic_image_faded_nostalgia.webp`,
273+
'realistic_image/forest_life': `${baseURL}realistic_image_forest_life.webp`,
274+
'realistic_image/mystic_naturalism': `${baseURL}realistic_image_mystic_naturalism.webp`,
275+
'realistic_image/natural_tones': `${baseURL}realistic_image_natural_tones.webp`,
276+
'realistic_image/organic_calm': `${baseURL}realistic_image_organic_calm.webp`,
277+
'realistic_image/real_life_glow': `${baseURL}realistic_image_real_life_glow.webp`,
278+
'realistic_image/retro_realism': `${baseURL}realistic_image_retro_realism.webp`,
279+
'realistic_image/retro_snapshot': `${baseURL}realistic_image_retro_snapshot.webp`,
280+
'realistic_image/urban_drama': `${baseURL}realistic_image_urban_drama.webp`,
281+
'realistic_image/village_realism': `${baseURL}realistic_image_village_realism.webp`,
282+
'realistic_image/warm_folk': `${baseURL}realistic_image_warm_folk.webp`,
283+
'digital_illustration/antiquarian': `${baseURL}digital_illustration_antiquarian.webp`,
284+
'digital_illustration/bold_fantasy': `${baseURL}digital_illustration_bold_fantasy.webp`,
285+
'digital_illustration/child_book': `${baseURL}digital_illustration_child_book.webp`,
286+
'digital_illustration/child_books': `${baseURL}digital_illustration_child_books.webp`,
287+
'digital_illustration/cover': `${baseURL}digital_illustration_cover.webp`,
288+
'digital_illustration/crosshatch': `${baseURL}digital_illustration_crosshatch.webp`,
289+
'digital_illustration/digital_engraving': `${baseURL}digital_illustration_digital_engraving.webp`,
290+
'digital_illustration/expressionism': `${baseURL}digital_illustration_expressionism.webp`,
291+
'digital_illustration/freehand_details': `${baseURL}digital_illustration_freehand_details.webp`,
292+
'digital_illustration/grain_20': `${baseURL}digital_illustration_grain_20.webp`,
293+
'digital_illustration/graphic_intensity': `${baseURL}digital_illustration_graphic_intensity.webp`,
294+
'digital_illustration/hard_comics': `${baseURL}digital_illustration_hard_comics.webp`,
295+
'digital_illustration/long_shadow': `${baseURL}digital_illustration_long_shadow.webp`,
296+
'digital_illustration/modern_folk': `${baseURL}digital_illustration_modern_folk.webp`,
297+
'digital_illustration/multicolor': `${baseURL}digital_illustration_multicolor.webp`,
298+
'digital_illustration/neon_calm': `${baseURL}digital_illustration_neon_calm.webp`,
299+
'digital_illustration/noir': `${baseURL}digital_illustration_noir.webp`,
300+
'digital_illustration/nostalgic_pastel': `${baseURL}digital_illustration_nostalgic_pastel.webp`,
301+
'digital_illustration/outline_details': `${baseURL}digital_illustration_outline_details.webp`,
302+
'digital_illustration/pastel_gradient': `${baseURL}digital_illustration_pastel_gradient.webp`,
303+
'digital_illustration/pastel_sketch': `${baseURL}digital_illustration_pastel_sketch.webp`,
304+
'digital_illustration/pop_art': `${baseURL}digital_illustration_pop_art.webp`,
305+
'digital_illustration/pop_renaissance': `${baseURL}digital_illustration_pop_renaissance.webp`,
306+
'digital_illustration/street_art': `${baseURL}digital_illustration_street_art.webp`,
307+
'digital_illustration/tablet_sketch': `${baseURL}digital_illustration_tablet_sketch.webp`,
308+
'digital_illustration/urban_glow': `${baseURL}digital_illustration_urban_glow.webp`,
309+
'digital_illustration/urban_sketching': `${baseURL}digital_illustration_urban_sketching.webp`,
310+
'digital_illustration/vanilla_dreams': `${baseURL}digital_illustration_vanilla_dreams.webp`,
311+
'digital_illustration/young_adult_book': `${baseURL}digital_illustration_young_adult_book.webp`,
312+
'digital_illustration/young_adult_book_2': `${baseURL}digital_illustration_young_adult_book_2.webp`,
313313
// Icon style thumbnails (using SVG format like vector styles)
314-
'icon/broken_line': `${baseURL}/icon_broken_line.svg`,
315-
'icon/colored_outline': `${baseURL}/icon_colored_outline.svg`,
316-
'icon/colored_shapes': `${baseURL}/icon_colored_shapes.svg`,
317-
'icon/colored_shapes_gradient': `${baseURL}/icon_colored_shapes_gradient.svg`,
318-
'icon/doodle_fill': `${baseURL}/icon_doodle_fill.svg`,
319-
'icon/doodle_offset_fill': `${baseURL}/icon_doodle_offset_fill.svg`,
320-
'icon/offset_fill': `${baseURL}/icon_offset_fill.svg`,
321-
'icon/outline': `${baseURL}/icon_outline.svg`,
322-
'icon/outline_gradient': `${baseURL}/icon_outline_gradient.svg`,
323-
'icon/uneven_fill': `${baseURL}/icon_uneven_fill.svg`,
314+
'icon/broken_line': `${baseURL}icon_broken_line.svg`,
315+
'icon/colored_outline': `${baseURL}icon_colored_outline.svg`,
316+
'icon/colored_shapes': `${baseURL}icon_colored_shapes.svg`,
317+
'icon/colored_shapes_gradient': `${baseURL}icon_colored_shapes_gradient.svg`,
318+
'icon/doodle_fill': `${baseURL}icon_doodle_fill.svg`,
319+
'icon/doodle_offset_fill': `${baseURL}icon_doodle_offset_fill.svg`,
320+
'icon/offset_fill': `${baseURL}icon_offset_fill.svg`,
321+
'icon/outline': `${baseURL}icon_outline.svg`,
322+
'icon/outline_gradient': `${baseURL}icon_outline_gradient.svg`,
323+
'icon/uneven_fill': `${baseURL}icon_uneven_fill.svg`,
324324
})
325325

326326
export function getStyleThumbnail(

packages/plugin-ai-image-generation-web/src/fal-ai/Recraft20b.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { Icons, CustomAssetSource } from '@imgly/plugin-utils';
22
import {
33
type Provider,
44
getPanelId,
5-
createTranslationCallback
5+
createTranslationCallback,
6+
normalizeBaseURL
67
} from '@imgly/plugin-ai-generation-web';
78
import Recraft20bSchema from './Recraft20b.json';
89
import CreativeEditorSDK, { AssetResult } from '@cesdk/cesdk-js';
@@ -73,9 +74,11 @@ function getProvider(
7374
const styleImageAssetSourceId = `${modelKey}/styles/image`;
7475
const styleVectorAssetSourceId = `${modelKey}/styles/vector`;
7576
const styleIconAssetSourceId = `${modelKey}/styles/icon`;
76-
const baseURL =
77+
// Normalize baseURL to ensure exactly one trailing slash
78+
const baseURL = normalizeBaseURL(
7779
config.baseURL ??
78-
'https://cdn.img.ly/assets/plugins/plugin-ai-image-generation-web/v1/recraft-v3/';
80+
'https://cdn.img.ly/assets/plugins/plugin-ai-image-generation-web/v1/recraft-v3/'
81+
);
7982

8083
// Initialize feature flags for style groups
8184
cesdk.feature.enable(

0 commit comments

Comments
 (0)