@fluid-app/portal-sdk 0.1.509 → 0.1.511

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShareablesScreen-CjbwoZdH.mjs","names":["X","K","P","u","centerCrop","makeAspectCrop","DOMPurify","version","DOMPurify","DEFAULT_IMAGE","PAGE_SIZE","useIsMobile","ImageIcon","FacebookIcon","InstagramIcon","FacebookIcon","InstagramIcon","DOMPurify","ImageIcon","MarketingAssetsGrid","DEFAULT_IMAGE","PAGE_SIZE","useIsMobile","useIsMobile","PAGE_SIZE","useIsMobile","DEFAULT_IMAGE","CoreFilePickerApiProvider","z","DialogPrimitive.Content","defaultFiltersState","PAGE_SIZE","ReactCrop","formatFileSize","DialogPrimitive.Content","defaultDamFilters","PAGE_SIZE","useIsMobile","useIsMobile","useIsMobile","useIsMobile","DEFAULT_IMAGE","useIsMobile","PAGE_SIZE","useIsMobile","DEFAULT_IMAGE","DEFAULT_IMAGE","useIsMobile","useIsMobile","PAGE_SIZE","DEFAULT_IMAGE","useIsMobile","useIsMobile"],"sources":["../../../file-picker/core/src/query-keys.ts","../../../file-picker/core/src/types.ts","../../../file-picker/core/src/schemas/file-picker-config.ts","../../../file-picker/core/src/constants/filter-options.ts","../../../../node_modules/.pnpm/react-image-crop@11.0.10_react@19.2.4/node_modules/react-image-crop/dist/index.js","../../../file-picker/core/src/utils/image-crop.ts","../../../file-picker/core/src/utils/dam-asset.ts","../../../file-picker/core/src/utils/dam-url.ts","../../../file-picker/core/src/utils/filename-sanitizer.ts","../../../shareables/ui/src/context.tsx","../../../../node_modules/.pnpm/dompurify@3.3.3/node_modules/dompurify/dist/purify.es.mjs","../../../../node_modules/.pnpm/isomorphic-dompurify@3.0.0/node_modules/isomorphic-dompurify/dist/browser.mjs","../../../shareables/ui/src/utils/strip-tags.ts","../../../shareables/ui/src/utils/social-sharing.ts","../../../shareables/ui/src/utils/os-detection.ts","../../../products/api-client/src/namespaces/products.ts","../../../shareables/core/src/translation-api-context.ts","../../../shareables/ui/src/components/MediaShare/ShareItemCard.tsx","../../../shareables/ui/src/components/MediaShare/products/ShareProductCard.tsx","../../../shareables/ui/src/components/shared/ShareableListLayout.tsx","../../../shareables/ui/src/components/shared/ShareableListRow.tsx","../../../shareables/ui/src/components/shared/ViewModeToggle.tsx","../../../shareables/ui/src/hooks/use-infinite-list-sentinel.ts","../../../shareables/ui/src/components/screens/ProductsScreen.tsx","../../../shareables/ui/src/components/SharePage/SharePageImageDisplay.tsx","../../../shareables/ui/src/components/QrCodeDisplay.tsx","../../../shareables/ui/src/components/icons/social-icons.tsx","../../../shareables/ui/src/components/SharePage/ShareLinkSection.tsx","../../../shareables/ui/src/utils/download-file.ts","../../../shareables/ui/src/components/SharePage/AssetActions.tsx","../../../shareables/ui/src/components/ShareablesModal/MarketingAssetsGrid.tsx","../../../shareables/ui/src/components/shared/ShareableDetailLayout.tsx","../../../shareables/ui/src/components/screens/ProductDetailScreen.tsx","../../../shareables/ui/src/components/MediaShare/ProductSharablesCarousel.tsx","../../../shareables/ui/src/components/media/AddToPlaylistDialog.tsx","../../../shareables/ui/src/components/media/MediaRowActionsMenu.tsx","../../../shareables/ui/src/components/OwnerFilterTabs.tsx","../../../shareables/ui/src/components/shared/ShareableFilterBarLayout.tsx","../../../shareables/ui/src/components/screens/MediaListingScreen.tsx","../../../shareables/ui/src/components/SharePage/TaggedProductsList.tsx","../../../shareables/ui/src/components/screens/MediaDetailScreen.tsx","../../../file-picker/ui/src/context/FilePickerApiContext.tsx","../../../file-picker/ui/src/context/FilePickerContext.tsx","../../../file-picker/ui/src/utils/brand-icons.tsx","../../../file-picker/ui/src/hooks/use-debounced-search.ts","../../../file-picker/ui/src/hooks/use-dam-library.ts","../../../file-picker/ui/src/hooks/use-is-mobile.ts","../../../file-picker/ui/src/components/AddFolderModal.tsx","../../../file-picker/ui/src/components/AssetActions.tsx","../../../file-picker/ui/src/utils/icons.ts","../../../file-picker/ui/src/components/DamLibraryAsset/AssetThumbnail.tsx","../../../file-picker/ui/src/components/DamLibraryAsset/SelectionIndexBadge.tsx","../../../file-picker/ui/src/components/DamLibraryAsset/AssetActionsMenu.tsx","../../../file-picker/ui/src/components/LibraryAssetGrid.tsx","../../../file-picker/ui/src/components/DamLibrary.tsx","../../../file-picker/ui/src/components/FilePickerSidebar.tsx","../../../file-picker/ui/src/components/FilePickerFiltersPanel.tsx","../../../file-picker/ui/src/components/FilePickerHeader.tsx","../../../file-picker/ui/src/components/MediaTab.tsx","../../../file-picker/ui/src/components/ContentBrowserTab.tsx","../../../file-picker/ui/src/components/PagesTab.tsx","../../../file-picker/ui/src/components/ProductsTab.tsx","../../../file-picker/ui/src/components/EnrollmentPacksTab.tsx","../../../file-picker/ui/src/hooks/use-unsplash-picker.ts","../../../file-picker/ui/src/components/UnsplashPicker.tsx","../../../file-picker/ui/src/hooks/use-url-upload.ts","../../../file-picker/ui/src/components/UrlUpload.tsx","../../../file-picker/ui/src/hooks/use-computer-upload.ts","../../../file-picker/ui/src/components/ImageCropModal.tsx","../../../file-picker/ui/src/components/ComputerUpload.tsx","../../../file-picker/ui/src/components/FilePickerContent.tsx","../../../file-picker/ui/src/components/SelectionPanel.tsx","../../../file-picker/ui/src/components/FilePicker.tsx","../../../shareables/ui/src/hooks/use-thumbnail-meta.ts","../../../shareables/ui/src/components/playlists/RichTextEditor.tsx","../../../shareables/ui/src/components/media/MediaProductTagging.tsx","../../../shareables/ui/src/components/media/CtaModal.tsx","../../../shareables/ui/src/components/media/VideoThumbnailSelector.tsx","../../../shareables/ui/src/components/screens/MediaEditScreen.tsx","../../../shareables/ui/src/components/screens/MediaCreateScreen.tsx","../../../shareables/ui/src/components/playlists/PlaylistCard.tsx","../../../shareables/ui/src/components/playlists/BulkSelectionBar.tsx","../../../shareables/ui/src/components/screens/PlaylistsListingScreen.tsx","../../../shareables/ui/src/constants.ts","../../../shareables/ui/src/components/SharePage/PlaylistItemsList.tsx","../../../shareables/ui/src/components/screens/PlaylistDetailScreen.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistFormProvider.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistItemsContext.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistFormHeader.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistFormDetails.tsx","../../../shareables/ui/src/components/playlists/form/SortableRow.tsx","../../../shareables/ui/src/components/playlists/form/SortableTable.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistItemsSection.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistOpenGraphPreview.tsx","../../../shareables/ui/src/components/screens/PlaylistCreateScreen.tsx","../../../shareables/ui/src/components/screens/FilesListingScreen.tsx","../../../shareables/ui/src/components/screens/PagesListingScreen.tsx","../../../shareables/ui/src/components/screens/PageDetailScreen.tsx","../../../shareables/ui/src/components/ShareablesApp.tsx","../src/providers/ShareablesTranslationBridge.tsx","../src/screens/shareables-source-context.ts","../src/screens/ShareablesScreen.tsx"],"sourcesContent":["export const damQueryKeys: {\n all: readonly [\"dam\"];\n assets: () => readonly [\"dam\", \"assets\"];\n query: (params: unknown) => readonly [\"dam\", \"query\", unknown];\n} = {\n all: [\"dam\"] as const,\n assets: (): readonly [\"dam\", \"assets\"] => [\"dam\", \"assets\"] as const,\n query: (params: unknown): readonly [\"dam\", \"query\", unknown] =>\n [\"dam\", \"query\", params] as const,\n};\n","export interface FilePickerConfig {\n accept?: string[];\n maxFiles?: number;\n maxSize?: number;\n showProgress?: boolean;\n showVariants?: boolean;\n fileTypeFilter?: string[];\n allowedMethods?: PickerMethod[];\n /**\n * When true, selections from all method tabs accumulate into a single list\n * (deduped by asset_code) instead of being cleared on tab switch. Used by\n * the playlist editor to let users pick across multiple content types in\n * a single session. Defaults to false to preserve existing per-method UX.\n */\n unifiedSelection?: boolean;\n}\n\nexport interface DamAsset {\n id: number; // Required numeric ID from API\n asset_code: string;\n name: string;\n category: string;\n variants: DamVariant[];\n default_variant_url?: string;\n created_at?: string;\n}\n\nexport interface DamVariant {\n id: string;\n url: string;\n file_name: string;\n mime_type: string;\n metadata: Record<string, unknown>;\n ready: boolean;\n tags: string[];\n}\n\nexport interface DamAssetPath {\n id: number;\n path: string;\n canonical: boolean;\n}\n\nexport interface DamTree {\n [key: string]: DamTree | DamAsset;\n}\n\nexport interface FilePickerResult {\n asset_id: number; // Numeric ID required by API\n asset_code: string;\n file_path: string;\n file_url: string;\n thumbnail_url?: string;\n description?: string;\n metadata: {\n file_name: string;\n mime_type: string;\n file_size?: number;\n dimensions?: {\n width: number;\n height: number;\n };\n };\n upload_status: \"uploading\" | \"ready\" | \"error\";\n variant_id?: string;\n}\n\nexport interface UploadProgress {\n file_name: string;\n progress: number;\n status: \"uploading\" | \"processing\" | \"ready\" | \"error\";\n error?: string;\n}\n\n/**\n * Discriminator for the picker's tabs. The original `UploadMethod` name\n * was a misnomer — only `upload`/`computer`/`url`/`unsplash`/cloud-storage\n * represent file uploads; the rest (`dam`/`media`/`pages`/`products`/\n * `enrollment_packs`) are existing-content browse tabs. `PickerMethod`\n * captures both flavors honestly without forcing a structural split.\n */\nexport type PickerMethod =\n | \"upload\"\n | \"computer\"\n | \"dam\"\n | \"media\"\n | \"pages\"\n | \"products\"\n | \"enrollment_packs\"\n | \"url\"\n | \"unsplash\"\n | \"google-drive\"\n | \"instagram\"\n | \"facebook\"\n | \"tiktok\"\n | \"dropbox\";\n\n/**\n * Whether each method is worth offering in a switcher. Keyed on\n * {@link PickerMethod} rather than listed, so adding a variant to the union is\n * a compile error here until someone decides which way it goes.\n *\n * `computer` is false because it renders the same tab as `upload` and nothing\n * builds a `computer` entry to switch to. The cloud-storage and social sources\n * are false because their tab bodies are still placeholders.\n */\nconst PICKER_METHOD_IMPLEMENTED: Record<PickerMethod, boolean> = {\n dam: true,\n media: true,\n pages: true,\n products: true,\n enrollment_packs: true,\n upload: true,\n url: true,\n unsplash: true,\n computer: false,\n \"google-drive\": false,\n instagram: false,\n facebook: false,\n tiktok: false,\n dropbox: false,\n};\n\n/**\n * Methods the picker's switchers offer, derived from\n * {@link PICKER_METHOD_IMPLEMENTED} so the two cannot drift.\n */\nexport const IMPLEMENTED_PICKER_METHODS: readonly PickerMethod[] = (\n Object.entries(PICKER_METHOD_IMPLEMENTED) as Array<[PickerMethod, boolean]>\n)\n .filter(([, implemented]) => implemented)\n .map(([method]) => method);\n\n/** @deprecated Renamed to {@link PickerMethod}. */\nexport type UploadMethod = PickerMethod;\n\n// Image cropping types\nexport interface CropArea {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface CropSettings {\n aspect?: number;\n unit?: \"px\" | \"%\";\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n}\n\nexport interface ImageCropModalProps {\n isOpen: boolean;\n onClose: () => void;\n onCrop: (croppedFile: File) => void;\n imageFile: File;\n aspectRatio?: number;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Picker data-source contracts (used by the non-file content tabs:\n// pages, products, enrollment_packs). Hosts implement these; tabs consume.\n// ---------------------------------------------------------------------------\n\nexport interface PickerListParams {\n search?: string;\n cursor?: string | null;\n limit?: number;\n}\n\nexport interface PickerListResult<T> {\n items: T[];\n nextCursor: string | null;\n}\n\nexport interface PickerPageItem {\n id: number;\n title: string;\n image_url?: string | null;\n}\n\nexport interface PickerProductItem {\n id: number;\n name: string;\n image_url?: string | null;\n price?: string | null;\n}\n\nexport interface PickerEnrollmentPackItem {\n id: number;\n title: string;\n image_url?: string | null;\n}\n\nexport interface PickerSource<T> {\n list: (params: PickerListParams) => Promise<PickerListResult<T>>;\n /**\n * Stable identity for this source's backing dataset (for example, a locale\n * or adapter version). Content tabs include it in query/cache keys and reset\n * accumulated pagination when it changes.\n */\n cacheKey?: string;\n /**\n * When false, the picker tab hides its search input — the underlying\n * endpoint doesn't accept a title/search filter, so typing in the box\n * would round-trip to the server and return the same results, giving\n * users false feedback. Defaults to true.\n */\n supportsSearch?: boolean;\n}\n","import { z } from \"zod\";\n\nconst pickerMethodLiterals = [\n \"upload\",\n \"computer\",\n \"dam\",\n \"media\",\n \"pages\",\n \"products\",\n \"enrollment_packs\",\n \"url\",\n \"unsplash\",\n \"google-drive\",\n \"instagram\",\n \"facebook\",\n \"tiktok\",\n \"dropbox\",\n] as const;\n\ntype PickerMethodLiteral = (typeof pickerMethodLiterals)[number];\n\ntype FilePickerConfigInput = {\n accept?: string[];\n maxFiles?: number;\n maxSize?: number;\n showProgress?: boolean;\n showVariants?: boolean;\n fileTypeFilter?: string[];\n allowedMethods?: PickerMethodLiteral[];\n unifiedSelection?: boolean;\n};\n\ntype FilePickerConfigOutput = {\n accept?: string[];\n maxFiles: number;\n maxSize?: number;\n showProgress: boolean;\n showVariants: boolean;\n fileTypeFilter?: string[];\n allowedMethods?: PickerMethodLiteral[];\n unifiedSelection: boolean;\n};\n\nexport const filePickerConfigSchema: z.ZodType<FilePickerConfigOutput> =\n z.object({\n accept: z.array(z.string()).optional(),\n maxFiles: z.number().min(1).optional().default(1),\n maxSize: z.number().positive().optional(),\n showProgress: z.boolean().optional().default(true),\n showVariants: z.boolean().optional().default(false),\n fileTypeFilter: z.array(z.string()).optional(),\n allowedMethods: z.array(z.enum(pickerMethodLiterals)).optional(),\n unifiedSelection: z.boolean().optional().default(false),\n });\n\nexport type { FilePickerConfigInput, FilePickerConfigOutput };\n","export const FILTER_VALUE_ALL = \"__all__\";\n\nexport const DAM_TYPE_FILTER_OPTIONS: { value: string; label: string }[] = [\n { value: FILTER_VALUE_ALL, label: \"All types\" },\n { value: \"images\", label: \"Image\" },\n { value: \"videos\", label: \"Video\" },\n { value: \"audio\", label: \"Audio\" },\n { value: \"text\", label: \"Text\" },\n { value: \"documents\", label: \"Document\" },\n { value: \"other\", label: \"Others\" },\n];\n\nexport function getCategoryFromTypeFilter(value: string): string | undefined {\n if (!value) return undefined;\n return value;\n}\n\nexport const DATE_FILTER_OPTIONS: readonly { value: string; label: string }[] =\n [\n { value: FILTER_VALUE_ALL, label: \"Any time\" },\n { value: \"24h\", label: \"Past 24 hours\" },\n { value: \"7d\", label: \"Past 7 days\" },\n { value: \"30d\", label: \"Past 30 days\" },\n { value: \"90d\", label: \"Past 90 days\" },\n ];\n\nexport const UPLOADED_BY_OPTIONS: readonly { value: string; label: string }[] =\n [\n { value: FILTER_VALUE_ALL, label: \"Anyone\" },\n { value: \"me\", label: \"Me\" },\n ];\n\nexport const USAGE_STATUS_OPTIONS: readonly {\n value: string;\n label: string;\n}[] = [\n { value: FILTER_VALUE_ALL, label: \"Any\" },\n { value: \"used\", label: \"Used\" },\n { value: \"unused\", label: \"Unused\" },\n];\n\nexport const SOURCE_OPTIONS: readonly { value: string; label: string }[] = [\n { value: FILTER_VALUE_ALL, label: \"Any\" },\n { value: \"upload\", label: \"Upload\" },\n { value: \"dam\", label: \"DAM\" },\n];\n\n// Need to add \"newest\" and \"oldest\" options later after we have the created_at field in the API\nexport type SortOptionId = \"name_asc\" | \"name_desc\";\n\nexport const SORT_OPTIONS: Array<{\n id: SortOptionId;\n label: string;\n sortBy: string;\n sortDirection: \"asc\" | \"desc\";\n}> = [\n { id: \"name_asc\", label: \"Name A–Z\", sortBy: \"name\", sortDirection: \"asc\" },\n { id: \"name_desc\", label: \"Name Z–A\", sortBy: \"name\", sortDirection: \"desc\" },\n];\n","var _ = Object.defineProperty;\nvar $ = (a, h, e) => h in a ? _(a, h, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[h] = e;\nvar m = (a, h, e) => $(a, typeof h != \"symbol\" ? h + \"\" : h, e);\nimport u, { PureComponent as K, createRef as P } from \"react\";\nconst E = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n unit: \"px\"\n}, b = (a, h, e) => Math.min(Math.max(a, h), e), H = (...a) => a.filter((h) => h && typeof h == \"string\").join(\" \"), X = (a, h) => a === h || a.width === h.width && a.height === h.height && a.x === h.x && a.y === h.y && a.unit === h.unit;\nfunction B(a, h, e, n) {\n const t = D(a, e, n);\n return a.width && (t.height = t.width / h), a.height && (t.width = t.height * h), t.y + t.height > n && (t.height = n - t.y, t.width = t.height * h), t.x + t.width > e && (t.width = e - t.x, t.height = t.width / h), a.unit === \"%\" ? v(t, e, n) : t;\n}\nfunction L(a, h, e) {\n const n = D(a, h, e);\n return n.x = (h - n.width) / 2, n.y = (e - n.height) / 2, a.unit === \"%\" ? v(n, h, e) : n;\n}\nfunction v(a, h, e) {\n return a.unit === \"%\" ? { ...E, ...a, unit: \"%\" } : {\n unit: \"%\",\n x: a.x ? a.x / h * 100 : 0,\n y: a.y ? a.y / e * 100 : 0,\n width: a.width ? a.width / h * 100 : 0,\n height: a.height ? a.height / e * 100 : 0\n };\n}\nfunction D(a, h, e) {\n return a.unit ? a.unit === \"px\" ? { ...E, ...a, unit: \"px\" } : {\n unit: \"px\",\n x: a.x ? a.x * h / 100 : 0,\n y: a.y ? a.y * e / 100 : 0,\n width: a.width ? a.width * h / 100 : 0,\n height: a.height ? a.height * e / 100 : 0\n } : { ...E, ...a, unit: \"px\" };\n}\nfunction k(a, h, e, n, t, d = 0, r = 0, o = n, w = t) {\n const i = { ...a };\n let s = Math.min(d, n), c = Math.min(r, t), g = Math.min(o, n), p = Math.min(w, t);\n h && (h > 1 ? (s = r ? r * h : s, c = s / h, g = o * h) : (c = d ? d / h : c, s = c * h, p = w / h)), i.y < 0 && (i.height = Math.max(i.height + i.y, c), i.y = 0), i.x < 0 && (i.width = Math.max(i.width + i.x, s), i.x = 0);\n const l = n - (i.x + i.width);\n l < 0 && (i.x = Math.min(i.x, n - s), i.width += l);\n const C = t - (i.y + i.height);\n if (C < 0 && (i.y = Math.min(i.y, t - c), i.height += C), i.width < s && ((e === \"sw\" || e == \"nw\") && (i.x -= s - i.width), i.width = s), i.height < c && ((e === \"nw\" || e == \"ne\") && (i.y -= c - i.height), i.height = c), i.width > g && ((e === \"sw\" || e == \"nw\") && (i.x -= g - i.width), i.width = g), i.height > p && ((e === \"nw\" || e == \"ne\") && (i.y -= p - i.height), i.height = p), h) {\n const y = i.width / i.height;\n if (y < h) {\n const f = Math.max(i.width / h, c);\n (e === \"nw\" || e == \"ne\") && (i.y -= f - i.height), i.height = f;\n } else if (y > h) {\n const f = Math.max(i.height * h, s);\n (e === \"sw\" || e == \"nw\") && (i.x -= f - i.width), i.width = f;\n }\n }\n return i;\n}\nfunction I(a, h, e, n) {\n const t = { ...a };\n return h === \"ArrowLeft\" ? n === \"nw\" ? (t.x -= e, t.y -= e, t.width += e, t.height += e) : n === \"w\" ? (t.x -= e, t.width += e) : n === \"sw\" ? (t.x -= e, t.width += e, t.height += e) : n === \"ne\" ? (t.y += e, t.width -= e, t.height -= e) : n === \"e\" ? t.width -= e : n === \"se\" && (t.width -= e, t.height -= e) : h === \"ArrowRight\" && (n === \"nw\" ? (t.x += e, t.y += e, t.width -= e, t.height -= e) : n === \"w\" ? (t.x += e, t.width -= e) : n === \"sw\" ? (t.x += e, t.width -= e, t.height -= e) : n === \"ne\" ? (t.y -= e, t.width += e, t.height += e) : n === \"e\" ? t.width += e : n === \"se\" && (t.width += e, t.height += e)), h === \"ArrowUp\" ? n === \"nw\" ? (t.x -= e, t.y -= e, t.width += e, t.height += e) : n === \"n\" ? (t.y -= e, t.height += e) : n === \"ne\" ? (t.y -= e, t.width += e, t.height += e) : n === \"sw\" ? (t.x += e, t.width -= e, t.height -= e) : n === \"s\" ? t.height -= e : n === \"se\" && (t.width -= e, t.height -= e) : h === \"ArrowDown\" && (n === \"nw\" ? (t.x += e, t.y += e, t.width -= e, t.height -= e) : n === \"n\" ? (t.y += e, t.height -= e) : n === \"ne\" ? (t.y += e, t.width -= e, t.height -= e) : n === \"sw\" ? (t.x -= e, t.width += e, t.height += e) : n === \"s\" ? t.height += e : n === \"se\" && (t.width += e, t.height += e)), t;\n}\nconst M = { capture: !0, passive: !1 };\nlet N = 0;\nconst x = class x extends K {\n constructor() {\n super(...arguments);\n m(this, \"docMoveBound\", !1);\n m(this, \"mouseDownOnCrop\", !1);\n m(this, \"dragStarted\", !1);\n m(this, \"evData\", {\n startClientX: 0,\n startClientY: 0,\n startCropX: 0,\n startCropY: 0,\n clientX: 0,\n clientY: 0,\n isResize: !0\n });\n m(this, \"componentRef\", P());\n m(this, \"mediaRef\", P());\n m(this, \"resizeObserver\");\n m(this, \"initChangeCalled\", !1);\n m(this, \"instanceId\", `rc-${N++}`);\n m(this, \"state\", {\n cropIsActive: !1,\n newCropIsBeingDrawn: !1\n });\n m(this, \"onCropPointerDown\", (e) => {\n const { crop: n, disabled: t } = this.props, d = this.getBox();\n if (!n)\n return;\n const r = D(n, d.width, d.height);\n if (t)\n return;\n e.cancelable && e.preventDefault(), this.bindDocMove(), this.componentRef.current.focus({ preventScroll: !0 });\n const o = e.target.dataset.ord, w = !!o;\n let i = e.clientX, s = e.clientY, c = r.x, g = r.y;\n if (o) {\n const p = e.clientX - d.x, l = e.clientY - d.y;\n let C = 0, y = 0;\n o === \"ne\" || o == \"e\" ? (C = p - (r.x + r.width), y = l - r.y, c = r.x, g = r.y + r.height) : o === \"se\" || o === \"s\" ? (C = p - (r.x + r.width), y = l - (r.y + r.height), c = r.x, g = r.y) : o === \"sw\" || o == \"w\" ? (C = p - r.x, y = l - (r.y + r.height), c = r.x + r.width, g = r.y) : (o === \"nw\" || o == \"n\") && (C = p - r.x, y = l - r.y, c = r.x + r.width, g = r.y + r.height), i = c + d.x + C, s = g + d.y + y;\n }\n this.evData = {\n startClientX: i,\n startClientY: s,\n startCropX: c,\n startCropY: g,\n clientX: e.clientX,\n clientY: e.clientY,\n isResize: w,\n ord: o\n }, this.mouseDownOnCrop = !0, this.setState({ cropIsActive: !0 });\n });\n m(this, \"onComponentPointerDown\", (e) => {\n const { crop: n, disabled: t, locked: d, keepSelection: r, onChange: o } = this.props, w = this.getBox();\n if (t || d || r && n)\n return;\n e.cancelable && e.preventDefault(), this.bindDocMove(), this.componentRef.current.focus({ preventScroll: !0 });\n const i = e.clientX - w.x, s = e.clientY - w.y, c = {\n unit: \"px\",\n x: i,\n y: s,\n width: 0,\n height: 0\n };\n this.evData = {\n startClientX: e.clientX,\n startClientY: e.clientY,\n startCropX: i,\n startCropY: s,\n clientX: e.clientX,\n clientY: e.clientY,\n isResize: !0\n }, this.mouseDownOnCrop = !0, o(D(c, w.width, w.height), v(c, w.width, w.height)), this.setState({ cropIsActive: !0, newCropIsBeingDrawn: !0 });\n });\n m(this, \"onDocPointerMove\", (e) => {\n const { crop: n, disabled: t, onChange: d, onDragStart: r } = this.props, o = this.getBox();\n if (t || !n || !this.mouseDownOnCrop)\n return;\n e.cancelable && e.preventDefault(), this.dragStarted || (this.dragStarted = !0, r && r(e));\n const { evData: w } = this;\n w.clientX = e.clientX, w.clientY = e.clientY;\n let i;\n w.isResize ? i = this.resizeCrop() : i = this.dragCrop(), X(n, i) || d(\n D(i, o.width, o.height),\n v(i, o.width, o.height)\n );\n });\n m(this, \"onComponentKeyDown\", (e) => {\n const { crop: n, disabled: t, onChange: d, onComplete: r } = this.props;\n if (t)\n return;\n const o = e.key;\n let w = !1;\n if (!n)\n return;\n const i = this.getBox(), s = this.makePixelCrop(i), g = (navigator.platform.match(\"Mac\") ? e.metaKey : e.ctrlKey) ? x.nudgeStepLarge : e.shiftKey ? x.nudgeStepMedium : x.nudgeStep;\n if (o === \"ArrowLeft\" ? (s.x -= g, w = !0) : o === \"ArrowRight\" ? (s.x += g, w = !0) : o === \"ArrowUp\" ? (s.y -= g, w = !0) : o === \"ArrowDown\" && (s.y += g, w = !0), w) {\n e.cancelable && e.preventDefault(), s.x = b(s.x, 0, i.width - s.width), s.y = b(s.y, 0, i.height - s.height);\n const p = D(s, i.width, i.height), l = v(s, i.width, i.height);\n d(p, l), r && r(p, l);\n }\n });\n m(this, \"onHandlerKeyDown\", (e, n) => {\n const {\n aspect: t = 0,\n crop: d,\n disabled: r,\n minWidth: o = 0,\n minHeight: w = 0,\n maxWidth: i,\n maxHeight: s,\n onChange: c,\n onComplete: g\n } = this.props, p = this.getBox();\n if (r || !d)\n return;\n if (e.key === \"ArrowUp\" || e.key === \"ArrowDown\" || e.key === \"ArrowLeft\" || e.key === \"ArrowRight\")\n e.stopPropagation(), e.preventDefault();\n else\n return;\n const C = (navigator.platform.match(\"Mac\") ? e.metaKey : e.ctrlKey) ? x.nudgeStepLarge : e.shiftKey ? x.nudgeStepMedium : x.nudgeStep, y = D(d, p.width, p.height), f = I(y, e.key, C, n), R = k(\n f,\n t,\n n,\n p.width,\n p.height,\n o,\n w,\n i,\n s\n );\n if (!X(d, R)) {\n const Y = v(R, p.width, p.height);\n c(R, Y), g && g(R, Y);\n }\n });\n m(this, \"onDocPointerDone\", (e) => {\n const { crop: n, disabled: t, onComplete: d, onDragEnd: r } = this.props, o = this.getBox();\n this.unbindDocMove(), !(t || !n) && this.mouseDownOnCrop && (this.mouseDownOnCrop = !1, this.dragStarted = !1, r && r(e), d && d(D(n, o.width, o.height), v(n, o.width, o.height)), this.setState({ cropIsActive: !1, newCropIsBeingDrawn: !1 }));\n });\n m(this, \"onDragFocus\", () => {\n var e;\n (e = this.componentRef.current) == null || e.scrollTo(0, 0);\n });\n }\n get document() {\n return document;\n }\n // We unfortunately get the bounding box every time as x+y changes\n // due to scrolling.\n getBox() {\n const e = this.mediaRef.current;\n if (!e)\n return { x: 0, y: 0, width: 0, height: 0 };\n const { x: n, y: t, width: d, height: r } = e.getBoundingClientRect();\n return { x: n, y: t, width: d, height: r };\n }\n componentDidUpdate(e) {\n const { crop: n, onComplete: t } = this.props;\n if (t && !e.crop && n) {\n const { width: d, height: r } = this.getBox();\n d && r && t(D(n, d, r), v(n, d, r));\n }\n }\n componentWillUnmount() {\n this.resizeObserver && this.resizeObserver.disconnect(), this.unbindDocMove();\n }\n bindDocMove() {\n this.docMoveBound || (this.document.addEventListener(\"pointermove\", this.onDocPointerMove, M), this.document.addEventListener(\"pointerup\", this.onDocPointerDone, M), this.document.addEventListener(\"pointercancel\", this.onDocPointerDone, M), this.docMoveBound = !0);\n }\n unbindDocMove() {\n this.docMoveBound && (this.document.removeEventListener(\"pointermove\", this.onDocPointerMove, M), this.document.removeEventListener(\"pointerup\", this.onDocPointerDone, M), this.document.removeEventListener(\"pointercancel\", this.onDocPointerDone, M), this.docMoveBound = !1);\n }\n getCropStyle() {\n const { crop: e } = this.props;\n if (e)\n return {\n top: `${e.y}${e.unit}`,\n left: `${e.x}${e.unit}`,\n width: `${e.width}${e.unit}`,\n height: `${e.height}${e.unit}`\n };\n }\n dragCrop() {\n const { evData: e } = this, n = this.getBox(), t = this.makePixelCrop(n), d = e.clientX - e.startClientX, r = e.clientY - e.startClientY;\n return t.x = b(e.startCropX + d, 0, n.width - t.width), t.y = b(e.startCropY + r, 0, n.height - t.height), t;\n }\n getPointRegion(e, n, t, d) {\n const { evData: r } = this, o = r.clientX - e.x, w = r.clientY - e.y;\n let i;\n d && n ? i = n === \"nw\" || n === \"n\" || n === \"ne\" : i = w < r.startCropY;\n let s;\n return t && n ? s = n === \"nw\" || n === \"w\" || n === \"sw\" : s = o < r.startCropX, s ? i ? \"nw\" : \"sw\" : i ? \"ne\" : \"se\";\n }\n resolveMinDimensions(e, n, t = 0, d = 0) {\n const r = Math.min(t, e.width), o = Math.min(d, e.height);\n return !n || !r && !o ? [r, o] : n > 1 ? r ? [r, r / n] : [o * n, o] : o ? [o * n, o] : [r, r / n];\n }\n resizeCrop() {\n const { evData: e } = this, { aspect: n = 0, maxWidth: t, maxHeight: d } = this.props, r = this.getBox(), [o, w] = this.resolveMinDimensions(r, n, this.props.minWidth, this.props.minHeight);\n let i = this.makePixelCrop(r);\n const s = this.getPointRegion(r, e.ord, o, w), c = e.ord || s;\n let g = e.clientX - e.startClientX, p = e.clientY - e.startClientY;\n (o && c === \"nw\" || c === \"w\" || c === \"sw\") && (g = Math.min(g, -o)), (w && c === \"nw\" || c === \"n\" || c === \"ne\") && (p = Math.min(p, -w));\n const l = {\n unit: \"px\",\n x: 0,\n y: 0,\n width: 0,\n height: 0\n };\n s === \"ne\" ? (l.x = e.startCropX, l.width = g, n ? (l.height = l.width / n, l.y = e.startCropY - l.height) : (l.height = Math.abs(p), l.y = e.startCropY - l.height)) : s === \"se\" ? (l.x = e.startCropX, l.y = e.startCropY, l.width = g, n ? l.height = l.width / n : l.height = p) : s === \"sw\" ? (l.x = e.startCropX + g, l.y = e.startCropY, l.width = Math.abs(g), n ? l.height = l.width / n : l.height = p) : s === \"nw\" && (l.x = e.startCropX + g, l.width = Math.abs(g), n ? (l.height = l.width / n, l.y = e.startCropY - l.height) : (l.height = Math.abs(p), l.y = e.startCropY + p));\n const C = k(\n l,\n n,\n s,\n r.width,\n r.height,\n o,\n w,\n t,\n d\n );\n return n || x.xyOrds.indexOf(c) > -1 ? i = C : x.xOrds.indexOf(c) > -1 ? (i.x = C.x, i.width = C.width) : x.yOrds.indexOf(c) > -1 && (i.y = C.y, i.height = C.height), i.x = b(i.x, 0, r.width - i.width), i.y = b(i.y, 0, r.height - i.height), i;\n }\n renderCropSelection() {\n const {\n ariaLabels: e = x.defaultProps.ariaLabels,\n disabled: n,\n locked: t,\n renderSelectionAddon: d,\n ruleOfThirds: r,\n crop: o\n } = this.props, w = this.getCropStyle();\n if (o)\n return /* @__PURE__ */ u.createElement(\n \"div\",\n {\n style: w,\n className: \"ReactCrop__crop-selection\",\n onPointerDown: this.onCropPointerDown,\n \"aria-label\": e.cropArea,\n tabIndex: 0,\n onKeyDown: this.onComponentKeyDown,\n role: \"group\"\n },\n !n && !t && /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-elements\", onFocus: this.onDragFocus }, /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-n\", \"data-ord\": \"n\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-e\", \"data-ord\": \"e\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-s\", \"data-ord\": \"s\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-w\", \"data-ord\": \"w\" }), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-nw\",\n \"data-ord\": \"nw\",\n tabIndex: 0,\n \"aria-label\": e.nwDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"nw\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-n\",\n \"data-ord\": \"n\",\n tabIndex: 0,\n \"aria-label\": e.nDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"n\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-ne\",\n \"data-ord\": \"ne\",\n tabIndex: 0,\n \"aria-label\": e.neDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"ne\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-e\",\n \"data-ord\": \"e\",\n tabIndex: 0,\n \"aria-label\": e.eDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"e\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-se\",\n \"data-ord\": \"se\",\n tabIndex: 0,\n \"aria-label\": e.seDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"se\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-s\",\n \"data-ord\": \"s\",\n tabIndex: 0,\n \"aria-label\": e.sDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"s\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-sw\",\n \"data-ord\": \"sw\",\n tabIndex: 0,\n \"aria-label\": e.swDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"sw\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-w\",\n \"data-ord\": \"w\",\n tabIndex: 0,\n \"aria-label\": e.wDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"w\"),\n role: \"button\"\n }\n )),\n d && /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__selection-addon\", onPointerDown: (i) => i.stopPropagation() }, d(this.state)),\n r && /* @__PURE__ */ u.createElement(u.Fragment, null, /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__rule-of-thirds-hz\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__rule-of-thirds-vt\" }))\n );\n }\n makePixelCrop(e) {\n const n = { ...E, ...this.props.crop || {} };\n return D(n, e.width, e.height);\n }\n render() {\n const { aspect: e, children: n, circularCrop: t, className: d, crop: r, disabled: o, locked: w, style: i, ruleOfThirds: s } = this.props, { cropIsActive: c, newCropIsBeingDrawn: g } = this.state, p = r ? this.renderCropSelection() : null, l = H(\n \"ReactCrop\",\n d,\n c && \"ReactCrop--active\",\n o && \"ReactCrop--disabled\",\n w && \"ReactCrop--locked\",\n g && \"ReactCrop--new-crop\",\n r && e && \"ReactCrop--fixed-aspect\",\n r && t && \"ReactCrop--circular-crop\",\n r && s && \"ReactCrop--rule-of-thirds\",\n !this.dragStarted && r && !r.width && !r.height && \"ReactCrop--invisible-crop\",\n t && \"ReactCrop--no-animate\"\n );\n return /* @__PURE__ */ u.createElement(\"div\", { ref: this.componentRef, className: l, style: i }, /* @__PURE__ */ u.createElement(\"div\", { ref: this.mediaRef, className: \"ReactCrop__child-wrapper\", onPointerDown: this.onComponentPointerDown }, n), r ? /* @__PURE__ */ u.createElement(\"svg\", { className: \"ReactCrop__crop-mask\", width: \"100%\", height: \"100%\" }, /* @__PURE__ */ u.createElement(\"defs\", null, /* @__PURE__ */ u.createElement(\"mask\", { id: `hole-${this.instanceId}` }, /* @__PURE__ */ u.createElement(\"rect\", { width: \"100%\", height: \"100%\", fill: \"white\" }), t ? /* @__PURE__ */ u.createElement(\n \"ellipse\",\n {\n cx: `${r.x + r.width / 2}${r.unit}`,\n cy: `${r.y + r.height / 2}${r.unit}`,\n rx: `${r.width / 2}${r.unit}`,\n ry: `${r.height / 2}${r.unit}`,\n fill: \"black\"\n }\n ) : /* @__PURE__ */ u.createElement(\n \"rect\",\n {\n x: `${r.x}${r.unit}`,\n y: `${r.y}${r.unit}`,\n width: `${r.width}${r.unit}`,\n height: `${r.height}${r.unit}`,\n fill: \"black\"\n }\n ))), /* @__PURE__ */ u.createElement(\"rect\", { fill: \"black\", fillOpacity: 0.5, width: \"100%\", height: \"100%\", mask: `url(#hole-${this.instanceId})` })) : void 0, p);\n }\n};\nm(x, \"xOrds\", [\"e\", \"w\"]), m(x, \"yOrds\", [\"n\", \"s\"]), m(x, \"xyOrds\", [\"nw\", \"ne\", \"se\", \"sw\"]), m(x, \"nudgeStep\", 1), m(x, \"nudgeStepMedium\", 10), m(x, \"nudgeStepLarge\", 100), m(x, \"defaultProps\", {\n ariaLabels: {\n cropArea: \"Use the arrow keys to move the crop selection area\",\n nwDragHandle: \"Use the arrow keys to move the north west drag handle to change the crop selection area\",\n nDragHandle: \"Use the up and down arrow keys to move the north drag handle to change the crop selection area\",\n neDragHandle: \"Use the arrow keys to move the north east drag handle to change the crop selection area\",\n eDragHandle: \"Use the up and down arrow keys to move the east drag handle to change the crop selection area\",\n seDragHandle: \"Use the arrow keys to move the south east drag handle to change the crop selection area\",\n sDragHandle: \"Use the up and down arrow keys to move the south drag handle to change the crop selection area\",\n swDragHandle: \"Use the arrow keys to move the south west drag handle to change the crop selection area\",\n wDragHandle: \"Use the up and down arrow keys to move the west drag handle to change the crop selection area\"\n }\n});\nlet S = x;\nexport {\n S as Component,\n S as ReactCrop,\n X as areCropsEqual,\n L as centerCrop,\n b as clamp,\n H as cls,\n k as containCrop,\n v as convertToPercentCrop,\n D as convertToPixelCrop,\n S as default,\n E as defaultCrop,\n B as makeAspectCrop,\n I as nudgeCrop\n};\n","import {\n centerCrop,\n makeAspectCrop,\n type Crop,\n type PixelCrop,\n} from \"react-image-crop\";\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\nexport const ASPECT_OPTIONS: readonly {\n label: string;\n value: \"free\" | number;\n}[] = [\n { label: \"Free\", value: \"free\" as const },\n { label: \"1:1\", value: 1 },\n { label: \"16:9\", value: 16 / 9 },\n { label: \"4:3\", value: 4 / 3 },\n];\n\nexport type AspectValue = \"free\" | number;\n\nexport const ROTATION_MIN = -180;\nexport const ROTATION_MAX = 180;\n\n// ── Crop helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Compute initial crop + completedCrop values for given container dimensions\n * and optional aspect ratio.\n *\n * Used when the image first loads, when the user resets, and when the aspect\n * ratio changes via the dropdown.\n */\nexport function computeInitialCrop(\n displayedWidth: number,\n displayedHeight: number,\n aspect?: number,\n): { crop: Crop; completedCrop: PixelCrop } {\n if (aspect) {\n const imageAspect = displayedWidth / displayedHeight;\n const seed =\n aspect > imageAspect\n ? { unit: \"%\" as const, width: 100 }\n : { unit: \"%\" as const, height: 100 };\n\n const crop = centerCrop(\n makeAspectCrop(seed, aspect, displayedWidth, displayedHeight),\n displayedWidth,\n displayedHeight,\n );\n const completedCrop: PixelCrop = {\n x: (crop.x / 100) * displayedWidth,\n y: (crop.y / 100) * displayedHeight,\n width: (crop.width / 100) * displayedWidth,\n height: (crop.height / 100) * displayedHeight,\n unit: \"px\",\n };\n return { crop, completedCrop };\n }\n\n const crop: Crop = { unit: \"%\", x: 0, y: 0, width: 100, height: 100 };\n const completedCrop: PixelCrop = {\n x: 0,\n y: 0,\n width: displayedWidth,\n height: displayedHeight,\n unit: \"px\",\n };\n return { crop, completedCrop };\n}\n\n// ── Canvas export ────────────────────────────────────────────────────────────\n\n/**\n * Render the image onto a canvas at natural (full) resolution\n * (including rotation, zoom, flip) then extract the crop rectangle as a File.\n *\n * Uses createImageBitmap() from the original file to guarantee full-resolution\n * decoding. The <img> element may have been decoded at reduced resolution by the\n * browser when displayed at a small CSS size (downsample-on-decode).\n */\nexport async function getCroppedImgFromViewport(\n image: HTMLImageElement,\n vw: number,\n vh: number,\n crop: { x: number; y: number; width: number; height: number },\n zoom: number,\n rotationDeg: number,\n flipHorizontal: boolean,\n fileName: string,\n type: string = \"image/jpeg\",\n quality: number = 0.9,\n sourceFile?: File,\n): Promise<File> {\n // Decode the image at full resolution from the original file bytes,\n // bypassing any browser downsample-on-decode optimization on the <img>.\n const bitmap = sourceFile\n ? await createImageBitmap(sourceFile)\n : await createImageBitmap(image);\n\n const nw = bitmap.width;\n const nh = bitmap.height;\n\n // Scale factor from viewport coords to natural-resolution coords.\n // Cap canvas dimensions at MAX_CANVAS_DIM to avoid exceeding browser limits\n // (most browsers cap at 16384, but we use 4096 for safe memory usage).\n const MAX_CANVAS_DIM = 4096;\n const rawUpscale = Math.max(nw / vw, nh / vh);\n const rawW = Math.round(vw * rawUpscale);\n const rawH = Math.round(vh * rawUpscale);\n const downscale = Math.min(1, MAX_CANVAS_DIM / rawW, MAX_CANVAS_DIM / rawH);\n const upscale = rawUpscale * downscale;\n const canvasW = Math.round(rawW * downscale);\n const canvasH = Math.round(rawH * downscale);\n\n const scaleFit = Math.min(canvasW / nw, canvasH / nh);\n const scale = scaleFit * zoom;\n\n const source = document.createElement(\"canvas\");\n source.width = canvasW;\n source.height = canvasH;\n const ctx = source.getContext(\"2d\");\n if (!ctx) throw new Error(\"No 2d context\");\n ctx.imageSmoothingQuality = \"high\";\n ctx.imageSmoothingEnabled = true;\n\n ctx.save();\n ctx.translate(canvasW / 2, canvasH / 2);\n ctx.rotate((rotationDeg * Math.PI) / 180);\n if (flipHorizontal) ctx.scale(-1, 1);\n ctx.scale(scale, scale);\n ctx.translate(-nw / 2, -nh / 2);\n ctx.drawImage(bitmap, 0, 0);\n ctx.restore();\n\n bitmap.close();\n\n // Scale crop coordinates to natural resolution\n const cropX = Math.max(0, Math.floor(crop.x * upscale));\n const cropY = Math.max(0, Math.floor(crop.y * upscale));\n const pw = Math.floor(crop.width * upscale);\n const ph = Math.floor(crop.height * upscale);\n\n const out = document.createElement(\"canvas\");\n out.width = pw;\n out.height = ph;\n const outCtx = out.getContext(\"2d\");\n if (!outCtx) throw new Error(\"No 2d context\");\n outCtx.imageSmoothingQuality = \"high\";\n outCtx.imageSmoothingEnabled = true;\n outCtx.drawImage(source, cropX, cropY, pw, ph, 0, 0, pw, ph);\n\n return new Promise((resolve, reject) => {\n out.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Canvas is empty\"));\n return;\n }\n const file = new File([blob], fileName, { type });\n resolve(file);\n },\n type,\n quality,\n );\n });\n}\n","import type React from \"react\";\nimport type { DamAsset } from \"../types\";\n\nexport function getAssetCardClassName(isSelected: boolean): string {\n return isSelected ? \"bg-blue-50 ring-2 ring-blue-500\" : \"bg-white\";\n}\n\nexport function createAssetClickHandlers(\n pendingClickTimeoutRef: React.MutableRefObject<ReturnType<\n typeof setTimeout\n > | null>,\n handleAssetClick: (\n e: React.MouseEvent,\n asset: DamAsset,\n index: number,\n ) => void,\n setPreviewAsset: (asset: DamAsset | null) => void,\n asset: DamAsset,\n index: number,\n): {\n onClick: (e: React.MouseEvent) => void;\n onDoubleClick: (e: React.MouseEvent) => void;\n} {\n const delayMs = 250;\n return {\n onClick: (e: React.MouseEvent) => {\n if (pendingClickTimeoutRef.current) {\n clearTimeout(pendingClickTimeoutRef.current);\n }\n pendingClickTimeoutRef.current = setTimeout(() => {\n handleAssetClick(e, asset, index);\n pendingClickTimeoutRef.current = null;\n }, delayMs);\n },\n onDoubleClick: (e: React.MouseEvent) => {\n e.stopPropagation();\n if (pendingClickTimeoutRef.current) {\n clearTimeout(pendingClickTimeoutRef.current);\n pendingClickTimeoutRef.current = null;\n }\n setPreviewAsset(asset);\n },\n };\n}\n","/**\n * Get the URL for a DAM asset, preferring the CDN URL if available.\n * Falls back to the API proxy URL when CDN URL is not ready.\n *\n * @param asset - The asset object with code and variant info\n * @param apiBaseUrl - Base URL for fallback API proxy (e.g. \"https://api.fluid.app/api\")\n */\nexport function getDamAssetUrl(\n asset: {\n code: string;\n default_variant_id: string | number;\n default_variant_url?: string | null;\n },\n apiBaseUrl?: string,\n): string {\n if (asset.default_variant_url) {\n return asset.default_variant_url;\n }\n const base = apiBaseUrl || \"\";\n return `${base}/dam/assets/${asset.code}/variants/${asset.default_variant_id}/content`;\n}\n","/**\n * Sanitizes a filename for safe upload by:\n * 1. Removing or replacing problematic characters\n * 2. Ensuring proper encoding for spaces and special characters\n * 3. Maintaining file extension\n */\nexport function sanitizeFilename(filename: string): string {\n if (!filename) return filename;\n\n // Split filename into name and extension\n const lastDotIndex = filename.lastIndexOf(\".\");\n const name =\n lastDotIndex > 0 ? filename.substring(0, lastDotIndex) : filename;\n const rawExtension = lastDotIndex > 0 ? filename.substring(lastDotIndex) : \"\";\n const extension = (rawExtension.split(\"?\")[0] ?? \"\").split(\"#\")[0] ?? \"\";\n\n // Clean the name part\n let sanitizedName = name\n // Replace problematic characters with underscores\n .replace(/[<>:\"/\\\\|?*]/g, \"_\")\n // Replace multiple spaces with single space\n .replace(/\\s+/g, \" \")\n // Trim whitespace\n .trim()\n // Replace spaces with underscores for better compatibility\n .replace(/\\s/g, \"_\");\n\n // If the name is empty after sanitization, use a default\n if (!sanitizedName) {\n sanitizedName = \"file\";\n }\n\n // Return the sanitized filename with extension\n return sanitizedName + extension;\n}\n\n/**\n * Extracts the base name (without extension) from a filename\n * and sanitizes it for use as an asset name\n * Server validation only allows: word characters, spaces, and hyphens\n */\nexport function getSanitizedAssetName(filename: string): string {\n if (!filename) return \"asset\";\n\n // Split filename into name and extension\n const lastDotIndex = filename.lastIndexOf(\".\");\n const name =\n lastDotIndex > 0 ? filename.substring(0, lastDotIndex) : filename;\n\n // Clean the name part for asset naming\n // Only allow word characters (letters, numbers, underscore), spaces, and hyphens\n let sanitizedName = name\n // Replace any characters that are not word characters, spaces, or hyphens with spaces\n .replace(/[^\\w\\s-]/g, \" \")\n // Replace multiple spaces with single space\n .replace(/\\s+/g, \" \")\n // Trim whitespace\n .trim();\n\n // If the name is empty after sanitization, use a default\n if (!sanitizedName) {\n sanitizedName = \"asset\";\n }\n\n return sanitizedName;\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { createContext, use } from \"react\";\nimport type {\n FilePickerApi,\n PickerEnrollmentPackItem,\n PickerPageItem,\n PickerProductItem,\n PickerSource,\n} from \"@fluid-app/file-picker-core\";\n\nexport type RenderImageProps = {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n fill?: boolean;\n className?: string;\n onError?: () => void;\n};\n\nexport interface ShareablesUIConfig {\n /** User info for fallback logo, etc. */\n user?: { id: number; company?: { logo_url?: string | null } | null } | null;\n /** Affiliate ID for user-scoped operations (favorites toggle) */\n affiliateId?: number | null;\n /** Base path for rep links (e.g., \"/rep/123\") */\n basePath: string;\n /** Navigation callback — replaces useRouter.push */\n navigate: (path: string) => void;\n /** Toast callback — replaces fluidToast */\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n error?: unknown;\n }) => void;\n /** MySite share handler — admin-coupled (dynamic import).\n * The implementation is responsible for providing user-specific fields\n * (affiliate_id, country_id) from its own auth context. */\n onMySiteShare?: (params: {\n shareLink: string;\n relateable_id: number;\n relateable_type: string;\n }) => Promise<void>;\n /** File download handler — replaces useFileDownload */\n onFileDownload?: (url: string, filename: string) => void;\n /** File picker API for DAM asset browsing/uploading */\n filePickerApi?: FilePickerApi;\n /** Custom image renderer — replaces next/image imports */\n renderImage?: (props: RenderImageProps) => React.ReactNode;\n /** Toggle favorite on a playlist (Library) or other entity.\n * Host app provides the implementation since the endpoint lives\n * outside the shareables API domain. */\n onToggleFavorite?: (params: {\n favoriteableId: number;\n favoriteableType: string;\n }) => Promise<{ is_favorited: boolean }>;\n /** Delete a playlist by ID. */\n onDeletePlaylist?: (playlistId: number) => Promise<void>;\n /** Media products API — list, add, remove product associations on media. */\n mediaProductsApi?: {\n listMediaProducts: (\n mediumId: number,\n params?: { language_iso?: string },\n ) => Promise<{\n media_products: Array<{\n id: number;\n name?: string;\n slug?: string | null;\n image_url?: string | null;\n price?: string | null;\n sku?: string | null;\n }>;\n }>;\n addMediaProduct: (\n mediumId: number,\n body: { id: number },\n ) => Promise<unknown>;\n removeMediaProduct: (\n mediumId: number,\n productId: number,\n ) => Promise<unknown>;\n };\n /** Product search for tagging — returns matching products with optional\n * cursor pagination so the tag modal can infinite-scroll. Empty query\n * should return the initial product list (host app's decision). */\n searchProducts?: (\n query: string,\n options?: { cursor?: string | null; limit?: number },\n ) => Promise<{\n products: Array<{\n id: number;\n name?: string;\n image_url?: string | null;\n price?: string | null;\n }>;\n nextCursor?: string | null;\n }>;\n /**\n * When true, hides actions that author or alter *content* — creating and\n * editing media, tagging products, uploading thumbnails. Set for member\n * types without rep eligibility.\n *\n * This does NOT gate playlist curation; see `canCuratePlaylists`. The two\n * were a single flag until customers were given their own playlists, at\n * which point one boolean could no longer answer both questions.\n */\n readOnly?: boolean;\n /**\n * When true, the member may curate playlists they own — create, rename,\n * reorder, delete, and add or remove items. Ownership is still enforced by\n * the API, which refuses any playlist the member doesn't own, so this only\n * decides whether the affordances are offered.\n *\n * Defaults to true when unset so hosts that never opted out (the admin app)\n * keep working unchanged.\n */\n canCuratePlaylists?: boolean;\n /** Upload a thumbnail blob to DAM and return the hosted URL. */\n uploadThumbnail?: (blob: Blob, filename: string) => Promise<string>;\n /**\n * Optional list/search functions for the non-media picker tabs in the\n * playlist editor (Pages, Products, Enrollment Packs).\n *\n * Each entry is opt-in: when an entry is omitted the corresponding tab is\n * hidden in the picker. Hosts wire these via their portal-tenant adapters\n * once the matching BFF endpoint exists.\n */\n pickerSources?: {\n pages?: PickerSource<PickerPageItem>;\n products?: PickerSource<PickerProductItem>;\n enrollment_packs?: PickerSource<PickerEnrollmentPackItem>;\n };\n}\n\nconst MISSING_PROVIDER = Symbol(\"missing-shareables-provider\");\n\nconst ShareablesUIContext = createContext<\n ShareablesUIConfig | typeof MISSING_PROVIDER\n>(MISSING_PROVIDER);\n\nconst OBJECT_FIT_CLASS_NAMES = new Set([\n \"object-contain\",\n \"object-cover\",\n \"object-fill\",\n \"object-none\",\n \"object-scale-down\",\n]);\n\nfunction hasObjectFitClass(className: string | undefined): boolean {\n return (\n className\n ?.split(/\\s+/)\n .some((classNamePart) => OBJECT_FIT_CLASS_NAMES.has(classNamePart)) ??\n false\n );\n}\n\nexport function ShareablesUIProvider({\n config,\n children,\n}: {\n config: ShareablesUIConfig;\n children: React.ReactNode;\n}): React.JSX.Element {\n return (\n <ShareablesUIContext.Provider value={config}>\n {children}\n </ShareablesUIContext.Provider>\n );\n}\n\nfunction defaultRenderImage(props: RenderImageProps): React.ReactNode {\n const fillClassName = props.fill\n ? `absolute inset-0 h-full w-full${hasObjectFitClass(props.className) ? \"\" : \" object-cover\"}`\n : \"\";\n const className = [fillClassName, props.className].filter(Boolean).join(\" \");\n\n return (\n <img\n src={props.src}\n alt={props.alt}\n width={props.width}\n height={props.height}\n className={className}\n onError={props.onError}\n />\n );\n}\n\nexport function useRenderImage(): (props: RenderImageProps) => React.ReactNode {\n const ctx = use(ShareablesUIContext);\n if (ctx === MISSING_PROVIDER) {\n return defaultRenderImage;\n }\n return ctx.renderImage ?? defaultRenderImage;\n}\n\nexport function useShareablesUI(): ShareablesUIConfig {\n const ctx = use(ShareablesUIContext);\n if (ctx === MISSING_PROVIDER) {\n throw new Error(\n \"useShareablesUI must be used within a <ShareablesUIProvider>. \" +\n \"Wrap your component tree with <ShareablesUIProvider config={...}>.\",\n );\n }\n return ctx;\n}\n\n/**\n * Whether the current member may curate their own playlists.\n *\n * Reads `canCuratePlaylists`, defaulting to true when the host hasn't set it,\n * so the default is applied in one place instead of every call site having to\n * remember that `undefined` means allowed.\n */\nexport function useCanCuratePlaylists(): boolean {\n return useShareablesUI().canCuratePlaylists !== false;\n}\n","/*! @license DOMPurify 3.3.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.3/LICENSE */\n\nconst {\n entries,\n setPrototypeOf,\n isFrozen,\n getPrototypeOf,\n getOwnPropertyDescriptor\n} = Object;\nlet {\n freeze,\n seal,\n create\n} = Object; // eslint-disable-line import/no-mutable-exports\nlet {\n apply,\n construct\n} = typeof Reflect !== 'undefined' && Reflect;\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\nif (!apply) {\n apply = function apply(func, thisArg) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return func.apply(thisArg, args);\n };\n}\nif (!construct) {\n construct = function construct(Func) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n return new Func(...args);\n };\n}\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySplice = unapply(Array.prototype.splice);\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringToString = unapply(String.prototype.toString);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\nconst objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);\nconst regExpTest = unapply(RegExp.prototype.test);\nconst typeErrorCreate = unconstruct(TypeError);\n/**\n * Creates a new function that calls the given function with a specified thisArg and arguments.\n *\n * @param func - The function to be wrapped and called.\n * @returns A new function that calls the given function with a specified thisArg and arguments.\n */\nfunction unapply(func) {\n return function (thisArg) {\n if (thisArg instanceof RegExp) {\n thisArg.lastIndex = 0;\n }\n for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {\n args[_key3 - 1] = arguments[_key3];\n }\n return apply(func, thisArg, args);\n };\n}\n/**\n * Creates a new function that constructs an instance of the given constructor function with the provided arguments.\n *\n * @param func - The constructor function to be wrapped and called.\n * @returns A new function that constructs an instance of the given constructor function with the provided arguments.\n */\nfunction unconstruct(Func) {\n return function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n return construct(Func, args);\n };\n}\n/**\n * Add properties to a lookup table\n *\n * @param set - The set to which elements will be added.\n * @param array - The array containing elements to be added to the set.\n * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.\n * @returns The modified set with added elements.\n */\nfunction addToSet(set, array) {\n let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = transformCaseFunc(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n element = lcElement;\n }\n }\n set[element] = true;\n }\n return set;\n}\n/**\n * Clean up an array to harden against CSPP\n *\n * @param array - The array to be cleaned.\n * @returns The cleaned version of the array\n */\nfunction cleanArray(array) {\n for (let index = 0; index < array.length; index++) {\n const isPropertyExist = objectHasOwnProperty(array, index);\n if (!isPropertyExist) {\n array[index] = null;\n }\n }\n return array;\n}\n/**\n * Shallow clone an object\n *\n * @param object - The object to be cloned.\n * @returns A new object that copies the original.\n */\nfunction clone(object) {\n const newObject = create(null);\n for (const [property, value] of entries(object)) {\n const isPropertyExist = objectHasOwnProperty(object, property);\n if (isPropertyExist) {\n if (Array.isArray(value)) {\n newObject[property] = cleanArray(value);\n } else if (value && typeof value === 'object' && value.constructor === Object) {\n newObject[property] = clone(value);\n } else {\n newObject[property] = value;\n }\n }\n }\n return newObject;\n}\n/**\n * This method automatically checks if the prop is function or getter and behaves accordingly.\n *\n * @param object - The object to look up the getter function in its prototype chain.\n * @param prop - The property name for which to find the getter function.\n * @returns The getter function found in the prototype chain or a fallback function.\n */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n object = getPrototypeOf(object);\n }\n function fallbackValue() {\n return null;\n }\n return fallbackValue;\n}\n\nconst html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\nconst svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\nconst svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nconst svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\nconst mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nconst mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\nconst text = freeze(['#text']);\n\nconst html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);\nconst svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\nconst mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\nconst xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\n// eslint-disable-next-line unicorn/better-regex\nconst MUSTACHE_EXPR = seal(/\\{\\{[\\w\\W]*|[\\w\\W]*\\}\\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode\nconst ERB_EXPR = seal(/<%[\\w\\W]*|[\\w\\W]*%>/gm);\nconst TMPLIT_EXPR = seal(/\\$\\{[\\w\\W]*/gm); // eslint-disable-line unicorn/better-regex\nconst DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]+$/); // eslint-disable-line no-useless-escape\nconst ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nconst IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nconst IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nconst ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\nconst DOCTYPE_NAME = seal(/^html$/i);\nconst CUSTOM_ELEMENT = seal(/^[a-z][.\\w]*(-[.\\w]+)+$/i);\n\nvar EXPRESSIONS = /*#__PURE__*/Object.freeze({\n __proto__: null,\n ARIA_ATTR: ARIA_ATTR,\n ATTR_WHITESPACE: ATTR_WHITESPACE,\n CUSTOM_ELEMENT: CUSTOM_ELEMENT,\n DATA_ATTR: DATA_ATTR,\n DOCTYPE_NAME: DOCTYPE_NAME,\n ERB_EXPR: ERB_EXPR,\n IS_ALLOWED_URI: IS_ALLOWED_URI,\n IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,\n MUSTACHE_EXPR: MUSTACHE_EXPR,\n TMPLIT_EXPR: TMPLIT_EXPR\n});\n\n/* eslint-disable @typescript-eslint/indent */\n// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nconst NODE_TYPE = {\n element: 1,\n attribute: 2,\n text: 3,\n cdataSection: 4,\n entityReference: 5,\n // Deprecated\n entityNode: 6,\n // Deprecated\n progressingInstruction: 7,\n comment: 8,\n document: 9,\n documentType: 10,\n documentFragment: 11,\n notation: 12 // Deprecated\n};\nconst getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param trustedTypes The policy factory.\n * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).\n * @return The policy created (or null, if Trusted Types\n * are not supported or creating the policy failed).\n */\nconst _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {\n if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {\n suffix = purifyHostElement.getAttribute(ATTR_NAME);\n }\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n createScriptURL(scriptUrl) {\n return scriptUrl;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\nconst _createHooksMap = function _createHooksMap() {\n return {\n afterSanitizeAttributes: [],\n afterSanitizeElements: [],\n afterSanitizeShadowDOM: [],\n beforeSanitizeAttributes: [],\n beforeSanitizeElements: [],\n beforeSanitizeShadowDOM: [],\n uponSanitizeAttribute: [],\n uponSanitizeElement: [],\n uponSanitizeShadowNode: []\n };\n};\nfunction createDOMPurify() {\n let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n const DOMPurify = root => createDOMPurify(root);\n DOMPurify.version = '3.3.3';\n DOMPurify.removed = [];\n if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n return DOMPurify;\n }\n let {\n document\n } = window;\n const originalDocument = document;\n const currentScript = originalDocument.currentScript;\n const {\n DocumentFragment,\n HTMLTemplateElement,\n Node,\n Element,\n NodeFilter,\n NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,\n HTMLFormElement,\n DOMParser,\n trustedTypes\n } = window;\n const ElementPrototype = Element.prototype;\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const remove = lookupGetter(ElementPrototype, 'remove');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n let trustedTypesPolicy;\n let emptyHTML = '';\n const {\n implementation,\n createNodeIterator,\n createDocumentFragment,\n getElementsByTagName\n } = document;\n const {\n importNode\n } = originalDocument;\n let hooks = _createHooksMap();\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;\n const {\n MUSTACHE_EXPR,\n ERB_EXPR,\n TMPLIT_EXPR,\n DATA_ATTR,\n ARIA_ATTR,\n IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE,\n CUSTOM_ELEMENT\n } = EXPRESSIONS;\n let {\n IS_ALLOWED_URI: IS_ALLOWED_URI$1\n } = EXPRESSIONS;\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);\n /*\n * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false\n }\n }));\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */\n const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {\n tagCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n }\n }));\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n /* Decide if self-closing tags in attributes are allowed.\n * Usually removed due to a mXSS issue in jQuery 3.0 */\n let ALLOW_SELF_CLOSE_IN_ATTR = true;\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n /* Output should be safe even for XML used within HTML and alike.\n * This means, DOMPurify removes comments when containing risky content.\n */\n let SAFE_FOR_XML = true;\n /* Decide if document with <html>... should be returned */\n let WHOLE_DOCUMENT = false;\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n /* Output should be free from DOM clobbering attacks?\n * This sanitizes markups named with colliding, clobberable built-in DOM APIs.\n */\n let SANITIZE_DOM = true;\n /* Achieve full DOM Clobbering protection by isolating the namespace of named\n * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.\n *\n * HTML/DOM spec rules that enable DOM Clobbering:\n * - Named Access on Window (§7.3.3)\n * - DOM Tree Accessors (§3.1.5)\n * - Form Element Parent-Child Relations (§4.10.3)\n * - Iframe srcdoc / Nested WindowProxies (§4.8.5)\n * - HTMLCollection (§4.2.10.2)\n *\n * Namespace isolation is implemented by prefixing `id` and `name` attributes\n * with a constant string, i.e., `user-content-`\n */\n let SANITIZE_NAMED_PROPS = false;\n const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);\n /* Tags that are safe for data: URIs */\n let DATA_URI_TAGS = null;\n const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);\n /* Attributes safe for values like \"javascript:\" */\n let URI_SAFE_ATTRIBUTES = null;\n const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);\n const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n let NAMESPACE = HTML_NAMESPACE;\n let IS_EMPTY_INPUT = false;\n /* Allowed XHTML+XML namespaces */\n let ALLOWED_NAMESPACES = null;\n const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);\n let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);\n let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erroneously deleted from\n // HTML namespace.\n const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);\n /* Parsing of strict XHTML documents */\n let PARSER_MEDIA_TYPE = null;\n const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];\n const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n let transformCaseFunc = null;\n /* Keep a reference to config to pass to hooks */\n let CONFIG = null;\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n const formElement = document.createElement('form');\n const isRegexOrFunction = function isRegexOrFunction(testValue) {\n return testValue instanceof RegExp || testValue instanceof Function;\n };\n /**\n * _parseConfig\n *\n * @param cfg optional config literal\n */\n // eslint-disable-next-line complexity\n const _parseConfig = function _parseConfig() {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n /* Shield configuration object from tampering */\n if (!cfg || typeof cfg !== 'object') {\n cfg = {};\n }\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n PARSER_MEDIA_TYPE =\n // eslint-disable-next-line unicorn/prefer-includes\n SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;\n // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.\n transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;\n /* Set configuration parameters */\n ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;\n ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;\n URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;\n FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;\n FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});\n FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});\n USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;\n NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;\n HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;\n CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n }\n if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n }\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, text);\n ALLOWED_ATTR = create(null);\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, html$1);\n addToSet(ALLOWED_ATTR, html);\n }\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, svg$1);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, svgFilters);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, mathMl$1);\n addToSet(ALLOWED_ATTR, mathMl);\n addToSet(ALLOWED_ATTR, xml);\n }\n }\n /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */\n if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {\n EXTRA_ELEMENT_HANDLING.tagCheck = null;\n }\n if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {\n EXTRA_ELEMENT_HANDLING.attributeCheck = null;\n }\n /* Merge configuration parameters */\n if (cfg.ADD_TAGS) {\n if (typeof cfg.ADD_TAGS === 'function') {\n EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;\n } else {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);\n }\n }\n if (cfg.ADD_ATTR) {\n if (typeof cfg.ADD_ATTR === 'function') {\n EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;\n } else {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);\n }\n }\n if (cfg.ADD_URI_SAFE_ATTR) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);\n }\n if (cfg.FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);\n }\n if (cfg.ADD_FORBID_CONTENTS) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);\n }\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n if (cfg.TRUSTED_TYPES_POLICY) {\n if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook.');\n }\n if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook.');\n }\n // Overwrite existing TrustedTypes policy.\n trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;\n // Sign local variables required by `sanitize`.\n emptyHTML = trustedTypesPolicy.createHTML('');\n } else {\n // Uninitialized policy, attempt to initialize the internal dompurify policy.\n if (trustedTypesPolicy === undefined) {\n trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);\n }\n // If creating the internal policy succeeded sign internal variables.\n if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {\n emptyHTML = trustedTypesPolicy.createHTML('');\n }\n }\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n CONFIG = cfg;\n };\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);\n const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);\n /**\n * @param element a DOM element whose namespace is being checked\n * @returns Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n const _checkValidNamespace = function _checkValidNamespace(element) {\n let parent = getParentNode(element);\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: NAMESPACE,\n tagName: 'template'\n };\n }\n const tagName = stringToLowerCase(element.tagName);\n const parentTagName = stringToLowerCase(parent.tagName);\n if (!ALLOWED_NAMESPACES[element.namespaceURI]) {\n return false;\n }\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via <svg>. If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'svg';\n }\n // The only way to switch from MathML to SVG is via`\n // svg if parent is either <annotation-xml> or MathML\n // text integration points.\n if (parent.namespaceURI === MATHML_NAMESPACE) {\n return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n }\n // We only allow elements that are defined in SVG\n // spec. All others are disallowed in SVG namespace.\n return Boolean(ALL_SVG_TAGS[tagName]);\n }\n if (element.namespaceURI === MATHML_NAMESPACE) {\n // The only way to switch from HTML namespace to MathML\n // is via <math>. If it happens via any other tag, then\n // it should be killed.\n if (parent.namespaceURI === HTML_NAMESPACE) {\n return tagName === 'math';\n }\n // The only way to switch from SVG to MathML is via\n // <math> and HTML integration points\n if (parent.namespaceURI === SVG_NAMESPACE) {\n return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n }\n // We only allow elements that are defined in MathML\n // spec. All others are disallowed in MathML namespace.\n return Boolean(ALL_MATHML_TAGS[tagName]);\n }\n if (element.namespaceURI === HTML_NAMESPACE) {\n // The only way to switch from SVG to HTML is via\n // HTML integration points, and from MathML to HTML\n // is via MathML text integration points\n if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n return false;\n }\n // We disallow tags that are specific for MathML\n // or SVG and should never appear in HTML namespace\n return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);\n }\n // For XHTML and XML documents that support custom namespaces\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {\n return true;\n }\n // The code should never reach this place (this means\n // that the element somehow got namespace that is not\n // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).\n // Return false just in case.\n return false;\n };\n /**\n * _forceRemove\n *\n * @param node a DOM node\n */\n const _forceRemove = function _forceRemove(node) {\n arrayPush(DOMPurify.removed, {\n element: node\n });\n try {\n // eslint-disable-next-line unicorn/prefer-dom-node-remove\n getParentNode(node).removeChild(node);\n } catch (_) {\n remove(node);\n }\n };\n /**\n * _removeAttribute\n *\n * @param name an Attribute name\n * @param element a DOM node\n */\n const _removeAttribute = function _removeAttribute(name, element) {\n try {\n arrayPush(DOMPurify.removed, {\n attribute: element.getAttributeNode(name),\n from: element\n });\n } catch (_) {\n arrayPush(DOMPurify.removed, {\n attribute: null,\n from: element\n });\n }\n element.removeAttribute(name);\n // We void attribute values for unremovable \"is\" attributes\n if (name === 'is') {\n if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n try {\n _forceRemove(element);\n } catch (_) {}\n } else {\n try {\n element.setAttribute(name, '');\n } catch (_) {}\n }\n }\n };\n /**\n * _initDocument\n *\n * @param dirty - a string of dirty markup\n * @return a DOM, filled with the dirty markup\n */\n const _initDocument = function _initDocument(dirty) {\n /* Create a HTML document */\n let doc = null;\n let leadingWhitespace = null;\n if (FORCE_BODY) {\n dirty = '<remove></remove>' + dirty;\n } else {\n /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */\n const matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n leadingWhitespace = matches && matches[0];\n }\n if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {\n // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)\n dirty = '<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body>' + dirty + '</body></html>';\n }\n const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n /*\n * Use the DOMParser API by default, fallback later if needs be\n * DOMParser not work for svg when has multiple root element.\n */\n if (NAMESPACE === HTML_NAMESPACE) {\n try {\n doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n } catch (_) {}\n }\n /* Use createHTMLDocument in case DOMParser is not available */\n if (!doc || !doc.documentElement) {\n doc = implementation.createDocument(NAMESPACE, 'template', null);\n try {\n doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;\n } catch (_) {\n // Syntax error if dirtyPayload is invalid xml\n }\n }\n const body = doc.body || doc.documentElement;\n if (dirty && leadingWhitespace) {\n body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n }\n /* Work on whole document or just its body */\n if (NAMESPACE === HTML_NAMESPACE) {\n return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n }\n return WHOLE_DOCUMENT ? doc.documentElement : body;\n };\n /**\n * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.\n *\n * @param root The root element or node to start traversing on.\n * @return The created NodeIterator\n */\n const _createNodeIterator = function _createNodeIterator(root) {\n return createNodeIterator.call(root.ownerDocument || root, root,\n // eslint-disable-next-line no-bitwise\n NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);\n };\n /**\n * _isClobbered\n *\n * @param element element to check for clobbering attacks\n * @return true if clobbered, false if safe\n */\n const _isClobbered = function _isClobbered(element) {\n return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');\n };\n /**\n * Checks whether the given object is a DOM node.\n *\n * @param value object to check whether it's a DOM node\n * @return true is object is a DOM node\n */\n const _isNode = function _isNode(value) {\n return typeof Node === 'function' && value instanceof Node;\n };\n function _executeHooks(hooks, currentNode, data) {\n arrayForEach(hooks, hook => {\n hook.call(DOMPurify, currentNode, data, CONFIG);\n });\n }\n /**\n * _sanitizeElements\n *\n * @protect nodeName\n * @protect textContent\n * @protect removeChild\n * @param currentNode to check for permission to exist\n * @return true if node was killed, false if left alive\n */\n const _sanitizeElements = function _sanitizeElements(currentNode) {\n let content = null;\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeElements, currentNode, null);\n /* Check if element is clobbered or can clobber */\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Now let's check the element's type and name */\n const tagName = transformCaseFunc(currentNode.nodeName);\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeElement, currentNode, {\n tagName,\n allowedTags: ALLOWED_TAGS\n });\n /* Detect mXSS attempts abusing namespace confusion */\n if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\\w!]/g, currentNode.textContent)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any occurrence of processing instructions */\n if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove any kind of possibly harmful comments */\n if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\\w]/g, currentNode.data)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Remove element if anything forbids its presence */\n if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {\n /* Check if we have a custom element to handle */\n if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {\n return false;\n }\n if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {\n return false;\n }\n }\n /* Keep content except for bad-listed elements */\n if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n const parentNode = getParentNode(currentNode) || currentNode.parentNode;\n const childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n if (childNodes && parentNode) {\n const childCount = childNodes.length;\n for (let i = childCount - 1; i >= 0; --i) {\n const childClone = cloneNode(childNodes[i], true);\n childClone.__removalCount = (currentNode.__removalCount || 0) + 1;\n parentNode.insertBefore(childClone, getNextSibling(currentNode));\n }\n }\n }\n _forceRemove(currentNode);\n return true;\n }\n /* Check whether element has a valid namespace */\n if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Make sure that older browsers don't get fallback-tag mXSS */\n if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\\/no(script|embed|frames)/i, currentNode.innerHTML)) {\n _forceRemove(currentNode);\n return true;\n }\n /* Sanitize element content to be template-safe */\n if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {\n /* Get the element's text content */\n content = currentNode.textContent;\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n content = stringReplace(content, expr, ' ');\n });\n if (currentNode.textContent !== content) {\n arrayPush(DOMPurify.removed, {\n element: currentNode.cloneNode()\n });\n currentNode.textContent = content;\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeElements, currentNode, null);\n return false;\n };\n /**\n * _isValidAttribute\n *\n * @param lcTag Lowercase tag name of containing element.\n * @param lcName Lowercase attribute name.\n * @param value Attribute value.\n * @return Returns true if `value` is valid, otherwise false.\n */\n // eslint-disable-next-line complexity\n const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */\n if (FORBID_ATTR[lcName]) {\n return false;\n }\n /* Make sure attribute cannot clobber */\n if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n return false;\n }\n /* Allow valid data-* attributes: At least one character after \"-\"\n (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)\n XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)\n We don't need to check the value; it's always URI safe. */\n if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n if (\n // First condition does a very basic check if a) it's basically a valid custom element tagname AND\n // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck\n _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||\n // Alternative, second condition checks if it's an `is`-attribute, AND\n // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck\n lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {\n return false;\n }\n /* Check value is safe. First, is attr inert? If so, is safe */\n } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {\n return false;\n } else ;\n return true;\n };\n /**\n * _isBasicCustomElement\n * checks if at least one dash is included in tagName, and it's not the first char\n * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name\n *\n * @param tagName name of the tag of the node to sanitize\n * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.\n */\n const _isBasicCustomElement = function _isBasicCustomElement(tagName) {\n return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);\n };\n /**\n * _sanitizeAttributes\n *\n * @protect attributes\n * @protect nodeName\n * @protect removeAttribute\n * @protect setAttribute\n *\n * @param currentNode to sanitize\n */\n const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);\n const {\n attributes\n } = currentNode;\n /* Check if we have attributes; if not we might have a text node */\n if (!attributes || _isClobbered(currentNode)) {\n return;\n }\n const hookEvent = {\n attrName: '',\n attrValue: '',\n keepAttr: true,\n allowedAttributes: ALLOWED_ATTR,\n forceKeepAttr: undefined\n };\n let l = attributes.length;\n /* Go backwards over all attributes; safely remove bad ones */\n while (l--) {\n const attr = attributes[l];\n const {\n name,\n namespaceURI,\n value: attrValue\n } = attr;\n const lcName = transformCaseFunc(name);\n const initValue = attrValue;\n let value = name === 'value' ? initValue : stringTrim(initValue);\n /* Execute a hook if present */\n hookEvent.attrName = lcName;\n hookEvent.attrValue = value;\n hookEvent.keepAttr = true;\n hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set\n _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);\n value = hookEvent.attrValue;\n /* Full DOM Clobbering protection via namespace isolation,\n * Prefix id and name attributes with `user-content-`\n */\n if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {\n // Remove the attribute with this value\n _removeAttribute(name, currentNode);\n // Prefix the value and later re-create the attribute with the sanitized value\n value = SANITIZE_NAMED_PROPS_PREFIX + value;\n }\n /* Work around a security issue with comments inside attributes */\n if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Make sure we cannot easily use animated hrefs, even if animations are allowed */\n if (lcName === 'attributename' && stringMatch(value, 'href')) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (hookEvent.forceKeepAttr) {\n continue;\n }\n /* Did the hooks approve of the attribute? */\n if (!hookEvent.keepAttr) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Work around a security issue in jQuery 3.0 */\n if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\\/>/i, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Sanitize attribute content to be template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n value = stringReplace(value, expr, ' ');\n });\n }\n /* Is `value` valid for this attribute? */\n const lcTag = transformCaseFunc(currentNode.nodeName);\n if (!_isValidAttribute(lcTag, lcName, value)) {\n _removeAttribute(name, currentNode);\n continue;\n }\n /* Handle attributes that require Trusted Types */\n if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {\n if (namespaceURI) ; else {\n switch (trustedTypes.getAttributeType(lcTag, lcName)) {\n case 'TrustedHTML':\n {\n value = trustedTypesPolicy.createHTML(value);\n break;\n }\n case 'TrustedScriptURL':\n {\n value = trustedTypesPolicy.createScriptURL(value);\n break;\n }\n }\n }\n }\n /* Handle invalid data-* attribute set by try-catching it */\n if (value !== initValue) {\n try {\n if (namespaceURI) {\n currentNode.setAttributeNS(namespaceURI, name, value);\n } else {\n /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. \"x-schema\". */\n currentNode.setAttribute(name, value);\n }\n if (_isClobbered(currentNode)) {\n _forceRemove(currentNode);\n } else {\n arrayPop(DOMPurify.removed);\n }\n } catch (_) {\n _removeAttribute(name, currentNode);\n }\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);\n };\n /**\n * _sanitizeShadowDOM\n *\n * @param fragment to iterate over recursively\n */\n const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n let shadowNode = null;\n const shadowIterator = _createNodeIterator(fragment);\n /* Execute a hook if present */\n _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);\n while (shadowNode = shadowIterator.nextNode()) {\n /* Execute a hook if present */\n _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);\n /* Sanitize tags and elements */\n _sanitizeElements(shadowNode);\n /* Check attributes next */\n _sanitizeAttributes(shadowNode);\n /* Deep shadow DOM detected */\n if (shadowNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(shadowNode.content);\n }\n }\n /* Execute a hook if present */\n _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);\n };\n // eslint-disable-next-line complexity\n DOMPurify.sanitize = function (dirty) {\n let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n let body = null;\n let importedNode = null;\n let currentNode = null;\n let returnNode = null;\n /* Make sure we have a string to sanitize.\n DO NOT return early, as this will return the wrong type if\n the user has requested a DOM object rather than a string */\n IS_EMPTY_INPUT = !dirty;\n if (IS_EMPTY_INPUT) {\n dirty = '<!-->';\n }\n /* Stringify, in case dirty is an object */\n if (typeof dirty !== 'string' && !_isNode(dirty)) {\n if (typeof dirty.toString === 'function') {\n dirty = dirty.toString();\n if (typeof dirty !== 'string') {\n throw typeErrorCreate('dirty is not a string, aborting');\n }\n } else {\n throw typeErrorCreate('toString is not a function');\n }\n }\n /* Return dirty HTML if DOMPurify cannot run */\n if (!DOMPurify.isSupported) {\n return dirty;\n }\n /* Assign config vars */\n if (!SET_CONFIG) {\n _parseConfig(cfg);\n }\n /* Clean up removed elements */\n DOMPurify.removed = [];\n /* Check if dirty is correctly typed for IN_PLACE */\n if (typeof dirty === 'string') {\n IN_PLACE = false;\n }\n if (IN_PLACE) {\n /* Do some early pre-sanitization to avoid unsafe root nodes */\n if (dirty.nodeName) {\n const tagName = transformCaseFunc(dirty.nodeName);\n if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');\n }\n }\n } else if (dirty instanceof Node) {\n /* If dirty is a DOM element, append to an empty document to avoid\n elements being stripped by the parser */\n body = _initDocument('<!---->');\n importedNode = body.ownerDocument.importNode(dirty, true);\n if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {\n /* Node is already a body, use as is */\n body = importedNode;\n } else if (importedNode.nodeName === 'HTML') {\n body = importedNode;\n } else {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n body.appendChild(importedNode);\n }\n } else {\n /* Exit directly if we have nothing to do */\n if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&\n // eslint-disable-next-line unicorn/prefer-includes\n dirty.indexOf('<') === -1) {\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n }\n /* Initialize the document to work on */\n body = _initDocument(dirty);\n /* Check we have a DOM node from the data */\n if (!body) {\n return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n }\n }\n /* Remove first element node (ours) if FORCE_BODY is set */\n if (body && FORCE_BODY) {\n _forceRemove(body.firstChild);\n }\n /* Get node iterator */\n const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);\n /* Now start iterating over the created document */\n while (currentNode = nodeIterator.nextNode()) {\n /* Sanitize tags and elements */\n _sanitizeElements(currentNode);\n /* Check attributes next */\n _sanitizeAttributes(currentNode);\n /* Shadow DOM detected, sanitize it */\n if (currentNode.content instanceof DocumentFragment) {\n _sanitizeShadowDOM(currentNode.content);\n }\n }\n /* If we sanitized `dirty` in-place, return it. */\n if (IN_PLACE) {\n return dirty;\n }\n /* Return sanitized string or DOM */\n if (RETURN_DOM) {\n if (RETURN_DOM_FRAGMENT) {\n returnNode = createDocumentFragment.call(body.ownerDocument);\n while (body.firstChild) {\n // eslint-disable-next-line unicorn/prefer-dom-node-append\n returnNode.appendChild(body.firstChild);\n }\n } else {\n returnNode = body;\n }\n if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {\n /*\n AdoptNode() is not used because internal state is not reset\n (e.g. the past names map of a HTMLFormElement), this is safe\n in theory but we would rather not risk another attack vector.\n The state that is cloned by importNode() is explicitly defined\n by the specs.\n */\n returnNode = importNode.call(originalDocument, returnNode, true);\n }\n return returnNode;\n }\n let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n /* Serialize doctype if allowed */\n if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {\n serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\\n' + serializedHTML;\n }\n /* Sanitize final string template-safe */\n if (SAFE_FOR_TEMPLATES) {\n arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {\n serializedHTML = stringReplace(serializedHTML, expr, ' ');\n });\n }\n return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n };\n DOMPurify.setConfig = function () {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _parseConfig(cfg);\n SET_CONFIG = true;\n };\n DOMPurify.clearConfig = function () {\n CONFIG = null;\n SET_CONFIG = false;\n };\n DOMPurify.isValidAttribute = function (tag, attr, value) {\n /* Initialize shared config vars if necessary. */\n if (!CONFIG) {\n _parseConfig({});\n }\n const lcTag = transformCaseFunc(tag);\n const lcName = transformCaseFunc(attr);\n return _isValidAttribute(lcTag, lcName, value);\n };\n DOMPurify.addHook = function (entryPoint, hookFunction) {\n if (typeof hookFunction !== 'function') {\n return;\n }\n arrayPush(hooks[entryPoint], hookFunction);\n };\n DOMPurify.removeHook = function (entryPoint, hookFunction) {\n if (hookFunction !== undefined) {\n const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);\n return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];\n }\n return arrayPop(hooks[entryPoint]);\n };\n DOMPurify.removeHooks = function (entryPoint) {\n hooks[entryPoint] = [];\n };\n DOMPurify.removeAllHooks = function () {\n hooks = _createHooksMap();\n };\n return DOMPurify;\n}\nvar purify = createDOMPurify();\n\nexport { purify as default };\n//# sourceMappingURL=purify.es.mjs.map\n","// src/browser.ts\nimport DOMPurify from \"dompurify\";\nvar browser_default = DOMPurify;\nvar sanitize = DOMPurify.sanitize.bind(DOMPurify);\nvar isSupported = DOMPurify.isSupported;\nvar addHook = DOMPurify.addHook.bind(DOMPurify);\nvar removeHook = DOMPurify.removeHook.bind(DOMPurify);\nvar removeHooks = DOMPurify.removeHooks.bind(DOMPurify);\nvar removeAllHooks = DOMPurify.removeAllHooks.bind(DOMPurify);\nvar setConfig = DOMPurify.setConfig.bind(DOMPurify);\nvar clearConfig = DOMPurify.clearConfig.bind(DOMPurify);\nvar isValidAttribute = DOMPurify.isValidAttribute.bind(DOMPurify);\nvar version = DOMPurify.version;\nvar removed = DOMPurify.removed;\nfunction clearWindow() {\n}\nexport {\n addHook,\n clearConfig,\n clearWindow,\n browser_default as default,\n isSupported,\n isValidAttribute,\n removeAllHooks,\n removeHook,\n removeHooks,\n removed,\n sanitize,\n setConfig,\n version\n};\n","import DOMPurify from \"isomorphic-dompurify\";\n\nconst BASIC_NAMED_HTML_ENTITIES: Record<string, string> = {\n amp: \"&\",\n apos: \"'\",\n gt: \">\",\n lt: \"<\",\n nbsp: \"\\u00A0\",\n quot: '\"',\n};\n\nconst decodeHtmlEntity = (entity: string): string => {\n const normalizedEntity = entity.toLowerCase();\n\n if (normalizedEntity.startsWith(\"&#x\")) {\n const codePoint = Number.parseInt(normalizedEntity.slice(3, -1), 16);\n\n try {\n return Number.isNaN(codePoint) ? entity : String.fromCodePoint(codePoint);\n } catch {\n return entity;\n }\n }\n\n if (normalizedEntity.startsWith(\"&#\")) {\n const codePoint = Number.parseInt(normalizedEntity.slice(2, -1), 10);\n\n try {\n return Number.isNaN(codePoint) ? entity : String.fromCodePoint(codePoint);\n } catch {\n return entity;\n }\n }\n\n const namedEntity = normalizedEntity.slice(1, -1);\n return BASIC_NAMED_HTML_ENTITIES[namedEntity] ?? entity;\n};\n\nconst decodeBasicHtmlEntities = (str: string): string =>\n str.replace(/&(?:#\\d+|#x[\\da-f]+|amp|lt|gt|quot|apos|nbsp);/gi, (entity) =>\n decodeHtmlEntity(entity),\n );\n\nexport const stripTagsForSsr = (str: string): string => {\n const textWithEntities = DOMPurify.sanitize(`<span>${str}</span>`, {\n ALLOWED_ATTR: [],\n ALLOWED_TAGS: [],\n KEEP_CONTENT: true,\n });\n return decodeBasicHtmlEntities(textWithEntities);\n};\n\nexport const stripTags = (str: string) => {\n if (!str) return \"\";\n\n return stripTagsForSsr(str);\n};\n","import type { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport interface SocialShareParams {\n url: string;\n title?: string;\n description?: string;\n hashtags?: string[];\n}\n\nexport function getFacebookShareUrl({\n url,\n title,\n description,\n}: SocialShareParams): string {\n const params = new URLSearchParams({\n u: url,\n ...(title && { quote: title }),\n ...(description && { description }),\n });\n\n return `https://www.facebook.com/sharer/sharer.php?${params.toString()}`;\n}\n\nexport function getXShareUrl({\n url,\n title,\n hashtags,\n}: SocialShareParams): string {\n const params = new URLSearchParams({\n url,\n ...(title && { text: title }),\n ...(hashtags && hashtags.length > 0 && { hashtags: hashtags.join(\",\") }),\n });\n\n return `https://twitter.com/intent/tweet?${params.toString()}`;\n}\n\nexport function getLinkedInShareUrl({\n url,\n title,\n description,\n}: SocialShareParams): string {\n const params = new URLSearchParams({\n url,\n ...(title && { title }),\n ...(description && { summary: description }),\n });\n\n return `https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`;\n}\n\nexport function handleInstagramShare({\n url,\n}: SocialShareParams): Promise<void> {\n return navigator.clipboard.writeText(url).then(() => {\n setTimeout(() => {\n window.open(\"https://www.instagram.com\", \"_blank\");\n }, 2000);\n });\n}\n\nexport function handleTikTokShare({ url }: SocialShareParams): Promise<void> {\n return navigator.clipboard.writeText(url).then(() => {\n setTimeout(() => {\n window.open(\"https://www.tiktok.com\", \"_blank\");\n }, 2000);\n });\n}\n\nexport function openSocialShare(shareUrl: string): void {\n const width = 600;\n const height = 400;\n const left = (window.screen.width - width) / 2;\n const top = (window.screen.height - height) / 2;\n\n window.open(\n shareUrl,\n \"social-share\",\n `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`,\n );\n}\n\ntype ShareablesT = ReturnType<typeof useShareablesTranslation>[\"t\"];\n\nexport interface SocialShareLabels {\n share_opening_facebook: string;\n share_opening_x: string;\n share_opening_linkedin: string;\n share_copied_instagram: string;\n share_copied_tiktok: string;\n share_failed: (platform: string) => string;\n}\n\nexport function buildSocialShareLabels(t: ShareablesT): SocialShareLabels {\n return {\n share_opening_facebook: t(\"share_opening_facebook\"),\n share_opening_x: t(\"share_opening_x\"),\n share_opening_linkedin: t(\"share_opening_linkedin\"),\n share_copied_instagram: t(\"share_copied_instagram\"),\n share_copied_tiktok: t(\"share_copied_tiktok\"),\n share_failed: (platform: string) => t(\"share_failed\", { platform }),\n };\n}\n\nexport async function handleSocialShare(\n platform: \"facebook\" | \"x\" | \"linkedin\" | \"instagram\" | \"tiktok\",\n params: SocialShareParams,\n onSuccess?: (message: string) => void,\n onError?: (message: string) => void,\n labels?: SocialShareLabels,\n): Promise<void> {\n try {\n switch (platform) {\n case \"facebook\":\n openSocialShare(getFacebookShareUrl(params));\n onSuccess?.(\n labels?.share_opening_facebook ?? \"Opening Facebook share dialog...\",\n );\n break;\n\n case \"x\":\n openSocialShare(getXShareUrl(params));\n onSuccess?.(labels?.share_opening_x ?? \"Opening X share dialog...\");\n break;\n\n case \"linkedin\":\n openSocialShare(getLinkedInShareUrl(params));\n onSuccess?.(\n labels?.share_opening_linkedin ?? \"Opening LinkedIn share dialog...\",\n );\n break;\n\n case \"instagram\":\n await handleInstagramShare(params);\n onSuccess?.(\n labels?.share_copied_instagram ??\n \"Link copied! Opening Instagram and paste link in bio or post!.\",\n );\n break;\n\n case \"tiktok\":\n await handleTikTokShare(params);\n onSuccess?.(\n labels?.share_copied_tiktok ??\n \"Link copied! Opening TikTok and paste in your link!\",\n );\n break;\n\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n } catch (error) {\n console.error(\"Social sharing error:\", error);\n const failedMsg =\n (labels?.share_failed ? labels.share_failed(platform) : null) ??\n `Failed to share on ${platform}. Please try again.`;\n onError?.(failedMsg);\n }\n}\n","export type OperatingSystem =\n | \"mac\"\n | \"windows\"\n | \"linux\"\n | \"ios\"\n | \"android\"\n | \"unknown\";\n\nexport function getOperatingSystem(): OperatingSystem {\n if (typeof navigator === \"undefined\") {\n return \"unknown\";\n }\n\n const userAgent = navigator.userAgent.toLowerCase();\n const platform = navigator.platform?.toLowerCase() || \"\";\n\n if (/iphone|ipad|ipod/.test(userAgent)) {\n return \"ios\";\n }\n\n if (/android/.test(userAgent)) {\n return \"android\";\n }\n\n if (/mac/.test(platform) || /darwin/.test(userAgent)) {\n return \"mac\";\n }\n\n if (/win/.test(platform) || /windows/.test(userAgent)) {\n return \"windows\";\n }\n\n if (/linux/.test(platform) || /x11/.test(platform)) {\n return \"linux\";\n }\n\n return \"unknown\";\n}\n\nexport function isMobileDevice(): boolean {\n const os = getOperatingSystem();\n return os === \"ios\" || os === \"android\";\n}\n\nexport function isDesktopDevice(): boolean {\n const os = getOperatingSystem();\n return os === \"mac\" || os === \"windows\" || os === \"linux\";\n}\n","import type { FetchClient } from \"../lib/fetch-client\";\nimport type { products } from \"../custom/products\";\n\n// ============================================================================\n// Products - List & Get\n// ============================================================================\n\nexport async function listProducts(\n client: FetchClient,\n params?: products.ListProductsParams,\n): Promise<products.ProductsResponse> {\n const { sorting, ...rest } = params ?? {};\n return client.get(\"/company/v1/products\", {\n ...rest,\n ...(sorting && { order_by: [sorting.id, sorting.desc ? \"desc\" : \"asc\"] }),\n });\n}\n\nexport async function getProduct(\n client: FetchClient,\n id: string | number,\n options?: { lang?: string; country_code?: string },\n): Promise<products.ProductResponse> {\n return client.get(`/company/v1/products/${id}`, {\n ...(options?.lang && { lang: options.lang }),\n ...(options?.country_code && { country_code: options.country_code }),\n });\n}\n\nexport async function listProductsV2(\n client: FetchClient,\n params?: products.ListProductsV2Params,\n): Promise<products.ProductsV2Response> {\n const { sorting, ...rest } = params ?? {};\n return client.get(\"/v2025-06/products\", {\n ...rest,\n ...(sorting && { order_by: [sorting.id, sorting.desc ? \"desc\" : \"asc\"] }),\n });\n}\n\nexport async function searchProducts(\n client: FetchClient,\n searchTerm: string,\n countryCodes: readonly string[],\n): Promise<products.ProductsResponse> {\n const input: Record<string, unknown> = {\n limit: 100,\n };\n if (searchTerm) {\n input.search_query = searchTerm;\n }\n if (countryCodes && countryCodes.length > 0) {\n input.country_code = countryCodes;\n }\n return client.get(\"/company/v1/products\", input);\n}\n\n// ============================================================================\n// Products - Create, Delete, Duplicate\n// ============================================================================\n\nexport async function createProduct(\n client: FetchClient,\n product: products.CreateProduct,\n): Promise<products.ProductResponse> {\n return client.post(\"/company/v1/products\", { product });\n}\n\nexport async function updateProduct(\n client: FetchClient,\n id: number | string,\n product: products.UpdateProduct,\n options?: products.UpdateProductParams,\n): Promise<products.ProductResponse> {\n return client.patch(`/company/v1/products/${id}`, {\n id: String(id),\n product,\n ...(options?.lang && { lang: options.lang }),\n ...(options?.dam_asset_codes &&\n options.dam_asset_codes.length > 0 && {\n dam_asset_codes: options.dam_asset_codes,\n }),\n });\n}\n\nexport async function deleteProduct(\n client: FetchClient,\n id: number,\n): Promise<products.DeleteProductResponse> {\n return client.delete(`/company/v1/products/${id}`);\n}\n\nexport async function duplicateProduct(\n client: FetchClient,\n id: number,\n): Promise<products.DuplicateProductResponse> {\n return client.post(`/company/v1/products/${id}/duplicate`);\n}\n\n// ============================================================================\n// Products - Status Changes\n// ============================================================================\n\nexport async function archiveProduct(\n client: FetchClient,\n product: { id: number; title: string },\n): Promise<products.ProductResponse> {\n return client.patch(`/company/v1/products/${product.id}`, {\n id: product.id.toString(),\n product: {\n title: product.title || \"\",\n status: \"archived\",\n },\n });\n}\n\nexport async function unarchiveProduct(\n client: FetchClient,\n product: { id: number; title: string },\n): Promise<products.ProductResponse> {\n return client.patch(`/company/v1/products/${product.id}`, {\n id: product.id.toString(),\n product: {\n title: product.title || \"\",\n status: \"active\",\n },\n });\n}\n\nexport async function draftProduct(\n client: FetchClient,\n product: { id: number; title: string },\n): Promise<products.ProductResponse> {\n return client.patch(`/company/v1/products/${product.id}`, {\n id: product.id.toString(),\n product: {\n title: product.title || \"\",\n status: \"draft\",\n },\n });\n}\n\nexport async function sendProductToTop(\n client: FetchClient,\n id: number,\n): Promise<products.SendToTopResponse> {\n return client.patch(`/company/v1/products/${id}/send_to_top`);\n}\n","import type { Provider } from \"react\";\nimport { createTranslationContext } from \"@fluid-app/i18n/translation-api-context-factory\";\nimport type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ShareablesDict } from \"./translation-dictionary\";\n\nconst { Provider: ShareablesProvider, useTranslation } =\n createTranslationContext<ShareablesDict>(\"Shareables\");\n\nexport const ShareablesTranslationProvider: Provider<TranslationApi<ShareablesDict> | null> =\n ShareablesProvider;\nexport const useShareablesTranslation = useTranslation;\n","\"use client\";\n\nimport type React from \"react\";\nimport { Card, Badge } from \"@fluid-app/ui-primitives\";\nimport { CirclePlay } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI, useRenderImage } from \"../../context\";\n\nexport interface ShareItemCardProps {\n /** Display title */\n title: string;\n /** Image URL to display */\n imageUrl?: string | null;\n /** Navigation URL when card is clicked */\n href: string;\n /** Optional badge to display (e.g., \"5 items\", \"12 assets\") */\n badge?: {\n text: string;\n isLoading?: boolean;\n };\n /** Whether this item is a video */\n isVideo?: boolean;\n /** Subtitle content below the title */\n subtitle?: React.ReactNode;\n}\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport default function ShareItemCard({\n title,\n imageUrl,\n href,\n badge,\n isVideo = false,\n subtitle,\n}: ShareItemCardProps): React.JSX.Element {\n const { navigate } = useShareablesUI();\n const renderImage = useRenderImage();\n const { t } = useShareablesTranslation();\n const resolvedSubtitle = subtitle ?? t(\"share_click_to_find_assets\");\n\n function handleClick() {\n navigate(href);\n }\n\n return (\n <Card\n role=\"button\"\n tabIndex={0}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleClick();\n }\n }}\n className=\"group hover:bg-muted cursor-pointer gap-0 overflow-hidden rounded-lg border-0 p-0 shadow-none transition-colors\"\n >\n {/* Image container */}\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: imageUrl || DEFAULT_IMAGE,\n alt: title,\n fill: true,\n className: \"object-cover transition-transform group-hover:scale-105\",\n })}\n\n {/* Video play indicator */}\n {isVideo && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-foreground/50 flex size-16 items-center justify-center rounded-full backdrop-blur-sm\">\n <CirclePlay className=\"text-background size-12\" />\n </div>\n </div>\n )}\n\n {/* Badge */}\n {badge && !isVideo && !badge.isLoading && (\n <Badge className=\"absolute top-2 right-2 shadow-lg\" variant=\"default\">\n {badge.text}\n </Badge>\n )}\n\n {/* Loading indicator for badge */}\n {badge?.isLoading && !isVideo && (\n <div className=\"bg-muted-foreground absolute top-2 right-2 size-6 animate-pulse rounded-full\" />\n )}\n </div>\n\n {/* Item info */}\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {title || t(\"common_untitled\")}\n </h3>\n <p className=\"text-muted-foreground mt-1 text-xs\">{resolvedSubtitle}</p>\n </div>\n </Card>\n );\n}\n","import type React from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport ShareItemCard from \"../ShareItemCard\";\n\ninterface ShareProductCardProps {\n product: {\n id: number;\n title: string;\n image_url?: string | null;\n images?: Array<{ image_url: string; position?: number }> | null;\n kind?: string;\n video_url?: string;\n };\n mediaCount?: number;\n}\n\nfunction getProductImageUrl(\n product: ShareProductCardProps[\"product\"],\n): string | null {\n if (Array.isArray(product.images) && product.images.length > 0) {\n const sortedImages = product.images.toSorted(\n (a, b) => (a.position ?? 0) - (b.position ?? 0),\n );\n const primaryImage = sortedImages[0];\n if (primaryImage?.image_url) {\n return primaryImage.image_url;\n }\n }\n return product.image_url ?? null;\n}\n\nexport default function ShareProductCard({\n product,\n mediaCount,\n}: ShareProductCardProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const imageUrl = getProductImageUrl(product);\n const isVideo = product.kind === \"video\" && !!product.video_url;\n\n return (\n <ShareItemCard\n title={product.title}\n imageUrl={imageUrl}\n href={`product/${product.id}`}\n badge={\n !isVideo && mediaCount != null\n ? { text: t(\"products_asset_count\", { count: String(mediaCount) }) }\n : undefined\n }\n isVideo={isVideo}\n />\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport type { ReactNode, RefObject } from \"react\";\nimport { Skeleton } from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\n/**\n * Grid column class shared by all shareable listing screens (products, media,\n * playlists, files). Exposed so the list-content slot can reuse it directly.\n */\nexport const SHAREABLE_GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nexport interface ShareableListLayoutProps {\n /** Filter bar slot — rendered above the grid/list when provided. */\n filters?: ReactNode;\n /** Main content — grid, list, or bulk-selection area. */\n children?: ReactNode;\n /** Loading state. When true, a skeleton screen is rendered. */\n isLoading?: boolean;\n /** When provided and truthy, an error message is rendered in place of content. */\n error?: unknown;\n /** Error text (defaults to generic message). */\n errorMessage?: string;\n /** Empty state — rendered in place of children when true. Must be paired with `emptyMessage`. */\n isEmpty?: boolean;\n /** Empty state message. */\n emptyMessage?: ReactNode;\n /** Pagination / load-more footer slot (rendered below content). */\n footer?: ReactNode;\n /**\n * Shape of the filter-bar skeleton rendered while loading.\n * - `\"search\"` — single skeleton bar. The default, used by files and pages.\n * - `\"search-view\"` — search + view toggle. Used by media, playlists and products.\n * - `\"search-action\"` — search + primary action. No current callers.\n */\n loadingFilterShape?: \"search\" | \"search-view\" | \"search-action\";\n /** Infinite-scroll sentinel ref (rendered as a 1px div below content). */\n sentinelRef?: RefObject<HTMLDivElement | null>;\n}\n\n/**\n * Standard page chrome for shareable listing screens. Provides:\n *\n * - Page-level padding (matching all existing listing screens).\n * - Loading skeleton (filter bar + grid of 8 card placeholders).\n * - Inline error rendering.\n * - Empty state rendering.\n * - Optional filter/footer/sentinel slots.\n *\n * Domain-specific UI (cards, rows, filter controls) is slotted in via\n * `filters` and `children`. This component does NOT own data fetching or\n * any state — callers pass in the derived flags.\n */\nexport function ShareableListLayout({\n filters,\n children,\n isLoading,\n error,\n errorMessage,\n isEmpty,\n emptyMessage,\n footer,\n loadingFilterShape = \"search\",\n sentinelRef,\n}: ShareableListLayoutProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const resolvedErrorMessage = errorMessage ?? t(\"common_load_error_default\");\n\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-10 flex-1\" />\n {loadingFilterShape === \"search-view\" && (\n <Skeleton className=\"size-10\" />\n )}\n {loadingFilterShape === \"search-action\" && (\n <Skeleton className=\"h-10 w-24\" />\n )}\n </div>\n <div className={SHAREABLE_GRID_CLASS}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">{resolvedErrorMessage}</p>\n </div>\n );\n }\n\n return (\n // overflow-x-clip contains decorative overhang (e.g. the rotated playlist\n // card \"stack\" fan) so it can't push the mobile viewport into a horizontal\n // scroll. The fan still shows within the page's horizontal padding.\n <div className=\"space-y-6 overflow-x-clip px-4 py-4 md:px-10 md:py-6\">\n {filters}\n {isEmpty ? (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-muted-foreground text-sm\">{emptyMessage}</p>\n </div>\n ) : (\n children\n )}\n {footer}\n {sentinelRef && <div ref={sentinelRef} className=\"h-1\" />}\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\n/**\n * Container className for a list-view rendering of shareables. Pairs with\n * `ShareableListRow` and mirrors `SHAREABLE_GRID_CLASS` for symmetry.\n */\nexport const SHAREABLE_LIST_CLASS = \"divide-border divide-y rounded-lg border\";\n\nexport interface ShareableListRowProps {\n /** Image URL for the row thumbnail. Falls back to a muted block. */\n imageUrl?: string | null;\n /** Alt text for the thumbnail image (defaults to title). */\n imageAlt?: string;\n /** Primary row label. Falls back to \"Untitled\" when empty. */\n title: string;\n /** Secondary content rendered beneath the title (badges, type label, etc.). */\n subtitle?: ReactNode;\n /** Click handler for the row body (thumbnail + title region). */\n onClick?: () => void;\n /**\n * Slot rendered before the thumbnail (e.g. selection checkbox). Sits as a\n * sibling of the navigation button so its clicks don't bubble through it.\n */\n leading?: ReactNode;\n /**\n * Slot rendered after the title region (e.g. favorite toggle, action menu).\n * Same sibling-positioning as `leading`.\n */\n trailing?: ReactNode;\n}\n\n/**\n * Single row in a shareables list view. The row body (thumbnail + title) is\n * a `<button>` for keyboard/click navigation; `leading` and `trailing` slots\n * are siblings so their interactive elements don't nest inside the body\n * button. Hover styling applies to the entire row container.\n */\nexport function ShareableListRow({\n imageUrl,\n imageAlt,\n title,\n subtitle,\n onClick,\n leading,\n trailing,\n}: ShareableListRowProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n return (\n <div className=\"hover:bg-muted flex items-center gap-3 px-4 py-3 transition-colors\">\n {leading}\n <button\n type=\"button\"\n onClick={onClick}\n className=\"flex min-w-0 flex-1 items-center gap-4 text-left\"\n >\n <div className=\"bg-muted size-12 shrink-0 overflow-hidden rounded-md\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAlt ?? title}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"bg-muted h-full w-full\" />\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-foreground truncate text-sm font-medium\">\n {title || t(\"common_untitled\")}\n </p>\n {subtitle != null && subtitle !== false && (\n <p className=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n {subtitle}\n </p>\n )}\n </div>\n </button>\n {trailing}\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { LayoutGrid, List } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport type ViewMode = \"grid\" | \"list\";\n\nexport interface ViewModeToggleProps {\n value: ViewMode;\n onChange: (mode: ViewMode) => void;\n}\n\n/**\n * Segmented list/grid toggle used in the filter bar of every shareable\n * listing screen. Stateless — callers own the `viewMode` state.\n */\nexport function ViewModeToggle({\n value,\n onChange,\n}: ViewModeToggleProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n return (\n <div className=\"border-input bg-muted flex items-center rounded-lg border p-0.5\">\n <ToggleButton\n active={value === \"list\"}\n label={t(\"common_list_view\")}\n onClick={() => onChange(\"list\")}\n >\n <List className=\"size-4\" />\n </ToggleButton>\n <ToggleButton\n active={value === \"grid\"}\n label={t(\"common_grid_view\")}\n onClick={() => onChange(\"grid\")}\n >\n <LayoutGrid className=\"size-4\" />\n </ToggleButton>\n </div>\n );\n}\n\nfunction ToggleButton({\n active,\n label,\n onClick,\n children,\n}: {\n active: boolean;\n label: string;\n onClick: () => void;\n children: React.ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={label}\n aria-label={label}\n aria-pressed={active}\n className={cn(\n \"flex h-8 w-8 items-center justify-center rounded-md transition-all\",\n active\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n >\n {children}\n </button>\n );\n}\n","import { useEffect, useRef, type RefObject } from \"react\";\n\nexport interface UseInfiniteListSentinelOptions {\n /** TanStack Query `hasNextPage` flag from `useInfiniteQuery`. */\n hasNextPage: boolean;\n /** TanStack Query `isFetchingNextPage` flag from `useInfiniteQuery`. */\n isFetchingNextPage: boolean;\n /** TanStack Query `fetchNextPage` callback from `useInfiniteQuery`. */\n fetchNextPage: () => void;\n /**\n * TanStack Query `error` from `useInfiniteQuery`. When truthy, the sentinel\n * stops requesting additional pages so a transient next-page failure doesn't\n * spin into a retry loop while the sentinel is still on screen. Pass\n * `undefined`/`null` when no error is present.\n */\n error: unknown;\n}\n\n/**\n * Wires a sentinel `<div>` to TanStack Query infinite-pagination state.\n *\n * Returns a ref the caller attaches to a 1px sentinel element rendered below\n * the list. When the sentinel scrolls into view (with a 200px root margin)\n * and the query has a next page, isn't already fetching, and hasn't errored,\n * `fetchNextPage()` is invoked.\n */\nexport function useInfiniteListSentinel({\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n error,\n}: UseInfiniteListSentinelOptions): RefObject<HTMLDivElement | null> {\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const target = sentinelRef.current;\n if (!target) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (\n entries[0]?.isIntersecting &&\n hasNextPage &&\n !isFetchingNextPage &&\n !error\n ) {\n fetchNextPage();\n }\n },\n { threshold: 0, rootMargin: \"200px\" },\n );\n observer.observe(target);\n return () => observer.disconnect();\n }, [fetchNextPage, hasNextPage, isFetchingNextPage, error]);\n\n return sentinelRef;\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useMemo } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { listProducts } from \"@fluid-app/products-api-client\";\nimport { useShareablesClient } from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport ShareProductCard from \"../MediaShare/products/ShareProductCard\";\nimport type { PortalProductsResponse } from \"../portal-product-types\";\nimport {\n ShareableListLayout,\n SHAREABLE_GRID_CLASS,\n} from \"../shared/ShareableListLayout\";\nimport {\n ShareableListRow,\n SHAREABLE_LIST_CLASS,\n} from \"../shared/ShareableListRow\";\nimport { ViewModeToggle, type ViewMode } from \"../shared/ViewModeToggle\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport { useInfiniteListSentinel } from \"../../hooks/use-infinite-list-sentinel\";\n\nconst PAGE_SIZE = 24;\n\n// SORT_OPTIONS moved into component as useMemo for i18n\n\nexport interface ProductsScreenProps {\n countryCode?: string;\n fetchProducts?: (\n search: string,\n cursor?: string,\n limit?: number,\n sort?: string,\n ) => Promise<PortalProductsResponse>;\n onNavigate?: (screen: string, detailId?: string) => void;\n}\n\nexport function ProductsScreen({\n countryCode,\n fetchProducts: fetchPortalProducts,\n onNavigate: _onNavigate,\n}: ProductsScreenProps): React.JSX.Element {\n const client = useShareablesClient();\n const { navigate } = useShareablesUI();\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n\n const SORT_OPTIONS = useMemo(\n () => [\n { label: t(\"sort_newest\"), value: \"created_at_desc\" },\n { label: t(\"sort_oldest\"), value: \"created_at_asc\" },\n { label: t(\"sort_title_asc\"), value: \"title_asc\" },\n { label: t(\"sort_title_desc\"), value: \"title_desc\" },\n { label: t(\"sort_price_asc\"), value: \"price_asc\" },\n { label: t(\"sort_price_desc\"), value: \"price_desc\" },\n ],\n [t],\n );\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [sortValue, setSortValue] = useState(\"created_at_desc\");\n const [viewMode, setViewMode] = useState<ViewMode>(\"grid\");\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearch(searchTerm);\n }, 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n // Sort is only applied to the list endpoint; the search endpoint uses\n // relevance ranking, so exclude sortValue from the key during search to\n // avoid unnecessary cache entries.\n const effectiveSort = debouncedSearch ? undefined : sortValue;\n\n // Portal-tenant query (cursor-based)\n const portalQuery = useInfiniteQuery({\n queryKey: [\n \"portal-product-catalog\",\n debouncedSearch || \"\",\n PAGE_SIZE,\n effectiveSort,\n ],\n queryFn: async ({ pageParam }) => {\n return fetchPortalProducts!(\n debouncedSearch,\n pageParam,\n PAGE_SIZE,\n effectiveSort,\n );\n },\n getNextPageParam: (lastPage) =>\n lastPage.meta?.pagination?.next_cursor ?? undefined,\n initialPageParam: undefined as string | undefined,\n enabled: !!fetchPortalProducts,\n });\n\n // Legacy query (page-based) — used when fetchProducts prop is not provided\n const legacyQuery = useInfiniteQuery({\n queryKey: [\"shareables-products\", debouncedSearch, countryCode],\n queryFn: async ({ pageParam = 1 }) => {\n // Invariant: `enabled` guarantees `client` is non-null when this runs.\n if (!client) throw new Error(\"Unreachable: client is null\");\n const response = await listProducts(client, {\n page: pageParam,\n per_page: PAGE_SIZE,\n search_query: debouncedSearch || undefined,\n sorting: { id: \"title\", desc: false },\n status: [\"active\"],\n country_code: countryCode ? [countryCode] : [\"US\"],\n published_stores: [\"rep\"],\n });\n return response.products;\n },\n getNextPageParam: (lastPage, allPages) =>\n lastPage.length === PAGE_SIZE ? allPages.length + 1 : undefined,\n initialPageParam: 1,\n enabled: !fetchPortalProducts && !!client,\n });\n\n // When neither `fetchPortalProducts` nor `client` is available, both\n // queries are disabled and the screen renders with an empty product list.\n const usePortal = !!fetchPortalProducts;\n const activeQuery = usePortal ? portalQuery : legacyQuery;\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = activeQuery;\n\n const sentinelRef = useInfiniteListSentinel({\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n error,\n });\n\n const products = useMemo(() => {\n if (!data) return [];\n if (usePortal) {\n const portalData = data as typeof portalQuery.data;\n return (\n portalData?.pages.flatMap((page) =>\n (page.products ?? []).map((p) => ({\n id: p.id ?? 0,\n title: p.name ?? \"\",\n image_url: p.images?.[0]?.url ?? null,\n media_count: p.media_count,\n })),\n ) ?? []\n );\n }\n const legacyData = data as typeof legacyQuery.data;\n return (\n legacyData?.pages.flat().map((p) => ({\n id: p.id,\n title: p.title ?? \"\",\n image_url:\n p.image_url ??\n (\n p.images as\n | Array<{ image_url: string; position?: number }>\n | undefined\n )?.[0]?.image_url ??\n null,\n media_count: undefined as number | undefined,\n })) ?? []\n );\n }, [data, usePortal]);\n\n const footer = (\n <>\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary size-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n {error && (\n <p className=\"bg-destructive/10 text-destructive rounded-lg px-3 py-2\">\n {t(\"products_load_error\")}\n </p>\n )}\n </>\n );\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"products_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableListLayout\n isLoading={isLoading}\n isEmpty={\n !error && products.length === 0 && !isFetchingNextPage && !hasNextPage\n }\n emptyMessage={\n debouncedSearch\n ? t(\"products_search_no_results\")\n : t(\"products_empty_default\")\n }\n filters={\n <div className=\"bg-background sticky top-0 z-10 -mx-4 flex items-center justify-end gap-2 px-4 py-2 md:static md:mx-0 md:bg-transparent md:px-0 md:py-0\">\n <div className=\"w-full md:max-w-sm\">\n <SearchSort\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n searchValue={searchTerm}\n onSearchChange={setSearchTerm}\n placeholder={t(\"products_search_placeholder\")}\n sortOptions={SORT_OPTIONS}\n sortValue={sortValue}\n onSortChange={setSortValue}\n />\n </div>\n\n <ViewModeToggle value={viewMode} onChange={setViewMode} />\n </div>\n }\n loadingFilterShape=\"search-view\"\n footer={footer}\n sentinelRef={sentinelRef}\n >\n {viewMode === \"grid\" ? (\n <div className={SHAREABLE_GRID_CLASS}>\n {products.map((product) => (\n <ShareProductCard\n key={product.id}\n product={{\n id: product.id,\n title: product.title,\n image_url: product.image_url,\n }}\n mediaCount={product.media_count}\n />\n ))}\n </div>\n ) : (\n <div className={SHAREABLE_LIST_CLASS}>\n {products.map((product) => (\n <ShareableListRow\n key={product.id}\n imageUrl={product.image_url}\n title={product.title}\n subtitle={\n product.media_count != null\n ? t(\"products_asset_count\", {\n count: String(product.media_count),\n })\n : undefined\n }\n onClick={() => navigate(`product/${product.id}`)}\n />\n ))}\n </div>\n )}\n </ShareableListLayout>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n Button,\n Badge,\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogTitle,\n} from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage } from \"../../context\";\nimport {\n ChevronLeft,\n ChevronRight,\n Image as ImageIcon,\n Maximize,\n X,\n} from \"lucide-react\";\n\nfunction buildPdfSrc(url: string, page = 1): string {\n try {\n const parsed = new URL(url);\n parsed.hash = `page=${page}&view=FitH`;\n return parsed.toString();\n } catch {\n return `${url}#page=${page}&view=FitH`;\n }\n}\n\ninterface SharePageImageDisplayProps {\n displayImage: string;\n displayTitle: string;\n displayVideo?: string;\n displayPdf?: string;\n isVideo: boolean;\n badgeLabel: string;\n /** When false, removes the rounded-2xl corners so the media fills its\n * container flush (used by full-screen detail pages where rounded\n * corners show background through the edges). Default true for the\n * modal/card use case. */\n rounded?: boolean;\n /** When false, hides the in-overlay type badge so callers can render\n * the media type elsewhere (e.g. below the media). Default true to\n * preserve existing behavior for modal/card callers. */\n showBadge?: boolean;\n pdfPrevAriaLabel?: string;\n pdfNextAriaLabel?: string;\n pdfDocumentTitle?: string;\n}\n\nexport default function SharePageImageDisplay({\n displayImage,\n displayTitle,\n displayVideo,\n displayPdf,\n isVideo,\n badgeLabel,\n rounded = true,\n showBadge = true,\n pdfPrevAriaLabel,\n pdfNextAriaLabel,\n pdfDocumentTitle,\n}: SharePageImageDisplayProps): React.JSX.Element {\n const [isPreviewOpen, setIsPreviewOpen] = useState(false);\n const [currentPage, setCurrentPage] = useState(1);\n const [imageFailed, setImageFailed] = useState(false);\n const imagePreviewButtonRef = useRef<HTMLButtonElement>(null);\n const renderImage = useRenderImage();\n const { t } = useShareablesTranslation();\n const imageAlt = displayTitle || badgeLabel;\n const hasDisplayImage = !!displayImage && !imageFailed;\n const imageOpenerAriaLabel = imageAlt\n ? t(\"open_image_viewer_for\", { title: imageAlt })\n : t(\"open_image_viewer\");\n const isImageViewerOpen =\n isPreviewOpen && !isVideo && !displayPdf && hasDisplayImage;\n const resolvedPdfPrevAriaLabel =\n pdfPrevAriaLabel ?? t(\"pdf_previous_page_aria\");\n const resolvedPdfNextAriaLabel = pdfNextAriaLabel ?? t(\"pdf_next_page_aria\");\n const resolvedPdfDocumentTitle = pdfDocumentTitle || t(\"pdf_document_title\");\n\n useEffect(() => {\n setImageFailed(false);\n }, [displayImage]);\n\n const handleImageError = () => {\n setImageFailed(true);\n setIsPreviewOpen(false);\n };\n\n const handlePrevPage = () => {\n setCurrentPage((prev) => (prev > 1 ? prev - 1 : prev));\n };\n\n const handleNextPage = () => {\n setCurrentPage((prev) => prev + 1);\n };\n\n return (\n <div\n className={`relative flex h-full w-full flex-col overflow-hidden bg-gray-100 ${\n rounded ? \"rounded-2xl\" : \"\"\n }`}\n >\n {isVideo ? (\n <video\n src={displayVideo || \"\"}\n className=\"h-full w-full object-cover\"\n autoPlay\n muted\n loop\n controls\n />\n ) : displayPdf ? (\n <>\n <iframe\n src={buildPdfSrc(displayPdf, currentPage)}\n className=\"min-h-0 w-full flex-1\"\n title={displayTitle || resolvedPdfDocumentTitle}\n />\n <div className=\"flex items-center justify-center gap-4 py-3\">\n <Button\n onClick={handlePrevPage}\n disabled={currentPage <= 1}\n variant=\"outline\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={resolvedPdfPrevAriaLabel}\n >\n <ChevronLeft className=\"size-4\" />\n </Button>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"number\"\n min=\"1\"\n value={currentPage}\n onChange={(e) => {\n const page = parseInt(e.target.value);\n if (page > 0) {\n setCurrentPage(page);\n }\n }}\n className=\"border-input bg-background w-14 rounded-md border px-2 py-1 text-center text-sm focus:ring-1 focus:outline-none\"\n aria-label={t(\"pdf_page_number_aria\")}\n />\n </div>\n <Button\n onClick={handleNextPage}\n variant=\"outline\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={resolvedPdfNextAriaLabel}\n >\n <ChevronRight className=\"size-4\" />\n </Button>\n </div>\n </>\n ) : hasDisplayImage ? (\n <button\n ref={imagePreviewButtonRef}\n type=\"button\"\n onClick={() => setIsPreviewOpen(true)}\n className=\"group absolute inset-0 z-0 cursor-pointer border-0 bg-transparent p-0\"\n aria-label={imageOpenerAriaLabel}\n aria-haspopup=\"dialog\"\n aria-expanded={isImageViewerOpen}\n >\n <img\n key={displayImage}\n src={displayImage}\n alt=\"\"\n loading=\"lazy\"\n decoding=\"async\"\n className=\"absolute inset-0 h-full w-full scale-125 object-cover blur-md\"\n aria-hidden=\"true\"\n onError={(e) => {\n e.currentTarget.style.display = \"none\";\n }}\n />\n {renderImage({\n src: displayImage,\n alt: imageAlt,\n fill: true,\n className: \"object-contain\",\n onError: handleImageError,\n })}\n <span className=\"group-focus-visible:ring-ring pointer-events-none absolute inset-0 group-focus-visible:ring-2 group-focus-visible:ring-inset\" />\n </button>\n ) : (\n <div className=\"bg-muted text-muted-foreground flex h-full w-full items-center justify-center\">\n <ImageIcon className=\"size-12 opacity-50\" aria-hidden=\"true\" />\n <span className=\"sr-only\">{t(\"image_unavailable\")}</span>\n </div>\n )}\n\n {/* Badge in top-right corner (opt-out when caller renders type elsewhere).\n Kept at z-0 so it stacks above the media element below it but cannot\n pass over a sticky screen header (z-10). */}\n {showBadge && (\n <Badge\n variant=\"secondary\"\n className=\"absolute top-3 right-3 z-0 bg-gray-100 text-gray-900 hover:bg-gray-100\"\n >\n {badgeLabel}\n </Badge>\n )}\n\n {/* Expand button for videos and PDFs (see z-index note above). */}\n {(isVideo || displayPdf) && (\n <Button\n onClick={() => setIsPreviewOpen(true)}\n className=\"absolute top-3 left-3 z-0 flex size-8 items-center justify-center rounded-full bg-white/90 text-gray-700 backdrop-blur-sm transition-colors hover:bg-white\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t(\"open_media_preview\")}\n >\n <Maximize className=\"size-4\" />\n </Button>\n )}\n\n {/* Full image viewer */}\n <Dialog open={isImageViewerOpen} onOpenChange={setIsPreviewOpen}>\n <DialogContent\n className=\"z-[9999] h-[95vh] w-[95vw] max-w-none border-0 bg-transparent p-0 shadow-none sm:max-w-none\"\n showCloseButton={false}\n overlayClassName=\"z-[9998] bg-black/90\"\n onCloseAutoFocus={(event) => {\n event.preventDefault();\n imagePreviewButtonRef.current?.focus();\n }}\n >\n <DialogTitle className=\"sr-only\">{imageAlt}</DialogTitle>\n <DialogDescription className=\"sr-only\">\n {t(\"image_viewer_description\")}\n </DialogDescription>\n <DialogClose\n aria-label={t(\"close_image_viewer\")}\n className=\"bg-foreground/60 text-background hover:bg-foreground/80 focus-visible:ring-ring focus-visible:ring-offset-background absolute top-4 right-4 z-10 flex size-10 cursor-pointer items-center justify-center rounded-full transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none\"\n >\n <X className=\"size-5\" />\n </DialogClose>\n <div className=\"relative flex h-full w-full items-center justify-center\">\n {renderImage({\n src: displayImage,\n alt: imageAlt,\n fill: true,\n className: \"object-contain\",\n onError: handleImageError,\n })}\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Fullscreen Video Preview */}\n {isPreviewOpen && isVideo && (\n <div\n className=\"fixed inset-0 z-9999 flex items-center justify-center bg-black\"\n onClick={() => setIsPreviewOpen(false)}\n >\n <Button\n onClick={() => setIsPreviewOpen(false)}\n className=\"absolute top-4 right-4 z-10000 flex size-10 items-center justify-center rounded-full bg-white/90 text-gray-900 backdrop-blur-sm transition-colors hover:bg-white\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t(\"close_media_preview\")}\n >\n <X className=\"size-5\" />\n </Button>\n <video\n src={displayVideo || \"\"}\n className=\"max-h-screen max-w-full\"\n autoPlay\n controls\n onClick={(e) => e.stopPropagation()}\n />\n </div>\n )}\n\n {/* Fullscreen PDF Preview */}\n {isPreviewOpen && displayPdf && (\n <div\n className=\"fixed inset-0 z-9999 flex items-center justify-center bg-black/70 backdrop-blur-sm\"\n onClick={() => setIsPreviewOpen(false)}\n >\n <Button\n onClick={(e) => {\n e.stopPropagation();\n setIsPreviewOpen(false);\n }}\n className=\"absolute top-4 right-4 z-10000 flex size-10 items-center justify-center rounded-full bg-white/90 text-gray-900 backdrop-blur-sm transition-colors hover:bg-white\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t(\"close_media_preview\")}\n >\n <X className=\"size-5\" />\n </Button>\n <div\n className=\"relative\"\n onClick={(e) => e.stopPropagation()}\n style={{ width: \"95vw\", height: \"90vh\" }}\n >\n <iframe\n src={buildPdfSrc(displayPdf, currentPage)}\n className=\"h-full w-full rounded-lg bg-white\"\n title={displayTitle || resolvedPdfDocumentTitle}\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport { useMemo } from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\ninterface QrCodeDisplayProps {\n url: string | null | undefined;\n alt?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n className?: string;\n}\n\nexport function QrCodeDisplay({\n url,\n alt,\n size = \"md\",\n className = \"\",\n}: QrCodeDisplayProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const resolvedAlt = alt ?? t(\"share_qr_code_default_alt\");\n const qrCodeUrl = useMemo(\n () =>\n url\n ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(url)}`\n : \"\",\n [url],\n );\n\n const sizeClasses = {\n sm: \"h-16 w-16\",\n md: \"h-[120px] w-[120px]\",\n lg: \"h-40 w-40\",\n };\n\n const iconSizes = {\n sm: \"h-4 w-4\",\n md: \"h-8 w-8\",\n lg: \"h-12 w-12\",\n };\n\n return (\n <div\n className={`flex ${sizeClasses[size]} items-center justify-center overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm ${className}`}\n >\n {qrCodeUrl ? (\n <img\n src={qrCodeUrl}\n alt={resolvedAlt}\n className=\"h-full w-full object-cover\"\n loading=\"lazy\"\n onError={(e) => {\n e.currentTarget.style.display = \"none\";\n const parent = e.currentTarget.parentElement;\n if (parent) parent.title = t(\"share_qr_code_load_error\");\n }}\n />\n ) : (\n <div className=\"flex items-center justify-center text-gray-400\">\n <svg\n className={iconSizes[size]}\n fill=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M3 3h6v6H3V3zm8 0h6v6h-6V3zm8 0h2v2h-2V3zm0 4h2v2h-2V7zM3 11h6v6H3v-6zm8 0h6v6h-6v-6zm8 0h2v2h-2v-2zm0 4h2v2h-2v-2zM3 19h6v2H3v-2zm8 0h6v2h-6v-2zm8 0h2v2h-2v-2z\" />\n </svg>\n </div>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport type { SVGProps } from \"react\";\n\ntype IconProps = SVGProps<SVGSVGElement>;\n\nexport function FacebookIcon(props: IconProps): React.JSX.Element {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z\" />\n </svg>\n );\n}\n\nexport function InstagramIcon(props: IconProps): React.JSX.Element {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z\" />\n </svg>\n );\n}\n\nexport function TikTokIcon(props: IconProps): React.JSX.Element {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z\" />\n </svg>\n );\n}\n\nexport function XTwitterIcon(props: IconProps): React.JSX.Element {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n </svg>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { QrCodeDisplay } from \"../QrCodeDisplay\";\nimport { Menu, Copy } from \"lucide-react\";\nimport {\n FacebookIcon,\n InstagramIcon,\n TikTokIcon,\n XTwitterIcon,\n} from \"../icons/social-icons\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport {\n handleSocialShare,\n buildSocialShareLabels,\n} from \"../../utils/social-sharing\";\nimport { isMobileDevice } from \"../../utils/os-detection\";\nimport { useShareablesUI } from \"../../context\";\n\ninterface ShareLinkSectionProps {\n shareLink: string | null;\n loading: boolean;\n displayTitle: string;\n isVideo: boolean;\n relateableId: number;\n relateableType: \"Product\" | \"Medium\" | \"Page\" | \"EnrollmentPack\" | \"Library\";\n}\n\nexport default function ShareLinkSection({\n shareLink,\n loading,\n displayTitle,\n isVideo,\n relateableId,\n relateableType,\n}: ShareLinkSectionProps): React.JSX.Element {\n const { showToast, onMySiteShare } = useShareablesUI();\n const { t } = useShareablesTranslation();\n const [isShareSheetOpen, setIsShareSheetOpen] = useState(false);\n\n const handleCopyLink = async () => {\n if (shareLink) {\n try {\n await navigator.clipboard.writeText(shareLink);\n showToast({\n title: t(\"share_link_copied\"),\n type: \"success\",\n });\n } catch (error) {\n showToast({\n title: t(\"share_link_copy_error\"),\n type: \"error\",\n error,\n });\n }\n }\n };\n\n const handlePlatformShare = async (platform: string) => {\n if (!shareLink) {\n showToast({\n title: t(\"share_link_not_available\"),\n type: \"error\",\n });\n return;\n }\n await handleSocialShare(\n platform.toLowerCase() as\n | \"facebook\"\n | \"x\"\n | \"linkedin\"\n | \"instagram\"\n | \"tiktok\",\n {\n url: shareLink,\n title: displayTitle,\n description: t(\"share_social_description\", {\n type: isVideo ? \"video\" : \"image\",\n title: displayTitle,\n }),\n },\n (message) => showToast({ title: message, type: \"success\" }),\n (message) => showToast({ title: message, type: \"error\" }),\n buildSocialShareLabels(t),\n );\n };\n\n const handleMySiteShareClick = async () => {\n if (!shareLink) {\n showToast({\n title: t(\"share_link_not_available\"),\n type: \"error\",\n });\n return;\n }\n\n if (onMySiteShare) {\n try {\n await onMySiteShare({\n shareLink,\n relateable_id: relateableId,\n relateable_type: relateableType,\n });\n showToast({\n title: t(\"share_mysite_success\"),\n type: \"success\",\n });\n } catch (error) {\n showToast({\n title: t(\"share_mysite_error\"),\n type: \"error\",\n error,\n });\n }\n }\n };\n\n const socialPlatforms = [\n { name: \"Facebook\", Icon: FacebookIcon },\n { name: \"Instagram\", Icon: InstagramIcon },\n { name: \"TikTok\", Icon: TikTokIcon },\n { name: \"X\", Icon: XTwitterIcon },\n ];\n\n return (\n <div className=\"space-y-4\">\n {/* Header with divider lines */}\n <div className=\"flex items-center gap-3\">\n <h3 className=\"text-foreground shrink-0 text-sm font-semibold\">\n {t(\"share_unique_link_heading\")}\n </h3>\n <div className=\"bg-foreground h-px flex-1\" />\n </div>\n\n <div className=\"flex items-start gap-4\">\n {/* QR Code */}\n <div className=\"bg-muted shrink-0 rounded-md p-2\">\n <QrCodeDisplay\n url={shareLink}\n alt={t(\"share_qr_code_alt\")}\n size=\"sm\"\n className=\"h-[75px] w-[75px] rounded-none! border-none\"\n />\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {/* Social buttons */}\n <div className=\"scrollbar-none flex flex-nowrap items-center gap-2 overflow-x-auto\">\n <Button\n onClick={handleMySiteShareClick}\n variant=\"secondary\"\n size=\"sm\"\n className=\"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\"\n >\n {t(\"share_mysite_button\")}\n </Button>\n {isMobileDevice() ? (\n <>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\"\n onClick={() => setIsShareSheetOpen(true)}\n >\n <Menu className=\"mr-1 size-3\" />\n {t(\"share_button\")}\n </Button>\n <MobileActionSheet\n open={isShareSheetOpen}\n onOpenChange={setIsShareSheetOpen}\n title={t(\"share_button\")}\n actions={socialPlatforms.map((platform) => ({\n id: platform.name,\n label: platform.name,\n onSelect: () => handlePlatformShare(platform.name),\n }))}\n />\n </>\n ) : (\n socialPlatforms.map((platform) => (\n <Button\n key={platform.name}\n onClick={() => handlePlatformShare(platform.name)}\n variant=\"secondary\"\n size=\"sm\"\n className=\"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\"\n >\n <platform.Icon className=\"mr-1.5 size-3\" />\n {platform.name}\n </Button>\n ))\n )}\n </div>\n\n {/* Gradient URL bar */}\n <div\n className=\"flex h-10 items-center gap-2 rounded-lg px-3\"\n style={{\n background:\n \"linear-gradient(81.27deg, #E5F7FF 0.95%, #FFDBF5 34.5%, #FFE0CF 60.44%, #EDFFFB 75.06%, #FFDBF5 99.76%)\",\n }}\n >\n <span className=\"flex-1 truncate text-sm text-[#344054]\">\n {shareLink ||\n (loading ? t(\"share_generating_link\") : t(\"common_loading\"))}\n </span>\n <Button\n onClick={handleCopyLink}\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-6 shrink-0 p-0 hover:bg-transparent\"\n disabled={!shareLink}\n >\n <Copy className=\"size-4 text-[#344054]\" />\n </Button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","export async function downloadFile(\n url: string,\n filename: string,\n): Promise<void> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Download failed: ${res.status} ${res.statusText}`);\n }\n const blob = await res.blob();\n const objectUrl = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = objectUrl;\n a.download = filename;\n a.click();\n URL.revokeObjectURL(objectUrl);\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { Download } from \"lucide-react\";\nimport ShareLinkSection from \"./ShareLinkSection\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport { downloadFile } from \"../../utils/download-file\";\n\ninterface AssetActionsProps {\n downloadUrl: string | null | undefined;\n displayTitle: string;\n shareLink: string | null;\n shareLinkLoading: boolean;\n isVideo: boolean;\n relateableId: number;\n relateableType: \"Product\" | \"Medium\" | \"Page\" | \"EnrollmentPack\" | \"Library\";\n /** Optional control rendered to the right of the Download button, on the\n * same row. When omitted, Download renders exactly as before\n * (full-width). When provided, Download shrinks to share the row with\n * this control. Used by MediaDetailScreen to place its \"Add To\" toggle\n * next to Download; the other three screens that render `AssetActions`\n * (Product/Page/Playlist detail) don't pass this and are unaffected. */\n secondaryAction?: React.ReactNode;\n /** Optional panel rendered directly below the Add To / Download row, full\n * width — not inside the row itself. Used by MediaDetailScreen for the\n * row of actions revealed by toggling its `secondaryAction` (\"Add To\")\n * button; only rendered when provided, so the other three screens that\n * render `AssetActions` and never pass it are unaffected. */\n secondaryActionPanel?: React.ReactNode;\n}\n\nexport default function AssetActions({\n downloadUrl,\n displayTitle,\n shareLink,\n shareLinkLoading,\n isVideo,\n relateableId,\n relateableType,\n secondaryAction,\n secondaryActionPanel,\n}: AssetActionsProps): React.JSX.Element {\n const { onFileDownload } = useShareablesUI();\n const { t } = useShareablesTranslation();\n const [isDownloading, setIsDownloading] = useState(false);\n\n const handleDownload = useCallback(async () => {\n if (!downloadUrl || isDownloading) return;\n if (onFileDownload) {\n onFileDownload(downloadUrl, displayTitle || \"asset\");\n return;\n }\n setIsDownloading(true);\n try {\n await downloadFile(downloadUrl, displayTitle || \"asset\");\n } catch {\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } finally {\n setIsDownloading(false);\n }\n }, [downloadUrl, displayTitle, onFileDownload, isDownloading]);\n\n const downloadButton = downloadUrl && (\n <Button\n onClick={handleDownload}\n disabled={isDownloading}\n className={\n secondaryAction\n ? \"border-foreground bg-background text-foreground hover:bg-muted flex h-10 items-center justify-center gap-2 rounded-lg border px-4 transition-all\"\n : \"bg-foreground text-background hover:bg-foreground/70 flex h-10 w-full items-center justify-between rounded-lg px-4 transition-all\"\n }\n >\n <span className=\"text-sm font-medium\">{t(\"share_download_asset\")}</span>\n <Download className=\"size-4\" />\n </Button>\n );\n\n return (\n <>\n {(downloadUrl || secondaryAction) &&\n (secondaryAction ? (\n // Row and panel are grouped into a single child so the parent's\n // `space-y-4` applies once, to the group. Left as siblings, the\n // collapsed panel would still count as a spaced child and leave a\n // phantom gap, and revealing would stack space-y on top of the\n // panel's own margin.\n <div>\n <div className=\"flex flex-wrap items-center gap-3\">\n {secondaryAction}\n {downloadButton}\n </div>\n {secondaryActionPanel}\n </div>\n ) : (\n downloadButton\n ))}\n\n <ShareLinkSection\n shareLink={shareLink}\n loading={shareLinkLoading}\n displayTitle={displayTitle}\n isVideo={isVideo}\n relateableId={relateableId}\n relateableType={relateableType}\n />\n </>\n );\n}\n","import React, { useCallback, useMemo } from \"react\";\nimport { Spinner, Button } from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage } from \"../../context\";\nimport { Image, Play, FileImage } from \"lucide-react\";\n\nconst PLACEHOLDER_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/placeholder.png\";\n\n// FILTER_TABS moved into component as useMemo for i18n\n\n// Sub-components\n\ninterface FilterTab {\n id: string;\n label: string;\n}\n\ninterface FilterTabsProps {\n tabs: FilterTab[];\n activeTab: string;\n onTabChange: (tabId: string) => void;\n}\n\nconst FilterTabs = React.memo(function FilterTabs({\n tabs,\n activeTab,\n onTabChange,\n}: FilterTabsProps): React.JSX.Element {\n return (\n <div className=\"mb-3 flex gap-2\">\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n onClick={() => onTabChange(tab.id)}\n variant=\"secondary\"\n size=\"sm\"\n className={`inline-flex items-center gap-x-1.5 rounded-sm border-none px-2 py-1 text-xs font-medium capitalize shadow-none! transition-colors ${\n activeTab === tab.id\n ? \"bg-primary text-primary-foreground ring-primary hover:bg-primary hover:text-primary-foreground hover:ring-primary\"\n : \"bg-muted text-foreground hover:bg-primary hover:text-primary-foreground hover:ring-primary\"\n }`}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n );\n});\n\ninterface MediaCardProps {\n mediaItem: MediaItem;\n onClick: (mediaItem: MediaItem) => void;\n}\n\nconst MediaCard = React.memo(function MediaCard({\n mediaItem,\n onClick,\n}: MediaCardProps) {\n const renderImage = useRenderImage();\n const { t } = useShareablesTranslation();\n const isVideo = mediaItem.kind === \"video\";\n const isImage = mediaItem.kind === \"image\";\n\n const MediaIcon = isVideo ? Play : isImage ? FileImage : Image;\n\n return (\n <button\n onClick={() => onClick(mediaItem)}\n className=\"group flex w-full flex-col gap-2 text-left transition-all\"\n >\n {/* Media Image */}\n <div className=\"bg-background relative aspect-[3/4] w-full overflow-hidden rounded-lg\">\n {renderImage({\n src: mediaItem.image_url || PLACEHOLDER_IMAGE,\n alt: mediaItem.title || t(\"media_type_default\"),\n fill: true,\n className: \"object-cover\",\n })}\n\n {/* Media Type Icon Badge in top-right corner */}\n <div className=\"absolute top-2 right-2\">\n <div className=\"bg-background/90 flex size-8 items-center justify-center rounded-full shadow-md backdrop-blur-sm\">\n <MediaIcon className=\"size-4 text-gray-900\" />\n </div>\n </div>\n </div>\n\n {/* Title */}\n <h3 className=\"text-foreground line-clamp-2 px-1 text-[15px] leading-[1.4] font-semibold\">\n {mediaItem.title || t(\"common_untitled\")}\n </h3>\n </button>\n );\n});\n\ninterface LoadingStateProps {\n message?: string;\n}\n\nconst LoadingState = React.memo(function LoadingState({\n message,\n}: LoadingStateProps) {\n const { t } = useShareablesTranslation();\n const resolvedMessage = message ?? t(\"share_loading_media\");\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 py-12\">\n <Spinner className=\"size-8\" />\n <div className=\"text-muted-foreground text-sm\">{resolvedMessage}</div>\n </div>\n );\n});\n\ninterface ErrorStateProps {\n message?: string;\n}\n\nconst ErrorState = React.memo(function ErrorState({\n message,\n}: ErrorStateProps) {\n const { t } = useShareablesTranslation();\n const resolvedMessage = message ?? t(\"share_media_load_error\");\n return (\n <div className=\"flex items-center justify-center\">\n <div className=\"text-sm text-red-500\">{resolvedMessage}</div>\n </div>\n );\n});\n\ninterface EmptyStateProps {\n message: string;\n icon?: React.ReactNode;\n description?: string;\n}\n\nconst EmptyState = React.memo(function EmptyState({\n message,\n icon,\n description,\n}: EmptyStateProps) {\n return (\n <div className=\"flex items-center justify-center py-8\">\n {icon && <div className=\"mb-2 text-gray-400\">{icon}</div>}\n <div className=\"text-center\">\n <div className=\"text-sm text-gray-500\">{message}</div>\n {description && (\n <div className=\"text-xs text-gray-400\">{description}</div>\n )}\n </div>\n </div>\n );\n});\n\nconst AnalyticsPlaceholder = React.memo(function AnalyticsPlaceholder() {\n const { t } = useShareablesTranslation();\n return (\n <div className=\"flex flex-1 items-center justify-center\">\n <div className=\"text-center\">\n <div className=\"mb-1 text-sm text-gray-500\">\n {t(\"share_analytics_coming_soon\")}\n </div>\n <div className=\"text-xs text-gray-400\">\n {t(\"share_analytics_description\")}\n </div>\n </div>\n </div>\n );\n});\n\n// RELATEABLE_TYPE_LABELS moved into component for i18n\n\nconst NoAssetsState = React.memo(function NoAssetsState({\n relateable_type,\n}: {\n relateable_type: string;\n}) {\n const { t } = useShareablesTranslation();\n const RELATEABLE_TYPE_LABELS: Record<string, string> = useMemo(\n () => ({\n Product: t(\"share_type_product\"),\n Medium: t(\"share_type_media\"),\n EnrollmentPack: t(\"share_type_enrollment_pack\"),\n MySite: t(\"share_type_site\"),\n Library: t(\"share_type_library\"),\n }),\n [t],\n );\n const label = RELATEABLE_TYPE_LABELS[relateable_type] ?? \"item\";\n return (\n <div className=\"flex flex-1 items-center justify-center px-6\">\n <div className=\"text-center\">\n <div className=\"mb-2 text-gray-400\">\n <Image className=\"mx-auto size-12\" />\n </div>\n <div className=\"mb-1 text-sm text-gray-500\">\n {t(\"share_no_marketing_assets\")}\n </div>\n <div className=\"text-xs text-gray-400\">\n {t(\"share_no_associated_media\", { label })}\n </div>\n </div>\n </div>\n );\n});\n\nexport interface MediaItem {\n id: number;\n title: string;\n image_url?: string | null;\n kind: string;\n video_url?: string | null;\n media_format?: string | null;\n}\n\ninterface MarketingAssetsGridProps {\n isLoading: boolean;\n error: Error | null;\n activeTab: string;\n onTabChange: (tab: string) => void;\n mediaItems: MediaItem[];\n onMediaItemClick: (mediaItem: MediaItem) => void;\n showEmptyState?: boolean;\n activeMainTab?: string;\n relateable_type:\n | \"Product\"\n | \"Medium\"\n | \"EnrollmentPack\"\n | \"MySite\"\n | \"Library\";\n}\n\nfunction MarketingAssetsGrid({\n isLoading,\n error,\n activeTab,\n onTabChange,\n mediaItems,\n onMediaItemClick,\n showEmptyState = false,\n activeMainTab = \"Related Sharables\",\n relateable_type,\n}: MarketingAssetsGridProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n\n const FILTER_TABS: FilterTab[] = useMemo(\n () => [\n { id: \"all\", label: t(\"common_all\") },\n { id: \"images\", label: t(\"media_filter_images\") },\n { id: \"videos\", label: t(\"media_filter_videos\") },\n ],\n [t],\n );\n\n const handleMediaItemClick = useCallback(\n (mediaItem: MediaItem) => {\n onMediaItemClick(mediaItem);\n },\n [onMediaItemClick],\n );\n\n // Render content based on current state\n const renderContent = () => {\n if (showEmptyState) {\n return <NoAssetsState relateable_type={relateable_type} />;\n }\n if (activeMainTab === \"Analytics\" && relateable_type === \"Medium\") {\n return <AnalyticsPlaceholder />;\n }\n\n if (activeMainTab === \"Related Sharables\") {\n return (\n <>\n {/* Header */}\n <div className=\"mb-4 px-4 pt-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n {t(\"share_related_sharables_count\", {\n count: String(mediaItems.length),\n })}\n </h2>\n </div>\n\n {/* Content */}\n <div className=\"px-4 pb-4\">\n <FilterTabs\n tabs={FILTER_TABS}\n activeTab={activeTab}\n onTabChange={onTabChange}\n />\n\n {isLoading && <LoadingState />}\n {error && <ErrorState />}\n\n {!isLoading && !error && mediaItems.length > 0 && (\n <div className=\"grid grid-cols-2 gap-4 xl:grid-cols-3\">\n {mediaItems.map((mediaItem: MediaItem) => (\n <MediaCard\n key={mediaItem.id}\n mediaItem={mediaItem}\n onClick={handleMediaItemClick}\n />\n ))}\n </div>\n )}\n\n {!isLoading && !error && mediaItems.length === 0 && (\n <EmptyState\n message={t(\"share_no_tab_items_found\", {\n tab: (\n FILTER_TABS.find((tab) => tab.id === activeTab)?.label ??\n activeTab\n ).toLowerCase(),\n })}\n />\n )}\n </div>\n </>\n );\n }\n\n return null;\n };\n\n return (\n <div\n className=\"hide-scrollbar bg-background flex min-h-0 flex-1 flex-col overflow-y-auto\"\n style={{ scrollbarWidth: \"none\", msOverflowStyle: \"none\" }}\n >\n {/* MainTabs hidden */}\n {renderContent()}\n </div>\n );\n}\n\n// Memoize the entire component to prevent unnecessary re-renders\nexport default React.memo(MarketingAssetsGrid);\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, type ReactNode } from \"react\";\nimport DOMPurify from \"isomorphic-dompurify\";\nimport { Button, Skeleton } from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { stripTags } from \"../../utils/strip-tags\";\n\nconst RICH_TEXT_ALLOWED_TAGS = [\n \"a\",\n \"b\",\n \"blockquote\",\n \"br\",\n \"caption\",\n \"code\",\n \"div\",\n \"em\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"hr\",\n \"i\",\n \"img\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"s\",\n \"span\",\n \"strong\",\n \"table\",\n \"tbody\",\n \"td\",\n \"tfoot\",\n \"th\",\n \"thead\",\n \"tr\",\n \"u\",\n \"ul\",\n];\n\nconst RICH_TEXT_ALLOWED_ATTRIBUTES_BY_TAG: Record<\n string,\n ReadonlySet<string>\n> = {\n a: new Set([\"href\", \"rel\", \"target\", \"title\"]),\n img: new Set([\"alt\", \"height\", \"src\", \"title\", \"width\"]),\n td: new Set([\"colspan\", \"rowspan\", \"style\"]),\n th: new Set([\"colspan\", \"rowspan\", \"style\"]),\n};\n\nconst RICH_TEXT_ALLOWED_ATTRIBUTES = [\n \"alt\",\n \"colspan\",\n \"height\",\n \"href\",\n \"rel\",\n \"rowspan\",\n \"src\",\n \"style\",\n \"target\",\n \"title\",\n \"width\",\n];\n\nconst TEXT_ALIGN_STYLE_ALLOWED_TAGS = new Set([\n \"blockquote\",\n \"div\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"li\",\n \"p\",\n \"span\",\n \"td\",\n \"th\",\n]);\nconst SAFE_TEXT_ALIGN_VALUES = new Set([\"center\", \"justify\", \"left\", \"right\"]);\nconst SAFE_LINK_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"]);\nconst SAFE_IMAGE_PROTOCOLS = new Set([\"http:\", \"https:\"]);\nconst SAFE_LINK_TARGETS = new Set([\"_blank\", \"_self\"]);\nconst HTML_INTEGER_ATTRIBUTE_PATTERN = /^\\d{1,5}$/;\nconst TABLE_SPAN_ATTRIBUTE_PATTERN = /^\\d{1,3}$/;\nconst SAFE_URL_BASE = \"https://fluid.app\";\nconst SAFE_URL_PROTOCOL_PATTERN =\n /^(?:(?:https?|mailto|tel):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i;\n\nconst SHAREABLE_DESCRIPTION_CLASS_NAME =\n \"text-foreground/70 text-sm leading-relaxed [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2 [&_a:hover]:text-primary/80 [&_blockquote]:my-4 [&_blockquote]:border-border [&_blockquote]:text-foreground/80 [&_blockquote]:border-l-2 [&_blockquote]:pl-4 [&_code]:bg-muted [&_code]:text-foreground [&_code]:rounded [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.85em] [&_h1]:mt-5 [&_h1]:mb-3 [&_h1]:text-xl [&_h1]:leading-tight [&_h1]:font-semibold [&_h1]:text-foreground [&_h2]:mt-5 [&_h2]:mb-3 [&_h2]:text-lg [&_h2]:leading-tight [&_h2]:font-semibold [&_h2]:text-foreground [&_h3]:mt-4 [&_h3]:mb-2 [&_h3]:text-base [&_h3]:leading-tight [&_h3]:font-semibold [&_h3]:text-foreground [&_h4]:mt-4 [&_h4]:mb-2 [&_h4]:font-semibold [&_h4]:text-foreground [&_h5]:mt-3 [&_h5]:mb-2 [&_h5]:font-semibold [&_h5]:text-foreground [&_h6]:mt-3 [&_h6]:mb-2 [&_h6]:font-semibold [&_h6]:text-foreground [&_hr]:border-border [&_hr]:my-4 [&_img]:my-3 [&_img]:h-auto [&_img]:max-w-full [&_img]:rounded-md [&_li>p]:mb-0 [&_li]:my-1 [&_ol]:my-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:mb-3 [&_p:last-child]:mb-0 [&_pre]:bg-muted [&_pre]:text-foreground [&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:p-4 [&_pre]:font-mono [&_pre]:text-xs [&_pre]:break-words [&_pre]:whitespace-pre-wrap [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-xs [&_table]:my-4 [&_table]:w-full [&_table]:border-collapse [&_tbody]:border-border [&_td]:border [&_td]:border-border [&_td]:p-2 [&_tfoot]:border-border [&_th]:border [&_th]:border-border [&_th]:bg-muted/50 [&_th]:p-2 [&_th]:text-left [&_thead]:border-border [&_tr]:border-border [&_ul]:my-3 [&_ul]:list-disc [&_ul]:pl-5\";\n\nexport interface ShareableDetailLayoutProps {\n /** Loading state — renders a centered spinner in place of the layout. */\n isLoading?: boolean;\n /** When true, the not-found state is rendered in place of the layout. */\n notFound?: boolean;\n /** Not-found message (defaults to generic). */\n notFoundMessage?: string;\n /** Title rendered at the top of the details column. */\n title: string;\n /**\n * Optional raw description (HTML allowed). Rendered as sanitized rich\n * text with a read-more toggle. Pass `null`/empty string to skip.\n */\n description?: string | null;\n /** Left column (hero image/video). */\n image: ReactNode;\n /** Actions slot (AssetActions, download, share, etc.) — rendered under description. */\n actions?: ReactNode;\n /** Optional extra header slot rendered between title and description (e.g. price). */\n meta?: ReactNode;\n /** Optional body/sections rendered below a divider (e.g. related media, playlist items). */\n children?: ReactNode;\n /** Override the container max viewport height. Defaults to `md:h-[calc(100vh-140px)]`. */\n containerHeightClass?: string;\n}\n\n/**\n * Standard page chrome for shareable detail screens (product/media/playlist).\n * Provides the two-column hero + details layout, loading/not-found states,\n * title, and the read-more description block.\n *\n * Domain-specific chrome (breadcrumbs, header actions) is still wired up by\n * each screen via the shell header context — this layout purposefully does\n * NOT touch the shell header. Dialogs (delete confirmation, etc.) belong to\n * the caller and should be rendered as siblings to this component.\n */\nexport function ShareableDetailLayout({\n isLoading,\n notFound,\n notFoundMessage,\n title,\n description,\n image,\n actions,\n meta,\n children,\n containerHeightClass = \"md:h-[calc(100vh-140px)]\",\n}: ShareableDetailLayoutProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const resolvedNotFoundMessage =\n notFoundMessage ?? t(\"common_not_found_default\");\n\n if (isLoading) {\n // Mirror the loaded two-column layout so content lands with no shift.\n return (\n <div\n className=\"flex flex-col gap-4 px-4 py-4 md:px-10 md:py-6\"\n role=\"status\"\n aria-busy=\"true\"\n >\n <div\n className={`mx-auto flex w-full max-w-[480px] flex-col gap-6 md:max-w-none md:flex-row ${containerHeightClass}`}\n >\n <Skeleton className=\"aspect-square w-full rounded-lg md:aspect-auto md:h-full\" />\n <div className=\"flex w-full flex-col gap-4\">\n <Skeleton className=\"h-8 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n <Skeleton className=\"h-4 w-2/3\" />\n <div className=\"flex gap-3 pt-2\">\n <Skeleton className=\"h-10 w-32\" />\n <Skeleton className=\"size-10\" />\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n if (notFound) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">{resolvedNotFoundMessage}</p>\n </div>\n );\n }\n\n return (\n <div className=\"animate-in fade-in flex flex-col gap-4 px-4 py-4 duration-300 motion-reduce:animate-none md:px-10 md:py-6\">\n <div\n className={`mx-auto flex w-full max-w-[480px] flex-col gap-6 md:max-w-none md:flex-row ${containerHeightClass}`}\n >\n {/* Left column - hero image/video */}\n <div className=\"aspect-square w-full md:aspect-auto md:h-full\">\n {image}\n </div>\n\n {/* Right column - details */}\n <div className=\"flex w-full flex-col md:overflow-y-auto\">\n <div className=\"space-y-4\">\n <h1 className=\"text-foreground text-[26px] leading-[1.2] font-semibold break-words\">\n {title}\n </h1>\n\n <ShareableDescription html={description} />\n\n {meta}\n\n {actions}\n </div>\n\n {children}\n </div>\n </div>\n </div>\n );\n}\n\nfunction isSafeUrl(\n value: string,\n allowedProtocols: ReadonlySet<string>,\n): boolean {\n try {\n const url = new URL(value, SAFE_URL_BASE);\n return allowedProtocols.has(url.protocol);\n } catch {\n return false;\n }\n}\n\nfunction isAllowedRichTextAttribute(\n tagName: string,\n attributeName: string,\n): boolean {\n const normalizedTagName = tagName.toLowerCase();\n const normalizedAttributeName = attributeName.toLowerCase();\n\n if (normalizedAttributeName === \"style\") {\n return TEXT_ALIGN_STYLE_ALLOWED_TAGS.has(normalizedTagName);\n }\n\n return (\n RICH_TEXT_ALLOWED_ATTRIBUTES_BY_TAG[normalizedTagName]?.has(\n normalizedAttributeName,\n ) ?? false\n );\n}\n\nfunction sanitizeTextAlignStyle(styleValue: string): string | null {\n let sanitizedTextAlign: string | null = null;\n\n for (const declaration of styleValue.split(\";\")) {\n const separatorIndex = declaration.indexOf(\":\");\n\n if (separatorIndex === -1) {\n continue;\n }\n\n const property = declaration.slice(0, separatorIndex).trim().toLowerCase();\n const value = declaration\n .slice(separatorIndex + 1)\n .trim()\n .toLowerCase();\n\n if (property === \"text-align\" && SAFE_TEXT_ALIGN_VALUES.has(value)) {\n sanitizedTextAlign = value;\n }\n }\n\n return sanitizedTextAlign === null\n ? null\n : `text-align: ${sanitizedTextAlign};`;\n}\n\nconst sanitizeRichTextElementAttributes = (node: Element) => {\n const tagName = node.tagName.toLowerCase();\n\n for (const attr of Array.from(node.attributes)) {\n const attributeName = attr.name.toLowerCase();\n const attributeValue = attr.value.trim();\n\n if (\n !isAllowedRichTextAttribute(tagName, attributeName) ||\n attributeValue.includes(\"<\") ||\n attributeValue.includes(\">\")\n ) {\n node.removeAttribute(attr.name);\n continue;\n }\n\n if (\n tagName === \"a\" &&\n attributeName === \"href\" &&\n !isSafeUrl(attributeValue, SAFE_LINK_PROTOCOLS)\n ) {\n node.removeAttribute(attr.name);\n continue;\n }\n\n if (\n tagName === \"img\" &&\n attributeName === \"src\" &&\n !isSafeUrl(attributeValue, SAFE_IMAGE_PROTOCOLS)\n ) {\n node.removeAttribute(attr.name);\n continue;\n }\n\n if (attributeName === \"target\") {\n const normalizedTarget = attributeValue.toLowerCase();\n\n if (SAFE_LINK_TARGETS.has(normalizedTarget)) {\n node.setAttribute(attr.name, normalizedTarget);\n } else {\n node.removeAttribute(attr.name);\n }\n\n continue;\n }\n\n if (attributeName === \"style\") {\n const sanitizedStyle = sanitizeTextAlignStyle(attributeValue);\n\n if (sanitizedStyle === null) {\n node.removeAttribute(attr.name);\n } else {\n node.setAttribute(attr.name, sanitizedStyle);\n }\n\n continue;\n }\n\n if (\n (attributeName === \"width\" || attributeName === \"height\") &&\n !HTML_INTEGER_ATTRIBUTE_PATTERN.test(attributeValue)\n ) {\n node.removeAttribute(attr.name);\n continue;\n }\n\n if (\n (attributeName === \"colspan\" || attributeName === \"rowspan\") &&\n !TABLE_SPAN_ATTRIBUTE_PATTERN.test(attributeValue)\n ) {\n node.removeAttribute(attr.name);\n }\n }\n};\n\nconst removeSanitizedImageWithoutSafeSrc = (node: Element) => {\n if (node.tagName.toLowerCase() !== \"img\") {\n return;\n }\n\n const src = node.getAttribute(\"src\")?.trim();\n\n if (src && isSafeUrl(src, SAFE_IMAGE_PROTOCOLS)) {\n return;\n }\n\n node.remove();\n};\n\nconst normalizeSanitizedElementAttributes = (node: Element) => {\n if (node.tagName.toLowerCase() === \"a\") {\n const target = node.getAttribute(\"target\")?.trim().toLowerCase();\n\n if (target === \"_blank\") {\n node.setAttribute(\"target\", \"_blank\");\n node.setAttribute(\"rel\", \"noopener noreferrer\");\n return;\n }\n\n if (target === \"_self\") {\n node.setAttribute(\"target\", \"_self\");\n } else {\n node.removeAttribute(\"target\");\n }\n\n node.removeAttribute(\"rel\");\n return;\n }\n\n removeSanitizedImageWithoutSafeSrc(node);\n};\n\nfunction hasSanitizedImageWithSrc(sanitizedHtml: string): boolean {\n if (typeof document === \"undefined\") {\n return /<img\\b[^>]*\\bsrc\\s*=/i.test(sanitizedHtml);\n }\n\n const doc = new DOMParser().parseFromString(sanitizedHtml, \"text/html\");\n return doc.body.querySelector(\"img[src]\") !== null;\n}\n\nfunction hasRenderableSanitizedHtml(sanitizedHtml: string): boolean {\n const normalizedHtml = sanitizedHtml.trim();\n\n if (!normalizedHtml) {\n return false;\n }\n\n return (\n stripTags(normalizedHtml).trim().length > 0 ||\n hasSanitizedImageWithSrc(normalizedHtml)\n );\n}\n\nexport function hasRenderableShareableDescriptionHtml(html: string): boolean {\n return hasRenderableSanitizedHtml(sanitizeShareableDescriptionHtml(html));\n}\n\nexport function sanitizeShareableDescriptionHtml(html: string): string {\n const sanitizedBody = DOMPurify.sanitize(html, {\n ALLOWED_ATTR: RICH_TEXT_ALLOWED_ATTRIBUTES,\n ALLOWED_TAGS: RICH_TEXT_ALLOWED_TAGS,\n ALLOWED_URI_REGEXP: SAFE_URL_PROTOCOL_PATTERN,\n ALLOW_ARIA_ATTR: false,\n ALLOW_DATA_ATTR: false,\n ADD_DATA_URI_TAGS: [],\n RETURN_DOM: true,\n }) as unknown as Element;\n\n for (const node of Array.from(sanitizedBody.querySelectorAll(\"*\"))) {\n sanitizeRichTextElementAttributes(node);\n normalizeSanitizedElementAttributes(node);\n }\n\n return sanitizedBody.innerHTML;\n}\n\nexport function ShareableDescription({\n html,\n threshold = 150,\n}: {\n html?: string | null;\n threshold?: number;\n}): React.JSX.Element | null {\n const { t } = useShareablesTranslation();\n const [isExpanded, setIsExpanded] = useState(false);\n const sanitized = sanitizeShareableDescriptionHtml(html ?? \"\");\n\n if (!hasRenderableSanitizedHtml(sanitized)) return null;\n\n const stripped = stripTags(sanitized).trim();\n const shouldShowReadMore = stripped.length > threshold;\n return (\n <div className={SHAREABLE_DESCRIPTION_CLASS_NAME}>\n <div\n className={!isExpanded && shouldShowReadMore ? \"line-clamp-3\" : \"\"}\n dangerouslySetInnerHTML={{ __html: sanitized }}\n />\n {shouldShowReadMore && (\n <Button\n onClick={() => setIsExpanded((prev) => !prev)}\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-foreground hover:text-foreground/80 mt-1 h-auto p-0 text-xs font-normal underline\"\n >\n {isExpanded ? t(\"common_read_less\") : t(\"common_read_more\")}\n </Button>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { getProduct } from \"@fluid-app/products-api-client\";\nimport {\n useShareablesApi,\n useShareablesClient,\n useShareLink,\n} from \"@fluid-app/shareables-core\";\nimport type { PortalProductResponse } from \"../portal-product-types\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Separator,\n} from \"@fluid-app/ui-primitives\";\nimport { Image as ImageIcon } from \"lucide-react\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport AssetActions from \"../SharePage/AssetActions\";\nimport MarketingAssetsGrid from \"../ShareablesModal/MarketingAssetsGrid\";\nimport type { MediaItem } from \"../ShareablesModal/MarketingAssetsGrid\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport type { ShareablesParentBreadcrumb } from \"../shareables-parent-breadcrumb-types\";\nimport { ShareableDetailLayout } from \"../shared/ShareableDetailLayout\";\n\nexport interface ProductDetailScreenProps {\n productId: string;\n countryCode?: string;\n fetchProduct?: (id: string | number) => Promise<PortalProductResponse>;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n parentBreadcrumb?: ShareablesParentBreadcrumb | null;\n}\n\nexport function ProductDetailScreen({\n productId,\n countryCode,\n fetchProduct: fetchPortalProduct,\n onNavigate,\n onBack,\n parentBreadcrumb,\n}: ProductDetailScreenProps): React.JSX.Element {\n const api = useShareablesApi();\n const client = useShareablesClient();\n const { navigate } = useShareablesUI();\n const { t, locale } = useShareablesTranslation();\n const [activeTab, setActiveTab] = useState(\"all\");\n\n // Portal-tenant product fetch\n const { data: portalProductResponse, isLoading: isLoadingPortalProduct } =\n useQuery({\n queryKey: [\"portal-products\", \"detail\", productId],\n queryFn: () => fetchPortalProduct!(productId),\n enabled: !!fetchPortalProduct,\n });\n\n // Legacy product fetch\n const { data: legacyProductResponse, isLoading: isLoadingLegacyProduct } =\n useQuery({\n queryKey: [\"product\", productId],\n queryFn: () => {\n // Invariant: `enabled` guarantees `client` is non-null when this runs.\n if (!client) throw new Error(\"Unreachable: client is null\");\n return getProduct(client, productId, { country_code: countryCode });\n },\n enabled: !fetchPortalProduct && !!client,\n });\n\n const isLoadingProduct = fetchPortalProduct\n ? isLoadingPortalProduct\n : isLoadingLegacyProduct;\n\n // Fetch product media\n const {\n data: productMediaResponse,\n isLoading: isLoadingMedia,\n error: mediaError,\n } = useQuery({\n queryKey: [\"media\", \"public\", productId],\n queryFn: () => api.productMedia.getProductMedia(Number(productId)),\n });\n\n // Share link\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink({ id: Number(productId) }, \"Product\", undefined, locale);\n\n // Derive display values from whichever source is available\n const portalProduct = portalProductResponse?.product;\n const legacyProduct = legacyProductResponse?.product;\n const product = portalProduct || legacyProduct;\n\n const displayTitle = portalProduct?.name || legacyProduct?.title || \"\";\n const productImage =\n portalProduct?.images?.[0]?.url || legacyProduct?.image_url || \"\";\n const displayDescription =\n portalProduct?.description ||\n legacyProduct?.description ||\n legacyProduct?.stripped ||\n \"\";\n\n const displayPrice = (() => {\n if (legacyProduct?.display_price) return legacyProduct.display_price;\n const price = portalProduct?.price ?? legacyProduct?.price;\n if (!price) return undefined;\n const numericPrice = Number(price);\n if (Number.isNaN(numericPrice)) return `$${price}`;\n try {\n return new Intl.NumberFormat(locale.replace(\"_\", \"-\"), {\n style: \"currency\",\n currency: portalProduct?.currency || \"USD\",\n }).format(numericPrice);\n } catch {\n return `$${price}`;\n }\n })();\n\n // Filter media items\n const filteredMediaItems: MediaItem[] = (productMediaResponse?.media ?? [])\n .filter((item: { kind: string | null }) => {\n if (activeTab === \"all\") return true;\n if (activeTab === \"images\") return item.kind === \"image\";\n if (activeTab === \"videos\") return item.kind === \"video\";\n return true;\n })\n .map(\n (item: {\n id: number;\n title: string;\n image_url?: string | null;\n kind: string | null;\n video_url?: string | null;\n media_format?: string | null;\n }) => ({\n id: item.id,\n title: item.title,\n image_url: item.image_url,\n kind: item.kind || \"\",\n video_url: item.video_url,\n media_format: item.media_format,\n }),\n );\n\n const hasMedia = (productMediaResponse?.media?.length || 0) > 0;\n\n // Media item click\n const handleMediaItemClick = useCallback(\n (mediaItem: MediaItem) => {\n onNavigate?.(\"media\", String(mediaItem.id));\n },\n [onNavigate],\n );\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (\n parentBreadcrumb?.onClick ??\n onBack ??\n (() => navigate(\"products\"))\n )();\n }}\n >\n {t(\"products_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {displayTitle || t(\"products_breadcrumb_fallback\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableDetailLayout\n isLoading={isLoadingProduct}\n notFound={!product}\n notFoundMessage={t(\"products_not_found\")}\n title={displayTitle}\n description={displayDescription}\n image={\n <div className=\"relative h-full overflow-hidden rounded-2xl\">\n {!productImage ? (\n <div className=\"bg-muted flex h-full w-full flex-col items-center justify-center rounded-2xl\">\n <div className=\"mb-2 text-gray-400\">\n <ImageIcon className=\"mx-auto size-12\" />\n </div>\n <div className=\"mb-1 text-sm text-gray-500\">\n {t(\"products_no_image\")}\n </div>\n <div className=\"text-xs text-gray-400\">\n {t(\"products_no_media\")}\n </div>\n </div>\n ) : (\n <SharePageImageDisplay\n displayImage={productImage}\n displayTitle={displayTitle}\n isVideo={false}\n badgeLabel={t(\"common_product\")}\n pdfPrevAriaLabel={t(\"pdf_previous_page_aria\")}\n pdfNextAriaLabel={t(\"pdf_next_page_aria\")}\n pdfDocumentTitle={t(\"pdf_document_title\")}\n />\n )}\n </div>\n }\n meta={\n displayPrice ? (\n <div className=\"text-foreground flex items-center gap-2 text-sm\">\n <span className=\"font-semibold\">{t(\"products_price_label\")}</span>\n <span>|</span>\n <span className=\"font-semibold\">{displayPrice}</span>\n </div>\n ) : null\n }\n actions={\n <AssetActions\n downloadUrl={productImage || null}\n displayTitle={displayTitle}\n shareLink={shareLinkError ? null : shareLink || null}\n shareLinkLoading={shareLinkLoading}\n isVideo={false}\n relateableId={Number(productId)}\n relateableType=\"Product\"\n />\n }\n >\n {/* Divider + Marketing Assets */}\n <Separator className=\"border-foreground my-4\" />\n\n <div className=\"hide-scrollbar bg-background h-full overflow-y-auto rounded-lg\">\n <MarketingAssetsGrid\n isLoading={isLoadingMedia}\n error={mediaError as Error | null}\n activeTab={activeTab}\n onTabChange={setActiveTab}\n mediaItems={filteredMediaItems}\n onMediaItemClick={handleMediaItemClick}\n showEmptyState={!hasMedia}\n activeMainTab=\"Related Sharables\"\n relateable_type=\"Product\"\n />\n </div>\n </ShareableDetailLayout>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n shareablesKeys,\n useProductMediaByProductIds,\n type shareables,\n} from \"@fluid-app/shareables-core\";\nimport { Button, Skeleton } from \"@fluid-app/ui-primitives\";\nimport { ChevronLeft, ChevronRight, Sparkles } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage, useShareablesUI } from \"../../context\";\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\ntype ProductSharablesMediaItem = shareables.ProductMedia;\n\ntype ProductSharablesProduct = {\n id: number;\n title: string;\n image_url?: string | null;\n};\n\ntype ProductSharablesImageItem = {\n title?: string | null;\n image_url?: string | null;\n};\n\nexport interface ProductSharablesCarouselProps {\n maxProducts?: number;\n}\n\nfunction getProductKitTitle(\n title: string | null | undefined,\n fallback: string,\n) {\n return (title ?? fallback).replace(/\\s+—\\s+Shareable\\s+\\d+$/i, \"\");\n}\n\nfunction getNewestMediaId(\n items: ProductSharablesMediaItem[] | undefined,\n): number {\n return items?.reduce((newestId, item) => Math.max(newestId, item.id), 0) ?? 0;\n}\n\nexport function ProductSharablesCarousel({\n maxProducts = 6,\n}: ProductSharablesCarouselProps): React.JSX.Element | null {\n const { t, locale } = useShareablesTranslation();\n const { navigate, searchProducts } = useShareablesUI();\n const renderImage = useRenderImage();\n const carouselRef = useRef<HTMLDivElement | null>(null);\n\n const scrollCarousel = useCallback((direction: -1 | 1) => {\n const carousel = carouselRef.current;\n if (!carousel) return;\n\n carousel.scrollBy({\n left: direction * Math.min(carousel.clientWidth * 0.85, 720),\n behavior: \"smooth\",\n });\n }, []);\n\n const { data: productsData, isLoading } = useQuery({\n queryKey: shareablesKeys.productMedia.carouselProducts(locale, maxProducts),\n queryFn: async () => {\n if (!searchProducts) return [];\n\n const response = await searchProducts(\"\", { limit: maxProducts + 2 });\n return response.products;\n },\n enabled: !!searchProducts,\n staleTime: 5 * 60 * 1000,\n });\n\n const products = useMemo<ProductSharablesProduct[]>(\n () =>\n productsData\n ?.filter((product) => product.id != null)\n .map((product) => ({\n id: product.id,\n title: product.name ?? t(\"common_untitled\"),\n image_url: product.image_url ?? null,\n })) ?? [],\n [productsData, t],\n );\n\n const productIds = useMemo(\n () => products.slice(0, maxProducts).map((product) => product.id),\n [maxProducts, products],\n );\n\n const { data: productMediaByProductId } =\n useProductMediaByProductIds(productIds);\n\n const carouselItems = useMemo(\n () =>\n products\n .toSorted(\n (a, b) =>\n getNewestMediaId(productMediaByProductId?.[b.id]) -\n getNewestMediaId(productMediaByProductId?.[a.id]),\n )\n .slice(0, maxProducts),\n [maxProducts, productMediaByProductId, products],\n );\n\n const renderCardImage = (\n item: ProductSharablesImageItem | undefined,\n className = \"object-cover transition-transform duration-500 group-hover:scale-105\",\n ) =>\n renderImage({\n src: item?.image_url || DEFAULT_IMAGE,\n alt: item?.title ?? t(\"common_untitled\"),\n fill: true,\n className,\n });\n\n if (isLoading) {\n return (\n <section className=\"space-y-4\">\n <div className=\"flex flex-col gap-1\">\n <Skeleton className=\"h-8 w-64\" />\n <Skeleton className=\"h-4 w-full max-w-96\" />\n </div>\n <div className=\"-my-2 flex gap-4 overflow-x-auto py-2 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n {Array.from({ length: 3 }, (_, i) => (\n <Skeleton\n key={i}\n className=\"h-[25rem] min-w-[min(78vw,20rem)] rounded-[2rem] md:min-w-[22rem] lg:min-w-[23rem]\"\n />\n ))}\n </div>\n </section>\n );\n }\n\n if (carouselItems.length === 0) return null;\n\n return (\n <section className=\"space-y-4\">\n <div className=\"flex items-end justify-between gap-4\">\n <div className=\"flex flex-col gap-1\">\n <h2 className=\"text-foreground text-2xl font-bold\">\n {t(\"media_product_carousel_title\")}\n </h2>\n <p className=\"text-muted-foreground text-sm\">\n {t(\"media_product_carousel_description\")}\n </p>\n </div>\n <div className=\"hidden items-center gap-2 sm:flex\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => scrollCarousel(-1)}\n className=\"bg-background/90 hover:bg-background size-10 rounded-full p-0 shadow-sm\"\n aria-label={t(\"media_product_carousel_previous\")}\n >\n <ChevronLeft className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => scrollCarousel(1)}\n className=\"bg-background/90 hover:bg-background size-10 rounded-full p-0 shadow-sm\"\n aria-label={t(\"media_product_carousel_next\")}\n >\n <ChevronRight className=\"size-4\" />\n </Button>\n </div>\n </div>\n <div className=\"relative\">\n <div\n ref={carouselRef}\n className=\"-my-2 flex snap-x gap-4 overflow-x-auto py-2 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n >\n {carouselItems.map((item) => {\n const productTitle = getProductKitTitle(\n item.title,\n t(\"common_untitled\"),\n );\n const relatedItems =\n productMediaByProductId?.[item.id]\n ?.toSorted((a, b) => b.id - a.id)\n .slice(0, 3) ?? [];\n const relatedCount =\n productMediaByProductId?.[item.id]?.length ?? relatedItems.length;\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigate(`product/${item.id}`)}\n className=\"group focus-visible:ring-ring relative h-[25rem] min-w-[min(78vw,20rem)] snap-start overflow-hidden rounded-[2rem] text-left shadow-sm transition-transform hover:-translate-y-1 focus-visible:ring-2 focus-visible:outline-none md:min-w-[22rem] lg:min-w-[23rem]\"\n >\n {renderCardImage(item)}\n <span className=\"bg-foreground/25 absolute inset-0\" />\n <span className=\"from-foreground/85 via-foreground/20 absolute inset-0 bg-gradient-to-t to-transparent\" />\n <span className=\"to-foreground/20 absolute inset-0 bg-gradient-to-b from-transparent\" />\n <span className=\"absolute inset-0 flex flex-col justify-between p-4\">\n <span className=\"text-background flex flex-col items-start gap-1 drop-shadow-sm\">\n <span className=\"max-w-[80%] text-base leading-tight font-bold\">\n {productTitle}\n </span>\n <span className=\"text-background/90 flex items-center gap-2 text-sm font-semibold\">\n <Sparkles className=\"size-3.5 fill-current\" />\n {t(\"media_product_carousel_count\", {\n count: relatedCount,\n })}\n </span>\n </span>\n\n <span className=\"text-background mx-auto max-w-[14rem] text-center text-3xl leading-none font-bold tracking-normal drop-shadow-md\">\n {t(\"media_product_carousel_kit_title\", {\n title: productTitle,\n })}\n </span>\n\n <span className=\"mx-auto flex max-w-[13rem] justify-center gap-2\">\n {relatedItems.map((relatedItem) => (\n <span\n key={relatedItem.id}\n className=\"bg-background/95 block size-16 shrink-0 overflow-hidden rounded-2xl p-1.5 shadow-md\"\n >\n <span className=\"relative block size-full overflow-hidden rounded-xl\">\n {renderCardImage(relatedItem)}\n </span>\n </span>\n ))}\n </span>\n </span>\n </button>\n );\n })}\n </div>\n </div>\n </section>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport { Button, cn, Input, Label, Spinner } from \"@fluid-app/ui-primitives\";\nimport { ListMusic } from \"lucide-react\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { isApiError } from \"@fluid-app/api-client-core\";\nimport { shareablesKeys } from \"@fluid-app/shareables-core\";\nimport { useShareablesApi } from \"@fluid-app/shareables-core/api-context\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { useInfiniteListSentinel } from \"../../hooks/use-infinite-list-sentinel\";\n\nconst PAGE_SIZE = 20;\n\n/**\n * Cache prefix for the picker's own playlist list. Declared once because the\n * query and its invalidation must agree: the list lived under this prefix while\n * invalidation targeted the screen's `[\"playlists\"]` keys, which prefix-matching\n * never reaches. With a 60s staleTime in the portal that meant a playlist just\n * created was missing from the picker on reopen.\n */\nconst PICKER_PLAYLISTS_KEY = [\"add-to-playlist\", \"playlists\"] as const;\n\n/** Content types a playlist accepts. The BFF allows all four; only media is\n * wired to a trigger today, but the dialog stays type-agnostic so pages and\n * products need no change here when their surfaces land. */\nexport type PlaylistContentType =\n | \"Medium\"\n | \"Page\"\n | \"Product\"\n | \"EnrollmentPack\";\n\nexport interface AddToPlaylistDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** The record being added — not fetched here, the caller already has it. */\n contentId: number;\n contentType: PlaylistContentType;\n mode: \"pick\" | \"create\";\n /**\n * A playlist an earlier attempt already created for this same content,\n * where the add that followed failed. Opening with one puts the dialog in\n * pick mode so the retry adds to that playlist instead of creating another.\n *\n * Held by the caller because this dialog unmounts when it closes. The caller\n * must scope it to the content it was created for — resuming while adding a\n * different medium would strand the user in pick mode for no visible reason.\n */\n resumePlaylistId?: number | null;\n /** Called once a playlist has been created, before the add is attempted. */\n onPlaylistCreated?: (playlistId: number) => void;\n /** Called once the content is in a playlist, so the caller can forget any\n * resume target it was holding. */\n onAdded?: () => void;\n /**\n * The control to hand focus back to once the dialog closes.\n *\n * Radix returns focus to whatever held it when the dialog opened, but on\n * both surfaces that element is gone by the time it closes: the row menu's\n * item unmounts with the menu, and the detail screen's panel pill is made\n * `inert`. Focusing something unfocusable is a no-op, so focus falls to\n * `<body>` and a keyboard member restarts from the top of the page. Naming a\n * control that outlives the dialog is what makes the return trip work.\n */\n restoreFocusTo?: React.RefObject<HTMLElement | null>;\n}\n\nexport function AddToPlaylistDialog({\n open,\n onOpenChange,\n contentId,\n contentType,\n mode,\n resumePlaylistId = null,\n onPlaylistCreated,\n onAdded,\n restoreFocusTo,\n}: AddToPlaylistDialogProps): React.JSX.Element {\n const api = useShareablesApi();\n const isMobile = useIsMobile();\n const { t } = useShareablesTranslation();\n const { showToast } = useShareablesUI();\n const queryClient = useQueryClient();\n\n const [search, setSearch] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [title, setTitle] = useState(\"\");\n // Playlists the API has told us already hold this content, by way of a 409.\n // There's no way to know up front — the list endpoint doesn't report\n // membership — so a row is marked only once its add has been refused.\n const [alreadyHolding, setAlreadyHolding] = useState<ReadonlySet<number>>(\n new Set(),\n );\n // The refusal to announce, captured whole at the moment it happens.\n // `alreadyHolding` can't carry it: a set has no order, and a live region only\n // re-announces when its text changes. Holding the title rather than looking\n // it up per render matters for the same reason — resolving it against the\n // loaded list let a search that filtered the row out blank the region, and\n // one that brought it back announce a refusal the member dealt with long ago.\n const [lastRefusal, setLastRefusal] = useState<{\n id: number;\n title: string;\n } | null>(null);\n // Set once create mode has actually created a playlist. Cleared on success.\n // Seeded from the caller so a playlist created before a failed add is still\n // the target after this dialog has been closed and reopened.\n const [createdPlaylistId, setCreatedPlaylistId] = useState<number | null>(\n resumePlaylistId,\n );\n // Which face the dialog is showing, so the empty state can flip itself to\n // create without a round trip through the caller. Seeded from the prop, and\n // reset by the unmount that closing performs, so the caller's `mode` is\n // honoured every time the dialog opens.\n const [internalMode, setInternalMode] = useState<\"pick\" | \"create\">(mode);\n const restoreTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Create and add are two calls, so a create that succeeds before a failing\n // add leaves a real playlist behind. Once that has happened, recovery belongs\n // in pick mode: the playlist exists and the list has been invalidated, so it\n // is right there to tap. Staying in create mode would offer a button that\n // makes a second playlist and abandons the first, whether the user retries\n // now or closes and comes back.\n const effectiveMode = createdPlaylistId === null ? internalMode : \"pick\";\n\n // Closing unmounts this dialog — both callers render it conditionally — so\n // the return trip is scheduled from the unmount cleanup rather than from a\n // close handler that never runs. The timeout places it after Radix's own\n // restore attempt, which is deferred by a tick and, per `restoreFocusTo`,\n // lands on a detached or inert node here.\n //\n // The id is held so setup can cancel it. StrictMode runs setup, cleanup,\n // setup on mount (the portal enables it — see create-portal.tsx), so the\n // cleanup fires while the dialog is opening and would otherwise queue a\n // focus on the trigger behind it. Radix re-focuses the content afterwards\n // and wins today, but that is an ordering accident, not a guarantee.\n useEffect(() => {\n if (restoreTimeout.current !== null) {\n clearTimeout(restoreTimeout.current);\n restoreTimeout.current = null;\n }\n return () => {\n const trigger = restoreFocusTo?.current;\n if (!trigger) return;\n restoreTimeout.current = setTimeout(() => {\n restoreTimeout.current = null;\n trigger.focus();\n }, 0);\n };\n }, [restoreFocusTo]);\n\n // Debounce search input so it doesn't fire a request per keystroke —\n // mirrors PlaylistsListingScreen's pattern.\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(search), 300);\n return () => clearTimeout(timer);\n }, [search]);\n\n const {\n data,\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n error,\n isPending,\n isFetching,\n refetch,\n } = useInfiniteQuery({\n queryKey: [...PICKER_PLAYLISTS_KEY, debouncedSearch],\n enabled: open && effectiveMode === \"pick\",\n // Hold the previous page while a new search is in flight, so the list\n // doesn't blank — and read as \"no playlists\" — on every keystroke.\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const response = await api.playlists.getPlaylists({\n \"filter[title]\": debouncedSearch || undefined,\n \"filter[ownership]\": \"mine\",\n \"page[cursor]\": pageParam,\n \"page[limit]\": PAGE_SIZE,\n });\n return {\n playlists: response.playlists,\n nextCursor: response.meta.pagination.next_cursor,\n };\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,\n initialPageParam: undefined as string | undefined,\n });\n\n const playlists = data?.pages.flatMap((page) => page.playlists) ?? [];\n\n // An untitled playlist falls back to the sentence that names no one, rather\n // than announcing a verb with a blank subject.\n const refusalAnnouncement = !lastRefusal\n ? \"\"\n : lastRefusal.title\n ? t(\"playlist_item_already_exists_named\", { title: lastRefusal.title })\n : t(\"playlist_item_already_exists\");\n\n const sentinelRef = useInfiniteListSentinel({\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n error,\n });\n\n // No `order`: the API appends to the end of the playlist when the position\n // is omitted, which is the agreed behavior for adds made while browsing.\n // Sending an explicit 0 is rejected outright (positions are 1-based).\n const addItem = (playlistId: number) =>\n api.playlists.addItemToPlaylist(playlistId, {\n items: [{ relateable_type: contentType, relateable_id: contentId }],\n });\n\n /** The row being added, carried so a refusal can name it. Looking the title\n * back up on failure reads a list that may have moved on under the request\n * — a debounced search resolving, a background refetch — and announces a\n * refusal with no subject. */\n type AddTarget = { id: number; title: string };\n\n const invalidate = () => {\n // Both surfaces: this picker, and the playlists screen behind it.\n void queryClient.invalidateQueries({ queryKey: PICKER_PLAYLISTS_KEY });\n void queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.all,\n });\n };\n\n const addMutation = useMutation({\n mutationFn: ({ id }: AddTarget) => addItem(id),\n onSuccess: () => {\n onAdded?.();\n showToast({ type: \"success\", title: t(\"playlist_add_item_success\") });\n invalidate();\n onOpenChange(false);\n },\n onError: (error: Error, { id: playlistId, title }: AddTarget) => {\n // 409 means this content is already in that playlist. Blocked inline on\n // the row rather than as a toast, and the dialog stays open so the user\n // can pick a different playlist without starting over.\n if (isApiError(error) && error.status === 409) {\n setAlreadyHolding((previous) => new Set(previous).add(playlistId));\n setLastRefusal({ id: playlistId, title });\n // A refusal from the playlist we were resuming means the content is in\n // it after all, so the recovery target has done its job — otherwise the\n // caller holds it forever and every later New playlist for this content\n // opens the picker instead. A refusal from any *other* playlist says\n // nothing about the resumed one, so that target stands.\n //\n // Released here as well as at the caller: leaving it set pins\n // `effectiveMode` to pick for the life of the dialog, which withholds\n // the empty state's create button and rebuilds the dead end this\n // branch exists to remove. `createMutation` already clears it on the\n // same reasoning.\n if (playlistId === createdPlaylistId) {\n setCreatedPlaylistId(null);\n // Pinned to pick by that same target, so releasing it would hand the\n // face back to whatever `internalMode` was last set to — the create\n // form the member left behind, title still filled in. They are\n // looking at the picker; a refusal is no reason to move them.\n setInternalMode(\"pick\");\n onAdded?.();\n }\n return;\n }\n showToast({ type: \"error\", title: t(\"playlist_add_item_error\"), error });\n },\n });\n\n const createMutation = useMutation({\n mutationFn: async (newTitle: string) => {\n const created = await api.playlists.createPlaylist({\n playlist: { title: newTitle },\n });\n // Recorded before the add so a failure leaves the dialog in pick mode\n // with the new playlist selectable, per `effectiveMode` above, and the\n // caller keeps the target for after this dialog closes.\n setCreatedPlaylistId(created.playlist.id);\n // Selectable only if it is on screen. Create is reachable from a search\n // that matched nothing, and that term still filters the list the dialog\n // drops back to — so the playlist just made would be filtered out, the\n // no-match message would render over it, and the way forward would be\n // clearing a box nothing points at.\n setSearch(\"\");\n setDebouncedSearch(\"\");\n onPlaylistCreated?.(created.playlist.id);\n await addItem(created.playlist.id);\n },\n onSuccess: () => {\n setCreatedPlaylistId(null);\n onAdded?.();\n showToast({ type: \"success\", title: t(\"playlist_add_item_success\") });\n onOpenChange(false);\n },\n onError: (error: Error) => {\n // A 409 here means the playlist we just created already holds the\n // content, so the request achieved what it set out to. Treated as\n // success, releasing the target — otherwise this is the one path where\n // that status means \"generic failure\" while addMutation reads it as\n // \"already there\", and the caller keeps a target that is spent.\n if (isApiError(error) && error.status === 409) {\n setCreatedPlaylistId(null);\n onAdded?.();\n showToast({ type: \"success\", title: t(\"playlist_add_item_success\") });\n onOpenChange(false);\n return;\n }\n showToast({ type: \"error\", title: t(\"playlist_add_item_error\"), error });\n },\n // Not onSuccess: the playlist can be created successfully even when the\n // subsequent addItem call fails, so invalidation must run on both paths\n // — otherwise a playlist created before the add failed would be missing\n // from a warm cache.\n onSettled: () => {\n invalidate();\n },\n });\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={onOpenChange}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n title={\n effectiveMode === \"create\"\n ? t(\"playlist_create_title\")\n : t(\"media_add_to_playlist\")\n }\n >\n {effectiveMode === \"create\" ? (\n <div className=\"flex flex-col gap-4\">\n <Label htmlFor=\"playlist-title\">\n {t(\"playlist_create_name_label\")}\n </Label>\n <Input\n id=\"playlist-title\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n />\n <Button\n disabled={!title.trim() || createMutation.isPending}\n onClick={() => createMutation.mutate(title.trim())}\n >\n {t(\"playlist_create_submit\")}\n </Button>\n {/* Only when the dialog was opened on the picker. Arriving here is\n then something the member did — off an empty state, possibly a\n mistyped search — and the way back is the picker they came from.\n A dialog opened straight into create has no picker behind it, so\n there is nowhere to return to and no control. */}\n {mode === \"pick\" && (\n <Button\n variant=\"outline\"\n disabled={createMutation.isPending}\n onClick={() => setInternalMode(\"pick\")}\n >\n {t(\"common_cancel\")}\n </Button>\n )}\n </div>\n ) : (\n <div className=\"flex flex-col gap-2\">\n <Input\n value={search}\n placeholder={t(\"playlist_picker_search\")}\n // Named here as well as by the placeholder, which stops being the\n // field's accessible name as soon as it holds content.\n aria-label={t(\"playlist_picker_search\")}\n onChange={(event) => setSearch(event.target.value)}\n />\n {/* A refusal is reported on the row, which is right for anyone who\n can see it, but a member driving the dialog from the keyboard has\n no reason to re-read a row they just left. Repeating it here says\n so out loud. Rendered in every pick-mode state so the region is\n already in the accessibility tree when its text arrives. */}\n <p\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"playlist-refusal-announcement\"\n className=\"sr-only\"\n >\n {refusalAnnouncement}\n </p>\n {/* `keepPreviousData` holds the last page while a new search is in\n flight, so an in-flight fetch over an empty previous result would\n otherwise render \"No playlists yet\" — the exact claim this dialog\n is being fixed for — in the beat between clearing a search and its\n rows arriving. */}\n {isPending || (isFetching && playlists.length === 0) ? (\n <div className=\"flex justify-center py-6\">\n <Spinner className=\"size-5\" />\n </div>\n ) : error ? (\n /* A failed list previously fell through to \"no playlists yet\",\n telling a member with twenty of them they had none, with no way\n to try again. */\n <div className=\"flex flex-col items-start gap-2 py-2\">\n <p className=\"text-muted-foreground text-sm\">\n {t(\"common_load_error_default\")}\n </p>\n <Button variant=\"outline\" size=\"sm\" onClick={() => refetch()}>\n {t(\"common_retry\")}\n </Button>\n </div>\n ) : playlists.length === 0 ? (\n /* Two different situations, and one message used to cover both:\n a member with twenty playlists who searched for something none\n of them match was told they had none.\n\n Either way the list is empty, and without a create control here\n that is a dead end: the sibling \"New playlist\" action sits on\n the menu *behind* this dialog, so the way forward is to close\n what you just opened. */\n <div className=\"flex flex-col items-start gap-3 py-2\">\n <p className=\"text-muted-foreground text-sm\">\n {debouncedSearch\n ? t(\"playlist_picker_no_matches\")\n : t(\"playlist_picker_empty\")}\n </p>\n {/* Withheld while a created-but-unadded playlist is waiting.\n `effectiveMode` pins the dialog to pick until that add lands,\n so the button could set the mode but never change the face —\n an affordance that looks live and does nothing. It is also the\n right answer on its own terms: a second playlist here would\n abandon the one already made. Clearing the search brings the\n waiting playlist back into view. */}\n {createdPlaylistId === null && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setInternalMode(\"create\")}\n >\n {t(\"media_new_playlist\")}\n </Button>\n )}\n </div>\n ) : (\n playlists.map((playlist) => {\n const isAlreadyHolding = alreadyHolding.has(playlist.id);\n const isBlocked = addMutation.isPending || isAlreadyHolding;\n\n return (\n <button\n key={playlist.id}\n type=\"button\"\n className={cn(\n \"hover:bg-muted flex items-center gap-3 rounded p-2 text-left\",\n isBlocked && \"opacity-60 hover:bg-transparent\",\n )}\n // `aria-disabled`, not `disabled`. The native attribute takes\n // the row out of the tab order and drops the focus sitting on\n // it, which throws a keyboard member clean out of the open\n // dialog at the exact moment the row starts carrying the one\n // explanation of what just happened. This keeps the row\n // reachable and readable; the guard in the handler is what\n // actually refuses the press.\n aria-disabled={isBlocked || undefined}\n // A pointer press on a button focuses it, which pulls focus\n // out of the search field mid-search — and a refusal is the\n // one outcome that leaves the dialog open with the member\n // still wanting to type. Rows are options over a search box,\n // so they behave like one: the press acts without taking\n // focus, and whatever the member was using keeps it. Nothing\n // changes for the keyboard, where focus is already on the row\n // before Enter and stays there to carry the refusal.\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n if (isBlocked) return;\n addMutation.mutate({\n id: playlist.id,\n title: playlist.title ?? \"\",\n });\n }}\n >\n {playlist.image_url ? (\n <img\n src={playlist.image_url}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"lazy\"\n className=\"bg-muted size-10 shrink-0 rounded object-cover\"\n />\n ) : (\n // Placeholder keeps every row the same height whether or not\n // the playlist has a cover, so the list doesn't jitter.\n <div\n aria-hidden=\"true\"\n className=\"bg-muted text-muted-foreground flex size-10 shrink-0 items-center justify-center rounded\"\n >\n <ListMusic className=\"size-4\" />\n </div>\n )}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{playlist.title}</span>\n {/* Only rendered on refusal. There is no second line\n otherwise: `filter[ownership]` is `mine`, so every row\n here belongs to the member and saying so adds nothing.\n `company_library` was never populated on this path\n either, so the admin half could not render. */}\n {isAlreadyHolding && (\n <span className=\"text-muted-foreground text-xs\">\n {t(\"playlist_item_already_exists\")}\n </span>\n )}\n </span>\n </button>\n );\n })\n )}\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-2\">\n <Spinner className=\"size-4\" />\n </div>\n )}\n {/* 1px sentinel — fetches the next page when scrolled into view. */}\n <div ref={sentinelRef} />\n </div>\n )}\n </ResponsiveDialog>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useRef, useState } from \"react\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { MoreVertical, Trash2 } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\n/** Three-dot dropdown attached to each media row/card.\n *\n * Two gates, because the actions aren't equivalent:\n *\n * - The playlist actions follow `canCuratePlaylists`. They don't mutate the\n * media and every member type has playlists of their own, so customers get\n * them too.\n * - \"Delete\" follows `canDelete`, since deleting mutates the media and a user\n * must not delete media they don't own.\n *\n * Both playlist handlers are handed this menu's own trigger. Selecting an\n * item closes the menu, so the item that had focus is gone by the time the\n * dialog it opened closes again; the trigger is the nearest control that\n * outlives both, and the caller passes it on as the dialog's\n * `restoreFocusTo`.\n */\nexport function MediaRowActionsMenu({\n canCuratePlaylists = true,\n canDelete,\n onDelete,\n onAddToPlaylist,\n onNewPlaylist,\n}: {\n /** Defaults to `true` so hosts that never opted out keep the actions. */\n canCuratePlaylists?: boolean;\n canDelete: boolean;\n onDelete: () => void;\n /** Receives this menu's trigger, so the dialog the action opens can hand\n * focus back to it on close. */\n onAddToPlaylist: (trigger: HTMLButtonElement | null) => void;\n onNewPlaylist: (trigger: HTMLButtonElement | null) => void;\n}): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const [isOpen, setIsOpen] = useState(false);\n const triggerRef = useRef<HTMLButtonElement>(null);\n if (isMobile) {\n return (\n <>\n <Button\n ref={triggerRef}\n variant=\"ghost\"\n size=\"sm\"\n className=\"bg-background/80 hover:bg-background size-8 p-0 backdrop-blur-sm\"\n onClick={(event) => {\n event.stopPropagation();\n setIsOpen(true);\n }}\n aria-label={t(\"media_actions_aria\")}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n <MobileActionSheet\n open={isOpen}\n onOpenChange={setIsOpen}\n title={t(\"media_actions_aria\")}\n actions={[\n ...(canCuratePlaylists\n ? [\n {\n id: \"add-to-playlist\",\n label: t(\"media_add_to_playlist\"),\n onSelect: () => onAddToPlaylist(triggerRef.current),\n },\n {\n id: \"new-playlist\",\n label: t(\"media_new_playlist\"),\n onSelect: () => onNewPlaylist(triggerRef.current),\n },\n ]\n : []),\n ...(canDelete\n ? [\n {\n id: \"delete\",\n label: t(\"common_delete\"),\n variant: \"destructive\" as const,\n onSelect: onDelete,\n },\n ]\n : []),\n ]}\n />\n </>\n );\n }\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n ref={triggerRef}\n variant=\"ghost\"\n size=\"sm\"\n className=\"bg-background/80 hover:bg-background size-8 p-0 backdrop-blur-sm\"\n onClick={(e) => {\n // Prevent the card/row click-through since the card wraps the\n // whole area with its own navigation handler.\n e.stopPropagation();\n }}\n aria-label={t(\"media_actions_aria\")}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" onClick={(e) => e.stopPropagation()}>\n {canCuratePlaylists && (\n <>\n <DropdownMenuItem\n onClick={() => onAddToPlaylist(triggerRef.current)}\n >\n {t(\"media_add_to_playlist\")}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => onNewPlaylist(triggerRef.current)}>\n {t(\"media_new_playlist\")}\n </DropdownMenuItem>\n </>\n )}\n {canDelete && (\n <DropdownMenuItem\n onClick={onDelete}\n className=\"text-destructive focus:text-destructive\"\n >\n <Trash2 className=\"mr-2 size-4\" />\n {t(\"common_delete\")}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { Tabs, TabsList, TabsTrigger, cn } from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport type OwnerFilter = \"all\" | \"mine\" | \"company\";\n\nexport const OWNER_FILTER_TABS_FULL_WIDTH_ROOT_CLASS = \"max-sm:w-full\";\nexport const OWNER_FILTER_TABS_FULL_WIDTH_LIST_CLASS =\n \"max-sm:grid max-sm:w-full max-sm:grid-cols-3\";\nexport const OWNER_FILTER_TABS_FULL_WIDTH_TRIGGER_CLASS =\n \"max-sm:min-w-0 max-sm:truncate max-sm:px-1.5\";\n\nexport function getFilteredEmptyMessage({\n searchTerm,\n ownerFilter,\n hasOtherFilters = false,\n entityName = \"items\",\n defaultMessage,\n messages,\n t,\n}: {\n searchTerm: string;\n ownerFilter: OwnerFilter;\n hasOtherFilters?: boolean;\n entityName?: string;\n defaultMessage?: string;\n messages?: {\n filterSearchNoResults: string;\n searchNoResults: string;\n filterNoResults: string;\n emptyDefault?: string;\n };\n t?: ReturnType<typeof useShareablesTranslation>[\"t\"];\n}): string {\n const isFiltered = ownerFilter !== \"all\" || hasOtherFilters;\n if (messages) {\n if (searchTerm && isFiltered) return messages.filterSearchNoResults;\n if (searchTerm) return messages.searchNoResults;\n if (isFiltered) return messages.filterNoResults;\n return (\n defaultMessage ??\n messages.emptyDefault ??\n t?.(\"filter_empty_default\") ??\n \"Nothing available at the moment.\"\n );\n }\n if (searchTerm && isFiltered)\n return (\n t?.(\"filter_empty_search_filtered\", { entityName, searchTerm }) ??\n `No ${entityName} match \"${searchTerm}\" with the current filters. Try a different search term or switch to \"All\".`\n );\n if (searchTerm)\n return (\n t?.(\"filter_empty_search\", { entityName, searchTerm }) ??\n `No ${entityName} match \"${searchTerm}\". Try a different search term.`\n );\n if (isFiltered)\n return (\n t?.(\"filter_empty_filtered\", { entityName }) ??\n `No matching ${entityName} for the current filters.`\n );\n return (\n defaultMessage ??\n t?.(\"filter_empty_default\") ??\n \"Nothing available at the moment.\"\n );\n}\n\nexport interface OwnerFilterTabsProps {\n value: OwnerFilter;\n onValueChange: (value: OwnerFilter) => void;\n myLabel?: string;\n fullWidthOnMobile?: boolean;\n}\n\nexport function OwnerFilterTabs({\n value,\n onValueChange,\n myLabel,\n fullWidthOnMobile = false,\n}: OwnerFilterTabsProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n return (\n <Tabs\n value={value}\n onValueChange={(v) => onValueChange(v as OwnerFilter)}\n className={cn(\n fullWidthOnMobile && OWNER_FILTER_TABS_FULL_WIDTH_ROOT_CLASS,\n )}\n >\n <TabsList\n className={cn(\n fullWidthOnMobile && OWNER_FILTER_TABS_FULL_WIDTH_LIST_CLASS,\n )}\n >\n <TabsTrigger\n value=\"all\"\n className={cn(\n fullWidthOnMobile && OWNER_FILTER_TABS_FULL_WIDTH_TRIGGER_CLASS,\n )}\n >\n {t(\"common_all\")}\n </TabsTrigger>\n <TabsTrigger\n value=\"mine\"\n className={cn(\n fullWidthOnMobile && OWNER_FILTER_TABS_FULL_WIDTH_TRIGGER_CLASS,\n )}\n >\n {myLabel ?? t(\"common_mine\")}\n </TabsTrigger>\n <TabsTrigger\n value=\"company\"\n className={cn(\n fullWidthOnMobile && OWNER_FILTER_TABS_FULL_WIDTH_TRIGGER_CLASS,\n )}\n >\n {t(\"common_company\")}\n </TabsTrigger>\n </TabsList>\n </Tabs>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\n\nexport const SHAREABLE_FILTER_BAR_CLASS =\n \"flex flex-col gap-3 sm:flex-row sm:items-center\";\nexport const SHAREABLE_FILTER_TABS_CLASS = \"w-full overflow-x-auto sm:w-auto\";\nexport const SHAREABLE_FILTER_CONTROLS_CLASS =\n \"flex w-full items-center gap-2 sm:ml-auto sm:w-auto\";\nexport const SHAREABLE_SEARCH_SORT_CLASS =\n \"min-w-0 flex-1 sm:w-full sm:max-w-sm\";\n\nexport interface ShareableFilterBarLayoutProps {\n tabs: React.ReactNode;\n controls: React.ReactNode;\n}\n\n/**\n * Filter bar for shareable listing screens with ownership tabs. Stacks the\n * tabs above the search/sort controls on mobile so they stop competing for\n * horizontal space, and collapses back to a single row from `sm` up.\n *\n * Wrap the search control in `SHAREABLE_SEARCH_SORT_CLASS` so it takes the\n * width left over by the adjacent buttons.\n */\nexport function ShareableFilterBarLayout({\n tabs,\n controls,\n}: ShareableFilterBarLayoutProps): React.JSX.Element {\n return (\n <div className={SHAREABLE_FILTER_BAR_CLASS}>\n <div className={SHAREABLE_FILTER_TABS_CLASS}>{tabs}</div>\n <div className={SHAREABLE_FILTER_CONTROLS_CLASS}>{controls}</div>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useMemo, useCallback, useRef } from \"react\";\nimport {\n useInfiniteQuery,\n useQueryClient,\n useMutation,\n keepPreviousData,\n} from \"@tanstack/react-query\";\nimport {\n useShareablesApi,\n useRepContext,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Badge,\n Button,\n DialogFooter,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Spinner,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { Check, Filter, MoreVertical, Plus, Trash2 } from \"lucide-react\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { ProductSharablesCarousel } from \"../MediaShare/ProductSharablesCarousel\";\nimport ShareItemCard from \"../MediaShare/ShareItemCard\";\nimport { useShareablesUI, useCanCuratePlaylists } from \"../../context\";\nimport { AddToPlaylistDialog } from \"../media/AddToPlaylistDialog\";\nimport { MediaRowActionsMenu } from \"../media/MediaRowActionsMenu\";\nimport { useInfiniteListSentinel } from \"../../hooks/use-infinite-list-sentinel\";\nimport {\n OwnerFilterTabs,\n getFilteredEmptyMessage,\n type OwnerFilter,\n} from \"../OwnerFilterTabs\";\nimport {\n ShareableListLayout,\n SHAREABLE_GRID_CLASS,\n} from \"../shared/ShareableListLayout\";\nimport {\n ShareableListRow,\n SHAREABLE_LIST_CLASS,\n} from \"../shared/ShareableListRow\";\nimport { ViewModeToggle, type ViewMode } from \"../shared/ViewModeToggle\";\nimport {\n ShareableFilterBarLayout,\n SHAREABLE_SEARCH_SORT_CLASS,\n} from \"../shared/ShareableFilterBarLayout\";\n\nconst PAGE_SIZE = 24;\n\nfunction getMediaKindLabel(\n kind: string | null,\n labels: { video: string; pdf: string; image: string },\n): string {\n switch (kind) {\n case \"video\":\n return labels.video;\n case \"pdf\":\n return labels.pdf;\n default:\n return labels.image;\n }\n}\n\nexport interface MediaListingScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n showProductSharablesCarousel?: boolean;\n}\n\ntype KindFilter = \"all\" | \"image\" | \"video\" | \"pdf\";\n\nconst KIND_FILTER_OPTIONS = [\n { value: \"all\", labelKey: \"media_filter_all_types\" },\n { value: \"image\", labelKey: \"media_filter_images\" },\n { value: \"video\", labelKey: \"media_filter_videos\" },\n { value: \"pdf\", labelKey: \"media_filter_pdfs\" },\n] as const satisfies readonly { value: KindFilter; labelKey: string }[];\n\nexport function MediaListingScreen({\n showProductSharablesCarousel = false,\n}: MediaListingScreenProps): React.JSX.Element {\n const { t, locale } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const api = useShareablesApi();\n const repContext = useRepContext();\n const queryClient = useQueryClient();\n const { navigate, readOnly, showToast } = useShareablesUI();\n const canCuratePlaylists = useCanCuratePlaylists();\n // Playlists created for a medium whose add then failed, by medium id. Kept\n // here rather than in the dialog because the dialog unmounts when it closes.\n //\n // Keyed rather than a single slot: this screen lists many media, so a second\n // medium hitting the same create-then-fail sequence used to overwrite the\n // first medium's target and let it create a duplicate. Bounded by the media\n // a member opens the dialog for in one session.\n const [pendingCreates, setPendingCreates] = useState<Record<number, number>>(\n {},\n );\n\n const [pendingDeleteId, setPendingDeleteId] = useState<number | null>(null);\n const [playlistDialog, setPlaylistDialog] = useState<{\n mediaId: number;\n mode: \"pick\" | \"create\";\n } | null>(null);\n // The row menu trigger that opened the dialog, so closing the dialog puts\n // focus back on the row a member was working through rather than at the top\n // of the page. A ref, not state: only the dialog's unmount reads it, and\n // re-rendering the whole listing to record a focus target would be waste.\n //\n // The menu hands its own trigger over rather than taking a ref from here,\n // because a ref passed down would be one ref shared by every row and would\n // end up holding whichever mounted last. The detail screen has a single\n // \"Add To\" toggle and so can own its ref directly.\n const playlistDialogTriggerRef = useRef<HTMLButtonElement | null>(null);\n const openPlaylistDialog = useCallback(\n (mediaId: number, mode: \"pick\" | \"create\") =>\n (trigger: HTMLButtonElement | null) => {\n playlistDialogTriggerRef.current = trigger;\n setPlaylistDialog({ mediaId, mode });\n },\n [],\n );\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [sortValue, setSortValue] = useState(\"newest\");\n const [viewMode, setViewMode] = useState<ViewMode>(\"grid\");\n const [ownerFilter, setOwnerFilter] = useState<OwnerFilter>(\"all\");\n const [kindFilter, setKindFilter] = useState<KindFilter>(\"all\");\n const [isKindFilterOpen, setIsKindFilterOpen] = useState(false);\n\n // Debounce search input\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearch(searchTerm);\n }, 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n const { mutate: deleteMedia, isPending: isDeleting } = useMutation({\n mutationFn: (id: number) => api.media.deleteMedia(id),\n onSuccess: () => {\n showToast({ title: t(\"media_delete_success\"), type: \"success\" });\n void queryClient.invalidateQueries({\n queryKey: shareablesKeys.media.all,\n });\n setPendingDeleteId(null);\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"media_delete_error\", { message: error.message }),\n type: \"error\",\n error,\n });\n setPendingDeleteId(null);\n },\n });\n const confirmDelete = useCallback(() => {\n if (pendingDeleteId != null) deleteMedia(pendingDeleteId);\n }, [pendingDeleteId, deleteMedia]);\n\n // Map the UI sort value to the BFF's sort enum. The BFF currently supports\n // only title_asc / title_desc; for \"newest\" we omit `sort` so the server\n // applies its own default (newest first).\n const bffSort: \"title_asc\" | \"title_desc\" | undefined =\n sortValue === \"title_asc\"\n ? \"title_asc\"\n : sortValue === \"title_desc\"\n ? \"title_desc\"\n : undefined;\n\n // Map the UI kind filter to the BFF enum; \"all\" becomes `undefined`.\n const bffKind: \"image\" | \"video\" | \"pdf\" | undefined =\n kindFilter === \"all\" ? undefined : kindFilter;\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey: [\n ...shareablesKeys.media.list(\n debouncedSearch,\n sortValue === \"title_desc\",\n repContext,\n ownerFilter,\n locale,\n ),\n sortValue,\n kindFilter,\n ],\n queryFn: async ({ pageParam }) => {\n const response = await api.media.list({\n \"filter[title]\": debouncedSearch || undefined,\n \"filter[ownership]\": ownerFilter,\n \"filter[content_format]\": bffKind,\n \"page[cursor]\": pageParam,\n \"page[limit]\": PAGE_SIZE,\n sort: bffSort,\n language_iso: locale,\n });\n return response;\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,\n initialPageParam: undefined as string | undefined,\n placeholderData: keepPreviousData,\n });\n\n const filteredItems = useMemo(\n () => data?.pages.flatMap((p) => p.media) ?? [],\n [data?.pages],\n );\n\n const sentinelRef = useInfiniteListSentinel({\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n error,\n });\n\n const kindFilterOptions = useMemo(\n () =>\n KIND_FILTER_OPTIONS.map((option) => ({\n value: option.value,\n label: t(option.labelKey),\n })),\n [t],\n );\n\n const kindFilterLabel =\n kindFilterOptions.find((option) => option.value === kindFilter)?.label ??\n t(\"media_filter_all_types\");\n\n const kindFilterTrigger = (\n <>\n <Filter className=\"size-4\" />\n {/* The label would crowd the row on a phone, so show just the icon there. */}\n <span className=\"hidden sm:inline\">{kindFilterLabel}</span>\n </>\n );\n\n const filterBar = (\n <div className=\"space-y-6\">\n {showProductSharablesCarousel && <ProductSharablesCarousel />}\n\n <ShareableFilterBarLayout\n tabs={\n <OwnerFilterTabs\n value={ownerFilter}\n onValueChange={setOwnerFilter}\n myLabel={t(\"media_my_media_tab\")}\n fullWidthOnMobile\n />\n }\n controls={\n <>\n <div className={SHAREABLE_SEARCH_SORT_CLASS}>\n <SearchSort\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n searchValue={searchTerm}\n onSearchChange={setSearchTerm}\n placeholder={t(\"media_search_placeholder\")}\n sortOptions={[\n { label: t(\"sort_newest\"), value: \"newest\" },\n { label: t(\"sort_name_asc\"), value: \"title_asc\" },\n { label: t(\"sort_name_desc\"), value: \"title_desc\" },\n ]}\n sortValue={sortValue}\n onSortChange={setSortValue}\n />\n </div>\n\n {/* SearchSort takes a presentation prop to pick its bottom sheet or\n dropdown. The kind filter has no such prop, so it chooses here.\n Keep the two in step. */}\n {isMobile ? (\n <>\n <Button\n variant=\"outline\"\n aria-label={t(\"media_filter_aria\", {\n label: kindFilterLabel,\n })}\n onClick={() => setIsKindFilterOpen(true)}\n >\n {kindFilterTrigger}\n </Button>\n <MobileActionSheet\n open={isKindFilterOpen}\n onOpenChange={setIsKindFilterOpen}\n title={t(\"media_filter_all_types\")}\n actions={kindFilterOptions.map((option) => ({\n id: option.value,\n label: option.label,\n selected: kindFilter === option.value,\n onSelect: () => setKindFilter(option.value),\n }))}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n aria-label={t(\"media_filter_aria\", {\n label: kindFilterLabel,\n })}\n >\n {kindFilterTrigger}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {kindFilterOptions.map((option) => (\n <DropdownMenuItem\n key={option.value}\n onClick={() => setKindFilter(option.value)}\n >\n <span className=\"flex-1\">{option.label}</span>\n {kindFilter === option.value && (\n <Check className=\"text-muted-foreground size-4\" />\n )}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n\n <ViewModeToggle value={viewMode} onChange={setViewMode} />\n </>\n }\n />\n </div>\n );\n\n const footer = (\n <>\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary size-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n {error && (\n <p className=\"bg-destructive/10 text-destructive rounded-lg px-3 py-2\">\n {t(\"common_error_prefix\")}\n {error.message}\n </p>\n )}\n </>\n );\n\n return (\n <>\n <ScreenHeaderActions>\n {readOnly ? null : (\n <Button onClick={() => navigate(\"media/new\")} size=\"sm\">\n <Plus className=\"mr-1 size-4\" />\n {t(\"media_add_button\")}\n </Button>\n )}\n </ScreenHeaderActions>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"media_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableListLayout\n isLoading={isLoading}\n isEmpty={\n !error &&\n filteredItems.length === 0 &&\n !isFetchingNextPage &&\n !hasNextPage\n }\n emptyMessage={getFilteredEmptyMessage({\n searchTerm: debouncedSearch,\n ownerFilter,\n hasOtherFilters: kindFilter !== \"all\",\n entityName: \"media\",\n defaultMessage: t(\"media_empty_default\"),\n t,\n messages: {\n filterSearchNoResults: t(\"common_filter_search_no_results\", {\n entityName: \"media\",\n searchTerm: debouncedSearch,\n }),\n searchNoResults: t(\"common_search_no_results\", {\n entityName: \"media\",\n searchTerm: debouncedSearch,\n }),\n filterNoResults: t(\"common_filter_no_results\", {\n entityName: \"media\",\n }),\n },\n })}\n filters={filterBar}\n footer={footer}\n sentinelRef={sentinelRef}\n loadingFilterShape=\"search-view\"\n >\n {viewMode === \"grid\" ? (\n <div className={SHAREABLE_GRID_CLASS}>\n {filteredItems.map((item) => {\n return (\n <div key={item.id} className=\"relative\">\n <ShareItemCard\n title={item.title ?? \"\"}\n imageUrl={item.image_url}\n href={`media/${item.id}`}\n badge={{\n text: getMediaKindLabel(item.kind, {\n video: t(\"media_type_video\"),\n pdf: t(\"media_type_pdf\"),\n image: t(\"media_type_image\"),\n }),\n }}\n isVideo={item.kind === \"video\"}\n subtitle={\n <span className=\"flex items-center gap-1.5\">\n {getMediaKindLabel(item.kind, {\n video: t(\"media_type_video\"),\n pdf: t(\"media_type_pdf\"),\n image: t(\"media_type_image\"),\n })}\n {item.owner_type === \"user\" && (\n <Badge\n variant=\"secondary\"\n className=\"px-1.5 py-0 text-[10px] leading-4\"\n >\n {t(\"media_my_media_badge\")}\n </Badge>\n )}\n </span>\n }\n />\n {(canCuratePlaylists ||\n (!readOnly && item.owner_type === \"user\")) && (\n <div className=\"absolute top-2 right-2\">\n <MediaRowActionsMenu\n canCuratePlaylists={canCuratePlaylists}\n canDelete={!readOnly && item.owner_type === \"user\"}\n onDelete={() => setPendingDeleteId(item.id)}\n onAddToPlaylist={openPlaylistDialog(item.id, \"pick\")}\n onNewPlaylist={openPlaylistDialog(item.id, \"create\")}\n />\n </div>\n )}\n </div>\n );\n })}\n </div>\n ) : (\n <div className={SHAREABLE_LIST_CLASS}>\n {filteredItems.map((item) => {\n return (\n <ShareableListRow\n key={item.id}\n imageUrl={item.image_url}\n imageAlt={item.title ?? \"\"}\n title={item.title ?? t(\"common_untitled\")}\n subtitle={\n <>\n {getMediaKindLabel(item.kind, {\n video: t(\"media_type_video\"),\n pdf: t(\"media_type_pdf\"),\n image: t(\"media_type_image\"),\n })}\n {item.owner_type === \"user\" && (\n <Badge\n variant=\"secondary\"\n className=\"px-1.5 py-0 text-[10px] leading-4\"\n >\n {t(\"media_my_media_badge\")}\n </Badge>\n )}\n </>\n }\n onClick={() => navigate(`media/${item.id}`)}\n trailing={\n (canCuratePlaylists ||\n (!readOnly && item.owner_type === \"user\")) && (\n <MediaRowActionsMenu\n canCuratePlaylists={canCuratePlaylists}\n canDelete={!readOnly && item.owner_type === \"user\"}\n onDelete={() => setPendingDeleteId(item.id)}\n onAddToPlaylist={openPlaylistDialog(item.id, \"pick\")}\n onNewPlaylist={openPlaylistDialog(item.id, \"create\")}\n />\n )\n }\n />\n );\n })}\n </div>\n )}\n </ShareableListLayout>\n\n {/* Delete confirmation */}\n <ResponsiveDialog\n open={pendingDeleteId !== null}\n onOpenChange={(open) => !open && setPendingDeleteId(null)}\n title={t(\"media_delete_confirm_title\")}\n description={t(\"media_delete_confirm_description\")}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n role=\"alertdialog\"\n size=\"compact\"\n >\n <DialogFooter>\n <Button\n variant=\"secondary\"\n onClick={() => setPendingDeleteId(null)}\n disabled={isDeleting}\n >\n {t(\"common_cancel\")}\n </Button>\n <Button\n variant=\"destructive\"\n onClick={confirmDelete}\n disabled={isDeleting}\n >\n {isDeleting ? <Spinner className=\"size-4\" /> : t(\"common_delete\")}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n\n {playlistDialog && (\n <AddToPlaylistDialog\n open\n onOpenChange={(next) => {\n if (!next) setPlaylistDialog(null);\n }}\n contentId={playlistDialog.mediaId}\n contentType=\"Medium\"\n mode={playlistDialog.mode}\n resumePlaylistId={pendingCreates[playlistDialog.mediaId] ?? null}\n restoreFocusTo={playlistDialogTriggerRef}\n onPlaylistCreated={(playlistId) =>\n setPendingCreates((pending) => ({\n ...pending,\n [playlistDialog.mediaId]: playlistId,\n }))\n }\n onAdded={() =>\n setPendingCreates(\n ({ [playlistDialog.mediaId]: _done, ...rest }) => rest,\n )\n }\n />\n )}\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage } from \"../../context\";\nimport { ShoppingBag, ArrowUpRight } from \"lucide-react\";\nimport type { shareables } from \"@fluid-app/shareables-core\";\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\ntype TaggedProduct = shareables.Relateable;\n\ninterface TaggedProductsListProps {\n products: TaggedProduct[];\n /** Called when user clicks a product card */\n onProductClick?: (productId: number) => void;\n}\n\nfunction resolvePrice(product: TaggedProduct): string | null {\n let price = product.display_price || product.price;\n\n if (product.variants && product.variants.length > 0) {\n const firstVariant = product.variants[0];\n if (firstVariant?.display_price) {\n price = firstVariant.display_price;\n } else if (firstVariant?.price) {\n price = firstVariant.price;\n }\n }\n\n if (!price) return null;\n\n const numPrice = parseFloat(price.replace(/[^0-9.-]/g, \"\"));\n if (Number.isNaN(numPrice) || numPrice <= 0) return null;\n\n if (price.includes(\"$\")) return price;\n\n return `$${numPrice.toFixed(2)}`;\n}\n\nexport default function TaggedProductsList({\n products,\n onProductClick,\n}: TaggedProductsListProps): React.JSX.Element {\n const renderImage = useRenderImage();\n const { t } = useShareablesTranslation();\n\n const handleProductClick = (\n productId: number | undefined,\n e: React.MouseEvent,\n ) => {\n e.stopPropagation();\n if (productId == null) return;\n onProductClick?.(productId);\n };\n\n if (!products || products.length === 0) {\n return (\n <div className=\"mb-6\">\n <div className=\"mb-4 px-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n {t(\"share_tagged_products_count\", { count: \"0\" })}\n </h2>\n </div>\n <div className=\"flex flex-1 items-center justify-center px-6 py-12\">\n <div className=\"text-center\">\n <div className=\"text-muted-foreground mb-2\">\n <ShoppingBag className=\"mx-auto size-12\" />\n </div>\n <div className=\"text-foreground mb-1 text-sm\">\n {t(\"share_no_tagged_products\")}\n </div>\n <div className=\"text-muted-foreground text-xs\">\n {t(\"share_no_tagged_products_description\")}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"mb-6\">\n {/* Header */}\n <div className=\"mb-4 px-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n {t(\"share_tagged_products_count\", { count: String(products.length) })}\n </h2>\n </div>\n\n {/* Content */}\n <div className=\"px-4\">\n <div className=\"scrollbar-none flex gap-4 overflow-x-auto pb-2\">\n {products.map((product, index) => {\n const imageUrl =\n product.images?.[0]?.image_url ||\n product.image_url ||\n product.compressed_image_url ||\n DEFAULT_IMAGE;\n const title = product.title || t(\"common_untitled\");\n const displayPrice = resolvePrice(product);\n return (\n <button\n key={product.id ?? index}\n onClick={(e) => handleProductClick(product.id, e)}\n className=\"group bg-muted hover:bg-muted/60 flex w-[168px] shrink-0 flex-col gap-2 rounded-lg p-3 text-left transition-all\"\n >\n {/* Product Image */}\n <div className=\"bg-background relative h-[168px] w-full overflow-hidden rounded-lg\">\n {renderImage({\n src: imageUrl,\n alt: title,\n fill: true,\n className: \"object-cover\",\n })}\n </div>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-col gap-1\">\n {/* Title with hover arrow */}\n <h3 className=\"text-foreground flex items-start gap-1.5 text-[15px] leading-[1.4] font-semibold\">\n <span className=\"line-clamp-2\">{title}</span>\n <ArrowUpRight className=\"text-foreground mt-0.5 size-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100\" />\n </h3>\n\n {/* Price */}\n {displayPrice && (\n <div className=\"text-foreground text-[15px] leading-[1.4] font-normal\">\n {displayPrice}\n </div>\n )}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useMemo, useRef, useState } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n useShareablesApi,\n useRepContext,\n useShareLink,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Separator,\n cn,\n} from \"@fluid-app/ui-primitives\";\nimport { Pencil, Plus } from \"lucide-react\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport AssetActions from \"../SharePage/AssetActions\";\nimport TaggedProductsList from \"../SharePage/TaggedProductsList\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI, useCanCuratePlaylists } from \"../../context\";\nimport type { ShareablesParentBreadcrumb } from \"../shareables-parent-breadcrumb-types\";\nimport { ShareableDetailLayout } from \"../shared/ShareableDetailLayout\";\nimport { AddToPlaylistDialog } from \"../media/AddToPlaylistDialog\";\n\nexport interface MediaDetailScreenProps {\n mediaId: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n parentBreadcrumb?: ShareablesParentBreadcrumb | null;\n}\n\n/** \"Add To\" disclosure trigger for the media detail screen. A plain toggle\n * button — no floating menu — rendered as `AssetActions`'s\n * `secondaryAction`, positioned to the left of the Download button on the\n * same row. Clicking it toggles `MediaAddToPanel` (below), which\n * MediaDetailScreen renders as `AssetActions`'s `secondaryActionPanel`\n * when `open` is true. Open/closed state lives in MediaDetailScreen (see\n * its `isAddToOpen` state) so both pieces stay in sync; this component\n * only reflects it. Kept as its own component so it's testable without\n * mounting the full detail screen's data-fetching tree, mirroring how\n * `MediaRowActionsMenu` is tested in isolation.\n *\n * Takes a `triggerRef` because it is the one control on this screen that\n * survives the whole add-to-playlist trip: selecting an action closes the\n * panel, which `inert`s the pill that was focused, so the dialog needs this\n * button to hand focus back to. */\nexport function MediaAddToMenu({\n open,\n onToggle,\n triggerRef,\n}: {\n open: boolean;\n onToggle: () => void;\n triggerRef?: React.Ref<HTMLButtonElement>;\n}): React.JSX.Element {\n const { t } = useShareablesTranslation();\n return (\n <Button\n ref={triggerRef}\n type=\"button\"\n aria-expanded={open}\n onClick={onToggle}\n className=\"bg-foreground text-background hover:bg-foreground/70 flex h-10 items-center justify-center gap-2 rounded-lg px-4 transition-all\"\n >\n {t(\"media_add_to_button\")}\n <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n </Button>\n );\n}\n\n/** The row of actions revealed by `MediaAddToMenu`. Rendered as\n * `AssetActions`'s `secondaryActionPanel`, directly below the Add To /\n * Download row, full width. Styled as secondary pills matching\n * `ShareLinkSection`'s social/share buttons (see that file's className)\n * so they read as lightweight options beneath the two black primary\n * buttons above. Selecting any action both performs it and closes the\n * panel — callers are expected to fold that closing behavior into the\n * handlers they pass in (see MediaDetailScreen). */\nexport function MediaAddToPanel({\n open,\n canCuratePlaylists = true,\n onAddToPlaylist,\n onNewPlaylist,\n}: {\n /** Drives the reveal. The panel stays mounted in both states so the\n * height/opacity transition can play on the way out as well as in —\n * unmounting on close would make it disappear abruptly. */\n open: boolean;\n /** Playlists belong to every member type, so this defaults to on. */\n canCuratePlaylists?: boolean;\n onAddToPlaylist: () => void;\n onNewPlaylist: () => void;\n}): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const pillClassName =\n \"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\";\n return (\n // The grid + `grid-rows-[0fr→1fr]` pair is what makes the height\n // animatable: `height: auto` cannot be transitioned, but a grid track\n // fraction can, so the inner `overflow-hidden` wrapper is squeezed to\n // zero and back smoothly without hard-coding a pixel height.\n <div\n inert={!open}\n className={cn(\n \"grid transition-[grid-template-rows,opacity] duration-300 ease-out motion-reduce:transition-none\",\n open ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\",\n )}\n >\n <div className=\"overflow-hidden\">\n <div className=\"mt-2 flex flex-wrap items-center gap-3\">\n {canCuratePlaylists && (\n <>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={onAddToPlaylist}\n className={pillClassName}\n >\n {t(\"media_add_to_playlist\")}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={onNewPlaylist}\n className={pillClassName}\n >\n {t(\"media_new_playlist\")}\n </Button>\n </>\n )}\n </div>\n </div>\n </div>\n );\n}\n\nfunction getBadgeLabel(\n kind: string | null,\n labels: { video: string; image: string; pdf: string; default: string },\n): string {\n switch (kind) {\n case \"video\":\n return labels.video;\n case \"image\":\n return labels.image;\n case \"pdf\":\n return labels.pdf;\n default:\n return labels.default;\n }\n}\n\nexport function MediaDetailScreen({\n mediaId,\n onNavigate,\n onBack,\n parentBreadcrumb,\n}: MediaDetailScreenProps): React.JSX.Element {\n const { t, locale } = useShareablesTranslation();\n const api = useShareablesApi();\n const repContext = useRepContext();\n const { navigate, readOnly, mediaProductsApi } = useShareablesUI();\n const canCuratePlaylists = useCanCuratePlaylists();\n const [playlistDialog, setPlaylistDialog] = useState<{\n mediaId: number;\n mode: \"pick\" | \"create\";\n } | null>(null);\n // Disclosure state for the \"Add To\" row (MediaAddToMenu / MediaAddToPanel)\n // rendered in AssetActions via `secondaryAction` / `secondaryActionPanel`.\n // Lives here rather than in MediaAddToMenu so selecting one of the three\n // revealed actions can close the row from this component's handlers.\n const [isAddToOpen, setIsAddToOpen] = useState(false);\n // A playlist created for this medium whose add then failed. Kept here rather\n // than in the dialog because the dialog unmounts when it closes. A single\n // slot is enough: this screen shows one medium, so unlike the listing screen\n // there is no second medium to collide with.\n const [pendingCreate, setPendingCreate] = useState<{\n mediaId: number;\n playlistId: number;\n } | null>(null);\n // Focus returns to the \"Add To\" toggle when the dialog closes. The pill that\n // was actually clicked lives in the panel, which this screen closes on the\n // way into the dialog, and an `inert` element cannot take focus.\n const addToTriggerRef = useRef<HTMLButtonElement>(null);\n\n const { data: mediaResponse, isLoading } = useQuery({\n queryKey: shareablesKeys.media.detail(Number(mediaId), repContext, locale),\n queryFn: () => api.media.getMediaById(Number(mediaId), locale),\n });\n\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink({ id: Number(mediaId) }, \"Medium\", undefined, locale);\n\n const { data: mediaProductsData } = useQuery({\n queryKey: shareablesKeys.mediaProducts.list(Number(mediaId), locale),\n queryFn: () =>\n mediaProductsApi!.listMediaProducts(Number(mediaId), {\n language_iso: locale,\n }),\n enabled: !!mediaProductsApi,\n });\n\n const taggedProducts = useMemo(\n () =>\n mediaProductsData?.media_products?.map((p) => ({\n id: p.id,\n title: p.name ?? null,\n image_url: p.image_url ?? null,\n price: p.price ?? null,\n slug: p.slug ?? null,\n sku: p.sku ?? null,\n })) ?? [],\n [mediaProductsData],\n );\n\n const mediaItem = mediaResponse?.media;\n\n const displayTitle = mediaItem?.title || \"\";\n const displayImage = mediaItem?.image_url || \"\";\n const displayVideo = mediaItem?.video_url || \"\";\n const displayPdf = mediaItem?.pdf_url || \"\";\n const isVideo = mediaItem?.kind === \"video\" && !!displayVideo;\n const isPdf = mediaItem?.kind === \"pdf\" && !!displayPdf;\n\n const badgeLabel = getBadgeLabel(mediaItem?.kind ?? null, {\n video: t(\"media_type_video\"),\n image: t(\"media_type_image\"),\n pdf: t(\"media_type_pdf\"),\n default: t(\"media_type_default\"),\n });\n\n const rawDescription =\n mediaItem?.description?.body || mediaItem?.stripped || \"\";\n\n const downloadUrl = isVideo\n ? displayVideo\n : mediaItem?.pdf_url || displayImage;\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (\n parentBreadcrumb?.onClick ??\n onBack ??\n (() => navigate(\"media\"))\n )();\n }}\n >\n {parentBreadcrumb?.label ?? t(\"media_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"block max-w-[60vw] truncate align-bottom font-semibold md:max-w-[60ch]\">\n {displayTitle || t(\"media_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ScreenHeaderActions>\n {readOnly || !mediaItem || mediaItem.owner_type !== \"user\" ? null : (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(`media/${mediaId}/edit`)}\n className=\"flex items-center gap-1.5\"\n >\n <Pencil className=\"size-3.5\" />\n {t(\"common_edit\")}\n </Button>\n )}\n </ScreenHeaderActions>\n <ShareableDetailLayout\n isLoading={isLoading}\n notFound={!mediaItem}\n notFoundMessage={t(\"media_not_found\")}\n title={displayTitle}\n description={rawDescription}\n // Trim ~5% off the viewport budget (95vh vs 100vh) so the right\n // column + footer padding fits without a page-level scroll.\n containerHeightClass=\"md:h-[calc(95vh-140px)]\"\n image={\n <div className=\"relative h-full overflow-hidden\">\n <SharePageImageDisplay\n displayImage={displayImage}\n displayTitle={displayTitle}\n displayVideo={isVideo ? displayVideo : undefined}\n displayPdf={isPdf ? displayPdf : undefined}\n isVideo={isVideo}\n badgeLabel={badgeLabel}\n rounded\n showBadge={false}\n pdfPrevAriaLabel={t(\"pdf_previous_page_aria\")}\n pdfNextAriaLabel={t(\"pdf_next_page_aria\")}\n pdfDocumentTitle={t(\"pdf_document_title\")}\n />\n {/* Media type chip overlay (Edit button moved to the screen\n header actions). Kept at z-0 so it can't scroll over the\n sticky ScreenHeader (which sits at z-10). */}\n <span className=\"bg-background/80 text-foreground absolute top-3 right-3 z-0 inline-flex h-8 items-center rounded-full px-3 text-xs font-medium shadow-md backdrop-blur-sm\">\n {badgeLabel}\n </span>\n </div>\n }\n actions={\n <AssetActions\n downloadUrl={downloadUrl || null}\n displayTitle={displayTitle}\n shareLink={shareLinkError ? null : shareLink || null}\n shareLinkLoading={shareLinkLoading}\n isVideo={isVideo}\n relateableId={Number(mediaId)}\n relateableType=\"Medium\"\n secondaryAction={\n canCuratePlaylists && mediaItem ? (\n <MediaAddToMenu\n open={isAddToOpen}\n onToggle={() => setIsAddToOpen((prev) => !prev)}\n triggerRef={addToTriggerRef}\n />\n ) : undefined\n }\n secondaryActionPanel={\n canCuratePlaylists && mediaItem ? (\n <MediaAddToPanel\n open={isAddToOpen}\n canCuratePlaylists={canCuratePlaylists}\n onAddToPlaylist={() => {\n setPlaylistDialog({\n mediaId: Number(mediaId),\n mode: \"pick\",\n });\n setIsAddToOpen(false);\n }}\n onNewPlaylist={() => {\n setPlaylistDialog({\n mediaId: Number(mediaId),\n mode: \"create\",\n });\n setIsAddToOpen(false);\n }}\n />\n ) : undefined\n }\n />\n }\n >\n {taggedProducts.length > 0 && (\n <>\n <Separator className=\"bg-foreground my-4\" />\n <div className=\"bg-background rounded-lg\">\n <TaggedProductsList\n products={taggedProducts}\n onProductClick={(productId) =>\n onNavigate?.(\"product\", String(productId))\n }\n />\n </div>\n </>\n )}\n </ShareableDetailLayout>\n\n {playlistDialog && (\n <AddToPlaylistDialog\n open\n onOpenChange={(next) => {\n if (!next) setPlaylistDialog(null);\n }}\n contentId={playlistDialog.mediaId}\n contentType=\"Medium\"\n mode={playlistDialog.mode}\n resumePlaylistId={\n pendingCreate?.mediaId === playlistDialog.mediaId\n ? pendingCreate.playlistId\n : null\n }\n restoreFocusTo={addToTriggerRef}\n onPlaylistCreated={(playlistId) =>\n setPendingCreate({ mediaId: playlistDialog.mediaId, playlistId })\n }\n onAdded={() =>\n // Only this medium's slot. A successful add for another\n // medium used to clear a target still owed to this one.\n setPendingCreate((pending) =>\n pending?.mediaId === playlistDialog.mediaId ? null : pending,\n )\n }\n />\n )}\n </>\n );\n}\n","import type { FilePickerApi } from \"@fluid-app/file-picker-core\";\nimport {\n FilePickerApiProvider as CoreFilePickerApiProvider,\n useFilePickerApi,\n} from \"@fluid-app/file-picker-core\";\n\nexport { useFilePickerApi };\n\nexport function FilePickerApiProvider({\n api,\n children,\n}: {\n api: FilePickerApi;\n children: React.ReactNode;\n}): React.JSX.Element {\n return (\n <CoreFilePickerApiProvider value={api}>\n {children}\n </CoreFilePickerApiProvider>\n );\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type {\n FilePickerResult,\n PickerEnrollmentPackItem,\n PickerPageItem,\n PickerProductItem,\n PickerSource,\n} from \"@fluid-app/file-picker-core\";\n\nexport interface FilePickerToast {\n success(title: string): void;\n error(title: string, error?: unknown): void;\n loading(title: string): string | number;\n dismiss(id: string | number): void;\n}\n\nexport interface FilePickerContextValue {\n companyId?: number;\n userId?: number;\n toast: FilePickerToast;\n /**\n * Per-content-type list/search functions used by the non-file picker tabs.\n * `media` is required because MediaTab is the original consumer and several\n * existing host configs always supply it. The other three are optional —\n * a missing key signals to the picker that the corresponding sidebar tab\n * should not render even when listed in `allowedMethods`.\n */\n shareablesClient?: {\n media: {\n list: (...args: unknown[]) => Promise<unknown>;\n cacheKey?: string;\n };\n pages?: PickerSource<PickerPageItem>;\n products?: PickerSource<PickerProductItem>;\n enrollment_packs?: PickerSource<PickerEnrollmentPackItem>;\n };\n apiBaseUrl?: string;\n}\n\nconst FilePickerContext = createContext<FilePickerContextValue | null>(null);\n\nexport function FilePickerProvider({\n children,\n value,\n}: {\n children: React.ReactNode;\n value: FilePickerContextValue;\n}): React.JSX.Element {\n return (\n <FilePickerContext.Provider value={value}>\n <FilePickerSelectionProvider>{children}</FilePickerSelectionProvider>\n </FilePickerContext.Provider>\n );\n}\n\nexport function useFilePickerContext(): FilePickerContextValue {\n const ctx = useContext(FilePickerContext);\n if (!ctx) {\n throw new Error(\n \"useFilePickerContext must be used within a FilePickerProvider\",\n );\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Unified cross-tab selection (opt-in via FilePickerConfig.unifiedSelection).\n//\n// When a tab body reads/writes the unified list it does so through these\n// hooks. Default per-method picker UX still works because each tab only\n// reaches into the unified context when explicitly wired by FilePicker.\n// ---------------------------------------------------------------------------\n\ntype UnifiedSelectionState = {\n items: FilePickerResult[];\n};\n\ntype UnifiedSelectionApi = {\n add: (item: FilePickerResult) => void;\n remove: (assetCode: string) => void;\n clear: () => void;\n isSelected: (assetCode: string) => boolean;\n selectionIndex: (assetCode: string) => number;\n};\n\nconst FilePickerSelectionReadContext =\n createContext<UnifiedSelectionState | null>(null);\nconst FilePickerSelectionWriteContext =\n createContext<UnifiedSelectionApi | null>(null);\n\nfunction FilePickerSelectionProvider({\n children,\n}: {\n children: React.ReactNode;\n}): React.JSX.Element {\n // Default no-op state. The active <FilePicker> mounts a real provider\n // (FilePickerSelectionScope) when `config.unifiedSelection === true`,\n // overriding these defaults.\n return (\n <FilePickerSelectionReadContext.Provider value={null}>\n <FilePickerSelectionWriteContext.Provider value={null}>\n {children}\n </FilePickerSelectionWriteContext.Provider>\n </FilePickerSelectionReadContext.Provider>\n );\n}\n\nexport function FilePickerSelectionScope({\n state,\n api,\n children,\n}: {\n state: UnifiedSelectionState;\n api: UnifiedSelectionApi;\n children: React.ReactNode;\n}): React.JSX.Element {\n return (\n <FilePickerSelectionReadContext.Provider value={state}>\n <FilePickerSelectionWriteContext.Provider value={api}>\n {children}\n </FilePickerSelectionWriteContext.Provider>\n </FilePickerSelectionReadContext.Provider>\n );\n}\n\n/**\n * Returns the unified selection state when the active picker has unified\n * selection enabled, otherwise null. Tabs use this as the signal to switch\n * between unified and per-method behavior.\n */\nexport function useFilePickerSelection(): {\n state: UnifiedSelectionState | null;\n api: UnifiedSelectionApi | null;\n} {\n const state = useContext(FilePickerSelectionReadContext);\n const api = useContext(FilePickerSelectionWriteContext);\n return { state, api };\n}\n\nexport type { UnifiedSelectionApi, UnifiedSelectionState };\n","import type React from \"react\";\n\ninterface BrandIconProps {\n className?: string;\n}\n\nexport function GoogleIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" />\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" />\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" />\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" />\n </svg>\n );\n}\n\nexport function InstagramIcon({\n className,\n}: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm6.406-11.845a1.44 1.44 0 1 0 0 2.881 1.44 1.44 0 0 0 0-2.881z\" />\n </svg>\n );\n}\n\nexport function FacebookIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z\" />\n </svg>\n );\n}\n\nexport function TiktokIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z\" />\n </svg>\n );\n}\n\nexport function DropboxIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 6.13L6 10.13l6 4-6 4-6-4 6-4-6-4 6-4 6 4zm0 0l6-4 6 4-6 4 6 4-6 4-6-4 6-4-6-4zm0 16.74l-6-4 6-4 6 4-6 4z\" />\n </svg>\n );\n}\n\nexport function UnsplashIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7.5 6.75V0h9v6.75h-9zm9 3.75H24V24H0V10.5h7.5v6.75h9V10.5z\" />\n </svg>\n );\n}\n","import type React from \"react\";\nimport { useState, useEffect } from \"react\";\n\nexport const useDebouncedSearch = (\n delay: number = 300,\n): {\n searchQuery: string;\n setSearchQuery: React.Dispatch<React.SetStateAction<string>>;\n debouncedSearchQuery: string;\n} => {\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(\"\");\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n }, delay);\n\n return () => clearTimeout(timer);\n }, [searchQuery, delay]);\n\n return {\n searchQuery,\n setSearchQuery,\n debouncedSearchQuery,\n };\n};\n","import { useState, useCallback, useMemo, useEffect } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n damQueryKeys,\n type DamAsset,\n type DamTree,\n type FilePickerResult,\n type DamAssetApi,\n type DamQueryResponse,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerApi } from \"../context/FilePickerApiContext\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { useDebouncedSearch } from \"./use-debounced-search\";\n\nexport type UseDamLibraryControlledSearch = {\n searchQuery: string;\n setSearchQuery: (value: string) => void;\n};\n\n// Replace and tag edits act on the asset's default variant, but the API\n// returns variants unordered, so it can arrive at any index. Put it first,\n// because callers read `variants[0]` as \"the file\". `index > 0` covers both\n// leave-alone cases: 0 is already first, -1 is no default at all.\n//\n// fluid-admin's `networking/dam/converters.ts` keeps the same logic for the\n// admin screens.\nconst defaultVariantFirst = <T extends { default?: boolean }>(\n variants: T[],\n): T[] => {\n const index = variants.findIndex((variant) => variant.default === true);\n const defaultVariant = index > 0 ? variants[index] : undefined;\n if (!defaultVariant) return variants;\n return [defaultVariant, ...variants.filter((_, i) => i !== index)];\n};\n\nconst convertApiAssetToInternal = (apiAsset: DamAssetApi): DamAsset => {\n const defaultUrl =\n (apiAsset as { default_variant_url?: string }).default_variant_url ?? \"\";\n return {\n id: apiAsset.id,\n // Fallback to `asset_code` for legacy API responses that used that field name instead of `code`\n asset_code:\n apiAsset.code ||\n (apiAsset as unknown as { asset_code: string }).asset_code,\n name: apiAsset.name,\n category: apiAsset.category,\n default_variant_url: defaultUrl || undefined,\n variants: defaultVariantFirst(\n Array.isArray(apiAsset.variants) ? apiAsset.variants : [],\n ).map((variant) => {\n const variantAny = variant as unknown as {\n metadata?: Record<string, unknown>;\n ready?: boolean;\n };\n return {\n id: String(variant.id || \"\"),\n url: variant.url || defaultUrl || \"\",\n file_name: variant.file_name || \"\",\n mime_type: variant.mime_type || \"\",\n metadata: variantAny.metadata || {},\n ready: variantAny.ready ?? variant.processing_status === \"completed\",\n tags: variant.tags || [],\n };\n }),\n };\n};\n\nconst isApiDamAsset = (value: unknown): value is DamAssetApi => {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (\"code\" in value || \"asset_code\" in value) &&\n \"name\" in value &&\n \"category\" in value\n );\n};\n\nconst normalizeAsset = (asset: DamAsset | DamAssetApi): DamAsset => {\n // Always run through convertApiAssetToInternal for DamAssetApi objects.\n // We can't rely on \"asset_code\" as a discriminator because legacy API\n // responses also carry that field — using \"variants\" with mapped `ready`\n // boolean is a more reliable signal that conversion already happened.\n if (\n \"asset_code\" in asset &&\n Array.isArray((asset as DamAsset).variants) &&\n (asset as DamAsset).variants.length > 0 &&\n typeof (asset as DamAsset).variants[0]?.ready === \"boolean\"\n ) {\n return asset as DamAsset;\n }\n return convertApiAssetToInternal(asset as DamAssetApi);\n};\n\nconst flattenTree = (\n tree: DamTree,\n basePath = \"\",\n depth = 0,\n): { folders: string[]; assets: DamAsset[] } => {\n if (depth > 10) return { folders: [], assets: [] };\n\n const folders: string[] = [];\n const assets: DamAsset[] = [];\n\n for (const [key, value] of Object.entries(tree)) {\n const fullPath = basePath ? `${basePath}.${key}` : key;\n\n if (isApiDamAsset(value)) {\n assets.push(convertApiAssetToInternal(value));\n } else if (typeof value === \"object\" && value !== null) {\n if (Object.keys(value ?? {})?.length === 0) continue;\n folders.push(fullPath);\n const nested = flattenTree(value as DamTree, fullPath, depth + 1);\n folders.push(...nested.folders);\n assets.push(...nested.assets);\n }\n }\n\n return { folders, assets };\n};\n\nexport const useDamLibrary = (\n fileTypeFilter?: string[],\n controlledSearch?: UseDamLibraryControlledSearch,\n): {\n currentPath: string;\n searchQuery: string;\n setSearchQuery: (value: string) => void;\n navigateToPath: (path: string) => void;\n navigateUp: () => void;\n selectAsset: (asset: DamAsset | DamAssetApi, variantId?: string) => void;\n deselectAsset: (assetCode: string) => void;\n clearSelection: () => void;\n clearAndSelectAsset: (\n asset: DamAsset | DamAssetApi,\n variantId?: string,\n ) => void;\n getSelectedResults: () => FilePickerResult[];\n getSelectionIndex: (assetCode: string) => number;\n selectedAssets: string[];\n selectedAssetsMap: Map<string, { asset: DamAsset; variantId?: string }>;\n folders: string[];\n assets: DamAsset[];\n isLoading: boolean;\n error: Error | null;\n isSearching: boolean;\n hasNextPage: boolean;\n loadMore: () => void;\n isFetching: boolean;\n} => {\n const api = useFilePickerApi();\n const { companyId } = useFilePickerContext();\n const [currentPath, setCurrentPath] = useState(\"*\");\n\n const internalSearch = useDebouncedSearch(300);\n const isControlled = controlledSearch !== undefined;\n const searchQuery = isControlled\n ? controlledSearch!.searchQuery\n : internalSearch.searchQuery;\n const setSearchQuery = isControlled\n ? controlledSearch!.setSearchQuery\n : internalSearch.setSearchQuery;\n\n const [controlledDebounced, setControlledDebounced] = useState(\n controlledSearch?.searchQuery ?? \"\",\n );\n useEffect(() => {\n if (!isControlled) return;\n const timer = setTimeout(() => setControlledDebounced(searchQuery), 300);\n return () => clearTimeout(timer);\n }, [isControlled, searchQuery]);\n\n const debouncedSearchQuery = isControlled\n ? controlledDebounced\n : internalSearch.debouncedSearchQuery;\n\n const [selectedAssets, setSelectedAssets] = useState<\n Map<string, { asset: DamAsset; variantId?: string }>\n >(new Map());\n\n const canonicalPathFilters = useMemo(() => {\n if (!fileTypeFilter || !companyId) return fileTypeFilter;\n return fileTypeFilter.map((filter) =>\n filter.includes(\".\") ? filter : `${companyId}.${filter}.*`,\n );\n }, [fileTypeFilter, companyId]);\n\n const ltreeQueryPath = useMemo(() => {\n if (!canonicalPathFilters?.length) return currentPath;\n if (canonicalPathFilters?.length === 1) return canonicalPathFilters[0];\n if (!companyId) return currentPath;\n\n const categories = canonicalPathFilters\n .map((path) => path.split(\".\")[1])\n .filter(Boolean);\n\n return categories?.length\n ? `${companyId}.${categories.join(\"|\")}.*`\n : currentPath;\n }, [canonicalPathFilters, currentPath, companyId]);\n\n const {\n data,\n isLoading,\n error,\n isFetching,\n fetchNextPage,\n hasNextPage,\n isFetchingNextPage,\n } = useInfiniteQuery({\n queryKey: damQueryKeys.query({\n path: ltreeQueryPath,\n search: debouncedSearchQuery,\n filters: canonicalPathFilters,\n }),\n queryFn: ({ pageParam }) =>\n api.queryDamAssets({\n path: ltreeQueryPath || \"*\",\n search: debouncedSearchQuery,\n tags_string: \"*\",\n exclude_tags: \"file_resource\",\n exclude_categories: \"text,other\",\n cursor: pageParam as string | undefined,\n }),\n getNextPageParam: (lastPage) =>\n (lastPage as DamQueryResponse)?.meta?.next_cursor || undefined,\n initialPageParam: undefined as string | undefined,\n refetchOnWindowFocus: false,\n });\n\n const navigateUp = useCallback(() => {\n if (currentPath === \"*\") return;\n\n const pathParts = currentPath.split(\".\");\n if (pathParts?.length <= 1) {\n setCurrentPath(\"*\");\n return;\n }\n if (pathParts?.[pathParts?.length - 1] === \"*\") {\n const parentParts = pathParts.slice(0, -2);\n if (parentParts?.length === 0) {\n setCurrentPath(\"*\");\n } else {\n setCurrentPath(parentParts.join(\".\") + \".*\");\n }\n } else {\n const parentParts = pathParts.slice(0, -1);\n setCurrentPath(parentParts.join(\".\") + \".*\");\n }\n }, [currentPath]);\n\n const selectAsset = useCallback(\n (asset: DamAsset | DamAssetApi, variantId?: string) => {\n const normalized = normalizeAsset(asset);\n setSelectedAssets((prev) => {\n const next = new Map(prev);\n next.set(normalized.asset_code, { asset: normalized, variantId });\n return next;\n });\n },\n [],\n );\n\n const deselectAsset = useCallback((assetCode: string) => {\n setSelectedAssets((prev) => {\n if (!prev.has(assetCode)) return prev;\n const next = new Map(prev);\n next.delete(assetCode);\n return next;\n });\n }, []);\n\n const clearSelection = useCallback(() => {\n setSelectedAssets(new Map());\n }, []);\n\n const clearAndSelectAsset = useCallback(\n (asset: DamAsset | DamAssetApi, variantId?: string) => {\n const normalized = normalizeAsset(asset);\n setSelectedAssets(\n new Map([[normalized.asset_code, { asset: normalized, variantId }]]),\n );\n },\n [],\n );\n\n const getSelectedResults = useCallback((): FilePickerResult[] => {\n return Array.from(selectedAssets.values()).map(({ asset, variantId }) => {\n const variant = variantId\n ? asset.variants.find((v) => v.id === variantId)\n : asset.variants.find((v) => v.ready) || asset.variants[0];\n const defaultVariantUrl = asset.default_variant_url ?? \"\";\n const fileUrl = variant?.url || defaultVariantUrl || \"\";\n\n const metadata = variant?.metadata || {};\n const dimensions =\n typeof metadata.width === \"number\" &&\n typeof metadata.height === \"number\"\n ? { width: metadata.width, height: metadata.height }\n : undefined;\n\n return {\n asset_id: asset.id,\n asset_code: asset.asset_code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: variant?.file_name || asset.name,\n mime_type: variant?.mime_type || \"\",\n dimensions,\n },\n upload_status: \"ready\",\n variant_id: variant?.id || undefined,\n };\n });\n }, [selectedAssets]);\n\n const getSelectionIndex = useCallback(\n (assetCode: string): number => {\n const keys = Array.from(selectedAssets.keys());\n const index = keys.indexOf(assetCode);\n return index === -1 ? -1 : index + 1;\n },\n [selectedAssets],\n );\n\n const selectedAssetCodes = useMemo(\n () => Array.from(selectedAssets.keys()),\n [selectedAssets],\n );\n\n const getCompanyTree = useCallback(\n (tree: DamTree): DamTree => {\n const companyIdStr = companyId?.toString();\n return companyIdStr && tree[companyIdStr]\n ? { [companyIdStr]: tree[companyIdStr] }\n : tree;\n },\n [companyId],\n );\n\n const { folders, allAssets } = useMemo(() => {\n if (!data?.pages?.length) {\n return { folders: [], allAssets: [] };\n }\n\n const companyIdStr = companyId?.toString();\n const excluded: string[] = companyIdStr ? [companyIdStr] : [];\n if (fileTypeFilter?.length && companyIdStr) {\n fileTypeFilter.forEach((filter) => {\n if (!filter.includes(\".\")) {\n excluded.push(`${companyIdStr}.${filter}`);\n }\n });\n }\n\n const folderSet = new Set<string>();\n const assetMap = new Map<string, DamAsset>();\n for (const page of data.pages) {\n const response = page as DamQueryResponse;\n const flat = flattenTree(getCompanyTree(response.tree));\n for (const folder of flat.folders) {\n if (!excluded.includes(folder)) {\n folderSet.add(folder);\n }\n }\n for (const asset of flat.assets) {\n assetMap.set(asset.asset_code, asset);\n }\n }\n\n return {\n folders: Array.from(folderSet),\n allAssets: Array.from(assetMap.values()),\n };\n }, [data, getCompanyTree, companyId, fileTypeFilter]);\n\n return {\n currentPath,\n searchQuery,\n setSearchQuery,\n navigateToPath: setCurrentPath,\n navigateUp,\n selectAsset,\n deselectAsset,\n clearSelection,\n clearAndSelectAsset,\n getSelectedResults,\n getSelectionIndex,\n selectedAssets: selectedAssetCodes,\n selectedAssetsMap: selectedAssets,\n folders: folders ?? [],\n assets: allAssets ?? [],\n isLoading,\n error,\n isSearching: searchQuery !== debouncedSearchQuery,\n hasNextPage: Boolean(hasNextPage),\n loadMore: fetchNextPage,\n isFetching: isFetching || isFetchingNextPage,\n };\n};\n","import { useEffect, useState } from \"react\";\n\nconst MOBILE_BREAKPOINT = 768;\n\nexport function useIsMobile(): boolean {\n const [isMobile, setIsMobile] = useState(false);\n\n useEffect(() => {\n if (typeof window.matchMedia !== \"function\") {\n setIsMobile(false);\n return;\n }\n\n const mediaQuery = window.matchMedia(\n `(max-width: ${MOBILE_BREAKPOINT - 1}px)`,\n );\n const update = () => setIsMobile(mediaQuery.matches);\n\n update();\n mediaQuery.addEventListener(\"change\", update);\n return () => mediaQuery.removeEventListener(\"change\", update);\n }, []);\n\n return isMobile;\n}\n","import type React from \"react\";\nimport type { UseFormReturn } from \"react-hook-form\";\nimport * as z from \"zod\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"../hooks/use-is-mobile\";\n\nexport const folderSchema: z.ZodObject<{\n folderName: z.ZodString;\n}> = z.object({\n folderName: z\n .string()\n .min(1, \"Folder name is required\")\n .min(3, \"Folder name must be at least 3 characters\")\n .max(50, \"Folder name must be at most 50 characters\")\n .regex(\n /^[a-zA-Z0-9_]+$/,\n \"Folder name may only contain letters, numbers, and underscores\",\n ),\n});\n\ntype FolderFormValues = z.infer<typeof folderSchema>;\n\ninterface AddFolderModalProps {\n open: boolean;\n setOpen(b: boolean): void;\n onSubmit(v: FolderFormValues): void;\n form: UseFormReturn<FolderFormValues>;\n isLoading?: boolean;\n}\n\nexport function AddFolderModal({\n open,\n setOpen,\n onSubmit,\n form,\n isLoading,\n}: AddFolderModalProps): React.JSX.Element {\n const isMobile = useIsMobile();\n const handleClose = () => {\n setOpen(false);\n form.reset();\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={handleClose}\n title=\"Add Folder\"\n description=\"Create a new folder by entering a name below.\"\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n size=\"compact\"\n >\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"folderName\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Folder Name</FormLabel>\n <FormControl>\n <Input\n placeholder=\"Enter folder name\"\n {...field}\n disabled={isLoading}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button variant=\"default\" type=\"submit\" disabled={isLoading}>\n {isLoading ? \"Creating...\" : \"Add Folder\"}\n </Button>\n </form>\n </Form>\n </ResponsiveDialog>\n );\n}\n","import type React from \"react\";\nimport {\n cn,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n Button,\n DialogFooter,\n} from \"@fluid-app/ui-primitives\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"../hooks/use-is-mobile\";\nimport { XIcon, Trash2Icon, AlertTriangleIcon } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\n\ntype AssetActionsProps = {\n onDiscard(): void;\n onDelete(): void;\n};\n\ntype HoverIconProps = {\n label: string;\n icon: LucideIcon;\n onClick(): void;\n className?: string;\n};\n\nconst HoverIcon = ({\n icon: Icon,\n label,\n onClick,\n className,\n}: HoverIconProps) => {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className={cn(\n \"mr-0.5 rounded-md border border-gray-300 bg-white p-2 text-gray-600 transition-colors hover:bg-gray-50 hover:text-gray-900\",\n className,\n )}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n onClick();\n }}\n >\n <Icon className=\"h-3.5 w-3.5\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <p>{label}</p>\n </TooltipContent>\n </Tooltip>\n );\n};\n\nexport function AssetActions({\n onDiscard,\n onDelete,\n}: AssetActionsProps): React.JSX.Element {\n return (\n <div className={\"absolute top-1 right-0.5 z-1 hidden group-hover:flex\"}>\n <TooltipProvider>\n <HoverIcon label=\"Discard\" icon={XIcon} onClick={onDiscard} />\n\n <HoverIcon\n label=\"Delete\"\n icon={Trash2Icon}\n onClick={onDelete}\n className=\"border-red-300 text-red-600 hover:bg-red-50 hover:text-red-700\"\n />\n </TooltipProvider>\n </div>\n );\n}\n\ntype ActionModalProps = {\n open: boolean;\n title: string;\n description: string;\n cancelLabel?: string;\n actionLabel?: string;\n onCancel?(): void;\n onAction(): void;\n actionVariant?:\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | \"link\";\n loading?: boolean;\n};\n\nexport function ActionModal({\n title,\n description,\n actionLabel = \"Delete\",\n cancelLabel = \"Cancel\",\n open,\n onAction,\n onCancel,\n actionVariant = \"destructive\",\n loading,\n}: ActionModalProps): React.JSX.Element {\n const isMobile = useIsMobile();\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(nextOpen) => {\n if (!nextOpen) onCancel?.();\n }}\n title={title}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n size=\"compact\"\n >\n <div className=\"sm:flex sm:items-start\">\n <AlertTriangleIcon className=\"mr-2 h-5 w-5 shrink-0 text-blue-600\" />\n <div className=\"mt-2\">\n <p className=\"text-sm text-gray-500\">{description}</p>\n </div>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n onClick={onCancel}\n variant=\"secondary\"\n disabled={loading}\n className=\"cursor-pointer\"\n >\n {cancelLabel}\n </Button>\n <Button\n disabled={loading}\n type=\"button\"\n onClick={onAction}\n variant={actionVariant}\n className=\"cursor-pointer\"\n >\n {actionLabel}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n );\n}\n","import type { LucideIcon } from \"lucide-react\";\nimport { ImageIcon, VideoIcon, FileIcon } from \"lucide-react\";\n\nexport function getCategoryIcon(category: string): LucideIcon {\n switch (category) {\n case \"images\":\n return ImageIcon;\n case \"videos\":\n return VideoIcon;\n default:\n return FileIcon;\n }\n}\n","\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport type { DamAsset, DamVariant } from \"@fluid-app/file-picker-core\";\nimport { getCategoryIcon } from \"../../utils/icons\";\n\nfunction formatDuration(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n if (h > 0)\n return `${h}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\nfunction VideoDurationBadge({ src }: { src: string }) {\n const [duration, setDuration] = useState<string | null>(null);\n const videoRef = useRef<HTMLVideoElement | null>(null);\n\n useEffect(() => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n video.src = src;\n video.onloadedmetadata = () => {\n if (video.duration && isFinite(video.duration)) {\n setDuration(formatDuration(video.duration));\n }\n };\n videoRef.current = video;\n return () => {\n video.onloadedmetadata = null;\n video.src = \"\";\n };\n }, [src]);\n\n if (!duration) return null;\n\n return (\n <span className=\"absolute right-1.5 bottom-1.5 rounded bg-black/70 px-1.5 py-0.5 text-[10px] font-medium text-white\">\n {duration}\n </span>\n );\n}\n\nexport type ThumbnailLayout = \"list\" | \"grid\" | \"preview\";\n\nexport interface AssetThumbnailProps {\n asset: DamAsset;\n variant: DamVariant | undefined;\n layout: ThumbnailLayout;\n imageSize?: number;\n className?: string;\n}\n\nexport function AssetThumbnail({\n asset,\n variant,\n layout,\n imageSize = 100,\n className,\n}: AssetThumbnailProps): React.ReactElement {\n const IconComponent = getCategoryIcon(asset.category);\n\n if (layout === \"list\") {\n return (\n <div\n className={cn(\n \"relative h-12 w-12 shrink-0 overflow-hidden rounded bg-gray-100\",\n className,\n )}\n >\n {variant?.url && asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={asset.name}\n width={48}\n height={48}\n className=\"h-full w-full object-cover\"\n />\n ) : variant?.url && asset.category === \"videos\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={variant.url || \"Video\"}\n width={48}\n height={48}\n className=\"h-full w-full object-cover\"\n />\n ) : variant?.url && asset.category === \"documents\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name || \"Document\"}\n width={48}\n height={48}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconComponent className=\"h-5 w-5 text-gray-400\" />\n </div>\n )}\n </div>\n );\n }\n\n if (layout === \"preview\") {\n return (\n <div\n className={cn(\n \"relative aspect-video w-full overflow-hidden bg-gray-100\",\n className,\n )}\n >\n {variant?.url && asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={asset.name}\n width={960}\n height={540}\n className=\"h-full w-full object-contain\"\n />\n ) : variant?.url && asset.category === \"videos\" ? (\n <video\n src={variant.url}\n autoPlay\n muted\n controls\n className=\"h-full w-full object-contain\"\n />\n ) : variant?.url && asset.category === \"documents\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name}\n width={960}\n height={540}\n className=\"h-full w-full object-contain\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconComponent className=\"h-16 w-16 text-gray-400\" />\n </div>\n )}\n </div>\n );\n }\n\n // grid\n return (\n <div\n className={cn(\n \"relative aspect-square overflow-hidden rounded-t-lg bg-gray-100\",\n className,\n )}\n >\n {variant?.url && asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={asset.name}\n width={imageSize}\n height={imageSize}\n className=\"h-full w-full object-contain\"\n />\n ) : variant?.url && asset.category === \"videos\" ? (\n <>\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name}\n width={imageSize}\n height={imageSize}\n className=\"h-full w-full object-contain\"\n />\n <VideoDurationBadge src={variant.url} />\n </>\n ) : variant?.url && asset.category === \"documents\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name}\n width={imageSize}\n height={imageSize}\n className=\"h-full w-full object-contain\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconComponent className=\"h-6 w-6 text-gray-400\" />\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\n\nexport interface SelectionIndexBadgeProps {\n index: number;\n className?: string;\n}\n\nexport function SelectionIndexBadge({\n index,\n className,\n}: SelectionIndexBadgeProps): React.ReactElement {\n const baseClasses =\n \"flex items-center justify-center rounded-full bg-blue-600 text-xs font-medium text-white shadow-sm z-10\";\n return (\n <div\n className={cn(\"absolute top-2 left-2 h-5 w-5\", baseClasses, className)}\n >\n {index}\n </div>\n );\n}\n","\"use client\";\n\nimport React, { useState } from \"react\";\nimport { EllipsisVerticalIcon, XIcon, Trash2Icon } from \"lucide-react\";\n\nimport {\n cn,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport type { DamAsset } from \"@fluid-app/file-picker-core\";\nimport { useIsMobile } from \"../../hooks/use-is-mobile\";\n\nexport type ActionsMenuPosition = \"list\" | \"grid\";\n\nexport interface AssetActionsMenuProps {\n asset: DamAsset;\n position: ActionsMenuPosition;\n onDiscard: () => void;\n onDelete: () => void;\n}\n\nexport function AssetActionsMenu({\n position,\n onDiscard,\n onDelete,\n}: AssetActionsMenuProps): React.ReactElement {\n const isMobile = useIsMobile();\n const [isOpen, setIsOpen] = useState(false);\n const wrapperClassName = isMobile\n ? position === \"list\"\n ? \"absolute top-1/2 right-2 z-10 -translate-y-1/2\"\n : \"absolute top-2 right-2 z-10\"\n : position === \"list\"\n ? \"absolute top-1/2 right-2 z-10 hidden -translate-y-1/2 group-hover:block has-data-[state=open]:block\"\n : \"absolute top-2 right-2 z-10 hidden group-hover:block has-data-[state=open]:block\";\n\n return (\n <div\n className={cn(wrapperClassName)}\n onClick={(e) => e.stopPropagation()}\n onDoubleClick={(e) => e.stopPropagation()}\n >\n {isMobile ? (\n <>\n <button\n type=\"button\"\n onClick={() => setIsOpen(true)}\n className=\"flex h-7 w-7 items-center justify-center rounded-md border border-gray-200 bg-white text-gray-500 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-700\"\n aria-label=\"Asset actions\"\n >\n <EllipsisVerticalIcon className=\"h-3.5 w-3.5\" />\n </button>\n <MobileActionSheet\n open={isOpen}\n onOpenChange={setIsOpen}\n title=\"Asset actions\"\n actions={[\n { id: \"discard\", label: \"Discard\", onSelect: onDiscard },\n {\n id: \"delete\",\n label: \"Delete\",\n variant: \"destructive\",\n onSelect: onDelete,\n },\n ]}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger className=\"flex h-7 w-7 items-center justify-center rounded-md border border-gray-200 bg-white text-gray-500 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-700\">\n <EllipsisVerticalIcon className=\"h-3.5 w-3.5\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-301\">\n <DropdownMenuItem onClick={onDiscard}>\n <XIcon className=\"h-3.5 w-3.5\" />\n Discard\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={onDelete}\n className=\"text-red-600 focus:text-red-700\"\n >\n <Trash2Icon className=\"h-3.5 w-3.5\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { getAssetCardClassName } from \"@fluid-app/file-picker-core\";\nimport { SelectionIndexBadge } from \"./DamLibraryAsset\";\n\nexport type LibraryAssetViewMode = \"grid\" | \"list\";\n\n/**\n * Normalized item for the shared library/media grid.\n * Consumers map DAM assets or media items to this shape and provide renderThumbnail.\n */\nexport interface LibraryAssetItem<T = unknown> {\n id: string;\n name: string;\n subtitle?: string;\n isSelected: boolean;\n /** 0 = not selected, 1-based when selected (for badge) */\n selectionIndex: number;\n raw: T;\n}\n\nexport interface LibraryAssetGridProps<T> {\n items: LibraryAssetItem<T>[];\n viewMode: LibraryAssetViewMode;\n thumbnailSize: number;\n showNamesOnMedia: boolean;\n renderThumbnail: (item: T, layout: LibraryAssetViewMode) => React.ReactNode;\n onClick: (e: React.MouseEvent, id: string, index: number) => void;\n onDoubleClick?: (e: React.MouseEvent, id: string, index: number) => void;\n renderTopLeftOverlay?: (item: LibraryAssetItem<T>) => React.ReactNode;\n renderTopRightOverlay?: (item: LibraryAssetItem<T>) => React.ReactNode;\n wrapItem?: (\n item: LibraryAssetItem<T>,\n content: React.ReactNode,\n ) => React.ReactNode;\n}\n\nfunction getGridClasses(\n viewMode: LibraryAssetViewMode,\n thumbnailSize: number,\n): string {\n if (viewMode === \"list\") {\n return \"grid-cols-1\";\n }\n if (thumbnailSize <= 35) {\n return \"grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-12 xl:grid-cols-16\";\n }\n if (thumbnailSize <= 50) {\n return \"grid-cols-3 sm:grid-cols-5 md:grid-cols-7 lg:grid-cols-10 xl:grid-cols-14\";\n }\n if (thumbnailSize <= 70) {\n return \"grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8\";\n }\n if (thumbnailSize <= 90) {\n return \"grid-cols-2 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-7\";\n }\n if (thumbnailSize <= 110) {\n return \"grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6\";\n }\n if (thumbnailSize <= 130) {\n return \"grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5\";\n }\n return \"grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4\";\n}\n\nexport function LibraryAssetGrid<T>({\n items,\n viewMode,\n thumbnailSize,\n showNamesOnMedia,\n renderThumbnail,\n onClick,\n onDoubleClick,\n renderTopLeftOverlay,\n renderTopRightOverlay,\n wrapItem,\n}: LibraryAssetGridProps<T>): React.ReactElement {\n const gridClasses = getGridClasses(viewMode, thumbnailSize);\n\n const renderCard = (item: LibraryAssetItem<T>, index: number) => {\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault();\n onClick(e, item.id, index);\n };\n const handleDoubleClick = onDoubleClick\n ? (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onDoubleClick(e, item.id, index);\n }\n : undefined;\n\n const content = (\n <>\n {renderTopRightOverlay?.(item)}\n {viewMode === \"list\" ? (\n <div className=\"flex items-start gap-4\">\n <div className=\"h-12 w-12 shrink-0 overflow-hidden rounded bg-gray-100\">\n {renderThumbnail(item.raw, \"list\")}\n </div>\n <div className=\"min-w-0 flex-1 text-start\">\n <p className=\"truncate text-sm font-medium text-gray-900\">\n {item.name}\n </p>\n {item.subtitle != null && item.subtitle !== \"\" && (\n <p className=\"truncate text-xs text-gray-500\">\n {item.subtitle}\n </p>\n )}\n </div>\n </div>\n ) : (\n <>\n <div className=\"relative aspect-square overflow-hidden rounded-t-lg bg-gray-100 p-2\">\n {renderThumbnail(item.raw, \"grid\")}\n </div>\n {showNamesOnMedia && (\n <div className=\"bg-gray-100 px-2 py-1.5\">\n <p className=\"truncate text-xs font-medium text-gray-900\">\n {item.name}\n </p>\n {item.subtitle && (\n <p className=\"truncate text-[10px] text-gray-500\">\n {item.subtitle}\n </p>\n )}\n </div>\n )}\n </>\n )}\n {item.isSelected && item.selectionIndex > 0 && (\n <SelectionIndexBadge index={item.selectionIndex} />\n )}\n {renderTopLeftOverlay?.(item)}\n </>\n );\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={cn(\n \"group relative cursor-pointer overflow-hidden rounded-lg transition-all\",\n getAssetCardClassName(item.isSelected),\n viewMode === \"list\" && \"flex items-start gap-4 p-3\",\n )}\n onClick={handleClick}\n onDoubleClick={handleDoubleClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick(e as unknown as React.MouseEvent, item.id, index);\n }\n }}\n >\n {content}\n </div>\n );\n };\n\n return (\n <div className={cn(\"grid gap-4\", gridClasses)}>\n {items.map((item, index) => {\n const card = renderCard(item, index);\n const wrapped = wrapItem ? wrapItem(item, card) : card;\n return <React.Fragment key={item.id}>{wrapped}</React.Fragment>;\n })}\n </div>\n );\n}\n","import React, {\n useState,\n useEffect,\n useCallback,\n useImperativeHandle,\n forwardRef,\n useRef,\n useMemo,\n} from \"react\";\nimport {\n Button,\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n cn,\n Dialog,\n DialogOverlay,\n DialogPortal,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { FolderIcon, LoaderIcon, ChevronDownIcon } from \"lucide-react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n damQueryKeys,\n SORT_OPTIONS,\n createAssetClickHandlers,\n type CreateDamAssetPathForAssetsParams,\n type FilePickerConfig,\n type FilePickerResult,\n type DamAsset,\n type DamAssetApi,\n type SortOptionId,\n} from \"@fluid-app/file-picker-core\";\nimport { useDamLibrary } from \"../hooks/use-dam-library\";\nimport type { UseDamLibraryControlledSearch } from \"../hooks/use-dam-library\";\nimport { useFilePickerApi } from \"../context/FilePickerApiContext\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { AddFolderModal, folderSchema } from \"./AddFolderModal\";\nimport { ActionModal } from \"./AssetActions\";\nimport type { ViewMode } from \"./FilePickerHeader\";\nimport { AssetThumbnail, AssetActionsMenu } from \"./DamLibraryAsset\";\nimport {\n LibraryAssetGrid,\n type LibraryAssetItem,\n type LibraryAssetViewMode,\n} from \"./LibraryAssetGrid\";\n\ninterface DamLibraryProps {\n config: FilePickerConfig;\n onAssetsSelected: (results: FilePickerResult[]) => void;\n onSelectionChange?: (\n count: number,\n files?: FilePickerResult[],\n variantCounts?: Record<string, number>,\n ) => void;\n onConfirmAndClose?: (results: FilePickerResult[]) => void;\n onSearchingChange?: (searching: boolean) => void;\n showVariants?: boolean;\n searchQuery?: string;\n onSearchChange?: (value: string) => void;\n thumbnailSize?: number;\n viewMode?: ViewMode;\n showNamesOnMedia?: boolean;\n sortOption?: SortOptionId;\n onFoldersChange?: (folders: string[]) => void;\n previewContainer?: HTMLDivElement | null;\n previewAsset?: DamAsset | null;\n onPreviewAssetChange?: (asset: DamAsset | null) => void;\n}\n\nexport interface DamLibraryRef {\n clearSelection: () => void;\n confirmSelection: () => void;\n confirmAndClose: () => void;\n getSelectedResults: () => FilePickerResult[];\n deselectAssetByCode: (assetCode: string) => void;\n selectAssetsByCodes: (assetCodes: string[]) => string[];\n addAssetToSelection: (asset: DamAsset | DamAssetApi) => void;\n openAddFolderModal: () => void;\n addToExistingFolder: (folderPath: string) => void;\n getVariantCount: (assetCode: string) => number;\n getAssetWithVariants: (assetCode: string) => DamAsset | null;\n setVariantForAsset: (assetCode: string, variantId: string) => void;\n}\n\ntype ActionType = {\n type: \"Discard\" | \"Delete\";\n item: DamAsset;\n};\n\ntype DamBreadcrumbItem = { label: string; path: string; isCurrent: boolean };\n\nfunction getBreadcrumbsFromPath(currentPath: string): DamBreadcrumbItem[] {\n if (currentPath === \"*\") {\n return [{ label: \"All assets\", path: \"*\", isCurrent: true }];\n }\n const normalized = currentPath.endsWith(\".*\")\n ? currentPath.slice(0, -3)\n : currentPath;\n const segments = normalized.split(\".\").filter((s): s is string => Boolean(s));\n if (segments.length === 0) {\n return [{ label: \"All assets\", path: \"*\", isCurrent: true }];\n }\n const crumbs: DamBreadcrumbItem[] = [\n { label: \"All assets\", path: \"*\", isCurrent: false },\n ];\n let pathAcc = \"\";\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i] as string;\n pathAcc = pathAcc ? `${pathAcc}.${segment}` : segment;\n crumbs.push({\n label: segment,\n path: `${pathAcc}.*`,\n isCurrent: i === segments.length - 1,\n });\n }\n return crumbs;\n}\n\nexport const DamLibrary: React.ForwardRefExoticComponent<\n DamLibraryProps & React.RefAttributes<DamLibraryRef>\n> = forwardRef<DamLibraryRef, DamLibraryProps>(\n (\n {\n config,\n onAssetsSelected,\n onSelectionChange,\n onConfirmAndClose,\n onSearchingChange,\n showVariants: _showVariants = false,\n searchQuery: controlledSearchQuery,\n onSearchChange: controlledSetSearchQuery,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n sortOption,\n onFoldersChange,\n previewContainer,\n previewAsset,\n onPreviewAssetChange,\n },\n ref,\n ) => {\n \"use no memo\";\n\n const api = useFilePickerApi();\n const { companyId, toast } = useFilePickerContext();\n const queryClient = useQueryClient();\n\n const controlledSearch: UseDamLibraryControlledSearch | undefined =\n controlledSearchQuery !== undefined && controlledSetSearchQuery\n ? {\n searchQuery: controlledSearchQuery,\n setSearchQuery: controlledSetSearchQuery,\n }\n : undefined;\n\n const {\n currentPath,\n navigateToPath,\n selectAsset,\n deselectAsset,\n clearSelection,\n clearAndSelectAsset,\n getSelectedResults,\n getSelectionIndex,\n selectedAssets,\n selectedAssetsMap,\n folders,\n assets,\n isLoading,\n isSearching,\n hasNextPage,\n loadMore,\n isFetching,\n } = useDamLibrary(config.fileTypeFilter, controlledSearch);\n\n const breadcrumbs = useMemo(\n () => getBreadcrumbsFromPath(currentPath),\n [currentPath],\n );\n\n const sortedAssets = useMemo(() => {\n if (!sortOption || !assets?.length) return assets ?? [];\n const option = SORT_OPTIONS.find((o) => o.id === sortOption);\n if (!option) return assets ?? [];\n\n return [...(assets ?? [])].sort((a, b) => {\n if (option.sortBy === \"name\") {\n const cmp = (a.name ?? \"\").localeCompare(b.name ?? \"\", undefined, {\n sensitivity: \"base\",\n });\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n if (option.sortBy === \"created_at\") {\n const aId = a.id ?? 0;\n const bId = b.id ?? 0;\n const cmp = aId - bId;\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n return 0;\n });\n }, [assets, sortOption]);\n\n useEffect(() => {\n onSearchingChange?.(isSearching);\n }, [isSearching, onSearchingChange]);\n\n useEffect(() => {\n onFoldersChange?.(folders ?? []);\n }, [folders, onFoldersChange]);\n\n const [open, setOpen] = useState(false);\n\n const form = useZodForm(folderSchema, {\n defaultValues: {\n folderName: \"\",\n },\n });\n\n const { mutate, isPending } = useMutation({\n mutationFn: (data: CreateDamAssetPathForAssetsParams[]) => {\n const promises = data.map((params) =>\n api.createDamAssetPathForAssets(params),\n );\n return Promise.all(promises);\n },\n onSuccess: () => {\n toast.success(\"Assets moved to folder successfully\");\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n form.reset();\n setOpen(false);\n clearSelection();\n },\n onError: () => {\n toast.error(\"Failed to move assets to folder.\");\n },\n });\n\n const [action, setAction] = useState<ActionType | null>(null);\n const lastClickedIndexRef = useRef<number>(-1);\n const pendingClickTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n const { mutate: deleteAssetMutate, isPending: isDeleting } = useMutation({\n mutationFn: (assetCode: string) => api.deleteDamAsset(assetCode),\n onSuccess: () => {\n toast.success(\"Asset deleted successfully\");\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n onError: () => {\n toast.error(\"Failed to delete asset.\");\n },\n });\n\n const { mutate: discardAssetMutate, isPending: isDiscarding } = useMutation(\n {\n mutationFn: (assetCode: string) => api.discardDamAsset(assetCode),\n onSuccess: () => {\n toast.success(\"Asset discarded successfully\");\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n onError: () => {\n toast.error(\"Failed to discard asset.\");\n },\n },\n );\n\n const getAssetBaseName = (fileName: string): string => {\n const dotIdx = fileName.lastIndexOf(\".\");\n const rawName = dotIdx > 0 ? fileName.substring(0, dotIdx) : fileName;\n return rawName.replace(/[^a-zA-Z0-9_]/g, \"_\") || \"file\";\n };\n\n const handleNewFolder = useCallback(\n (data: { folderName: string }) => {\n if (!companyId) {\n toast.error(\"Company ID is required to create folders\");\n return;\n }\n const promises = getSelectedResults().map((item) => {\n const basePath = companyId.toString() + \".\";\n const name = getAssetBaseName(item.metadata.file_name);\n\n return {\n code: item.asset_code,\n asset_paths: [`${basePath}${data.folderName}.${name}`],\n };\n });\n\n mutate(promises);\n },\n [getSelectedResults, companyId, mutate, toast],\n );\n\n const handleAddToExistingFolder = useCallback(\n (folderPath: string) => {\n const promises = getSelectedResults().map((item) => {\n const name = getAssetBaseName(item.metadata.file_name);\n\n return {\n code: item.asset_code,\n asset_paths: [`${folderPath}.${name}`],\n };\n });\n\n mutate(promises);\n },\n [getSelectedResults, mutate],\n );\n\n // Expose methods to parent component\n useImperativeHandle(\n ref,\n () => ({\n clearSelection: () => {\n clearSelection();\n },\n confirmSelection: () => {\n const results = getSelectedResults();\n onAssetsSelected(results);\n },\n confirmAndClose: () => {\n const results = getSelectedResults();\n if (onConfirmAndClose) {\n onConfirmAndClose(results);\n } else {\n onAssetsSelected(results);\n }\n },\n getSelectedResults: () => {\n return getSelectedResults();\n },\n deselectAssetByCode: (assetCode: string) => {\n deselectAsset(assetCode);\n },\n addAssetToSelection: (asset: DamAsset | DamAssetApi) => {\n selectAsset(asset);\n },\n selectAssetsByCodes: (assetCodes: string[]): string[] => {\n const setToSelect = new Set(assetCodes);\n const selected: string[] = [];\n for (const asset of sortedAssets || []) {\n if (setToSelect.has(asset.asset_code)) {\n selectAsset(asset);\n selected.push(asset.asset_code);\n }\n }\n return selected;\n },\n openAddFolderModal: () => {\n setOpen(true);\n },\n addToExistingFolder: (folderPath: string) => {\n handleAddToExistingFolder(folderPath);\n },\n getVariantCount: (assetCode: string) => {\n const entry = selectedAssetsMap.get(assetCode);\n return entry?.asset?.variants?.length ?? 0;\n },\n getAssetWithVariants: (assetCode: string) => {\n const entry = selectedAssetsMap.get(assetCode);\n return entry?.asset ?? null;\n },\n setVariantForAsset: (assetCode: string, variantId: string) => {\n const entry = selectedAssetsMap.get(assetCode);\n const asset = entry?.asset;\n if (asset) {\n if (config.maxFiles === 1) {\n clearAndSelectAsset(asset, variantId);\n } else {\n selectAsset(asset, variantId);\n }\n }\n },\n }),\n [\n clearSelection,\n getSelectedResults,\n onAssetsSelected,\n onConfirmAndClose,\n sortedAssets,\n deselectAsset,\n selectAsset,\n clearAndSelectAsset,\n handleAddToExistingFolder,\n selectedAssetsMap,\n config.maxFiles,\n ],\n );\n\n // Keep a stable ref to onSelectionChange to avoid re-running the effect\n // when the parent passes an unstable callback reference.\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n\n // Notify parent when selection count changes (include variant counts so parent can enable \"Change variant\" without reading ref during render)\n useEffect(() => {\n const count = (selectedAssets ?? []).length;\n const files = getSelectedResults();\n const variantCounts: Record<string, number> = {};\n selectedAssetsMap.forEach((entry, assetCode) => {\n const n = entry?.asset?.variants?.length ?? 0;\n if (n > 0) variantCounts[assetCode] = n;\n });\n onSelectionChangeRef.current?.(count, files, variantCounts);\n }, [selectedAssets, getSelectedResults, selectedAssetsMap]);\n\n const handleAssetClick = (\n e: React.MouseEvent,\n asset: DamAsset,\n index: number,\n ) => {\n const originalVariant =\n asset.variants.find((v) => v.ready) || asset.variants[0];\n\n if (e.shiftKey && config.maxFiles !== 1) {\n // Range select: from last clicked to current index\n const last = lastClickedIndexRef.current;\n const from = last >= 0 ? Math.min(last, index) : index;\n const to = last >= 0 ? Math.max(last, index) : index;\n const toSelect = sortedAssets?.slice(from, to + 1) ?? [];\n const maxToAdd = config.maxFiles\n ? Math.max(0, config.maxFiles - selectedAssets.length)\n : toSelect.length;\n let added = 0;\n toSelect.forEach((a: DamAsset) => {\n if (selectedAssets.includes(a.asset_code)) return;\n if (added >= maxToAdd) return;\n const v =\n a.variants.find((v: DamAsset[\"variants\"][number]) => v.ready) ||\n a.variants[0];\n selectAsset(a, v?.id);\n added += 1;\n });\n lastClickedIndexRef.current = index;\n return;\n }\n\n if ((e.metaKey || e.ctrlKey) && config.maxFiles !== 1) {\n // Toggle selection (add only if under maxFiles)\n if (selectedAssets.includes(asset.asset_code)) {\n deselectAsset(asset.asset_code);\n } else if (\n !config.maxFiles ||\n selectedAssets.length < config.maxFiles\n ) {\n selectAsset(asset, originalVariant?.id);\n }\n lastClickedIndexRef.current = index;\n return;\n }\n\n lastClickedIndexRef.current = index;\n\n if (selectedAssets.includes(asset.asset_code)) {\n deselectAsset(asset.asset_code);\n } else {\n if (config.maxFiles === 1) {\n clearAndSelectAsset(asset, originalVariant?.id);\n } else {\n selectAsset(asset, originalVariant?.id);\n }\n }\n };\n\n const closeAction = () => setAction(null);\n\n const libraryItems = useMemo((): LibraryAssetItem<DamAsset>[] => {\n if (!sortedAssets?.length) return [];\n return sortedAssets.map((asset: DamAsset) => {\n const isSelected = selectedAssets.includes(asset.asset_code);\n const selectionIndex = getSelectionIndex(asset.asset_code);\n return {\n id: asset.asset_code,\n name: asset.name ?? \"\",\n subtitle: `${asset.category} • ${asset.variants?.length ?? 0} variant${(asset.variants?.length ?? 0) !== 1 ? \"s\" : \"\"}`,\n isSelected,\n selectionIndex,\n raw: asset,\n };\n });\n }, [sortedAssets, selectedAssets, getSelectionIndex]);\n\n const getImageSize = () => {\n const baseSize = 200;\n return Math.round(baseSize * (thumbnailSize / 100));\n };\n\n return (\n <>\n <AddFolderModal\n open={open}\n setOpen={setOpen}\n onSubmit={handleNewFolder}\n form={form}\n isLoading={isPending}\n />\n\n <ActionModal\n open={action?.type === \"Delete\"}\n description={`This asset **${action?.item.name}** will be permanently deleted.`}\n onCancel={closeAction}\n onAction={() =>\n deleteAssetMutate(action?.item.asset_code ?? \"\", {\n onSuccess: closeAction,\n })\n }\n title=\"Are you sure?\"\n loading={isDeleting}\n />\n\n <ActionModal\n actionLabel=\"Discard\"\n open={action?.type === \"Discard\"}\n description={`This asset **${action?.item.name}** will be discarded from your library and will be permanently deleted after a month!`}\n onAction={() =>\n discardAssetMutate(action?.item.asset_code ?? \"\", {\n onSuccess: closeAction,\n })\n }\n title=\"Are you sure?\"\n onCancel={closeAction}\n actionVariant=\"default\"\n loading={isDiscarding}\n />\n\n {/* Preview popup on double-click */}\n <Dialog\n open={previewAsset !== null}\n onOpenChange={(open) => !open && onPreviewAssetChange?.(null)}\n >\n <DialogPortal container={previewContainer ?? undefined}>\n <DialogOverlay className=\"z-9999 bg-black/70 backdrop-blur-none\" />\n <DialogPrimitive.Content\n className=\"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-10000 max-h-[85vh] w-full max-w-4xl -translate-x-1/2 -translate-y-1/2 overflow-hidden bg-white shadow-xl\"\n style={{ zIndex: 10000 }}\n aria-label=\"Asset preview\"\n >\n {previewAsset && (\n <div className=\"flex max-h-[85vh] flex-col\">\n <AssetThumbnail\n asset={previewAsset}\n variant={\n previewAsset.variants.find((x) => x.ready) ||\n previewAsset.variants[0]\n }\n layout=\"preview\"\n />\n </div>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n </Dialog>\n\n <div>\n <div className=\"space-y-4\">\n <Breadcrumb>\n <BreadcrumbList className=\"min-w-0 flex-wrap\">\n {breadcrumbs.map((crumb, index) => (\n <React.Fragment key={crumb.path}>\n <BreadcrumbItem className=\"inline-flex min-w-0\">\n {crumb.isCurrent ? (\n <BreadcrumbPage className=\"max-w-48 truncate sm:max-w-xs\">\n {crumb.label}\n </BreadcrumbPage>\n ) : (\n <Button\n variant=\"ghost\"\n type=\"button\"\n onClick={() => navigateToPath(crumb.path)}\n className={cn(\n \"hover:text-foreground max-w-48 truncate transition-colors sm:max-w-xs\",\n )}\n >\n {crumb.label}\n </Button>\n )}\n </BreadcrumbItem>\n {index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}\n </React.Fragment>\n ))}\n </BreadcrumbList>\n </Breadcrumb>\n\n {isLoading || isSearching ? (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-2 h-8 w-8 animate-spin rounded-full border-2 border-blue-600 border-t-transparent\"></div>\n <p className=\"text-sm text-gray-500\">\n {isSearching ? \"Searching...\" : \"Loading assets...\"}\n </p>\n </div>\n </div>\n ) : (\n <div className=\"space-y-6\">\n {folders?.length > 0 && (\n <div className=\"space-y-6\">\n <h3 className=\"text-sm font-medium text-gray-900\">\n Folders\n </h3>\n\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5\">\n {folders.map((folder) => (\n <Button\n variant=\"ghost\"\n key={folder}\n onClick={() => navigateToPath(folder + \".*\")}\n className=\"group flex items-center gap-3 rounded-lg border border-gray-200 bg-white p-3 text-left shadow-sm transition-colors hover:border-gray-300 hover:bg-gray-50\"\n >\n <div className=\"flex h-8 w-8 items-center justify-center rounded-md bg-blue-50\">\n <FolderIcon className=\"h-4 w-4 text-blue-600\" />\n </div>\n <span className=\"truncate text-sm font-medium text-gray-900 group-hover:text-gray-700\">\n {folder.split(\".\").pop()}\n </span>\n </Button>\n ))}\n </div>\n </div>\n )}\n\n {sortedAssets?.length > 0 && (\n <div>\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"text-sm font-medium text-gray-900\">\n Assets\n </h3>\n </div>\n\n <LibraryAssetGrid<DamAsset>\n items={libraryItems}\n viewMode={viewMode as LibraryAssetViewMode}\n thumbnailSize={thumbnailSize}\n showNamesOnMedia={showNamesOnMedia}\n renderThumbnail={(raw, layout) => {\n const defaultVariant =\n raw.variants.find((v) => v.ready) || raw.variants[0];\n return (\n <AssetThumbnail\n asset={raw}\n variant={defaultVariant}\n layout={layout}\n imageSize={getImageSize()}\n />\n );\n }}\n onClick={(e, id, index) => {\n const asset = sortedAssets?.[index];\n if (!asset) return;\n const handlers = createAssetClickHandlers(\n pendingClickTimeoutRef,\n handleAssetClick,\n (a) => onPreviewAssetChange?.(a),\n asset,\n index,\n );\n handlers.onClick(e);\n }}\n onDoubleClick={(e, _id, index) => {\n const asset = sortedAssets?.[index];\n if (!asset) return;\n const handlers = createAssetClickHandlers(\n pendingClickTimeoutRef,\n handleAssetClick,\n (a) => onPreviewAssetChange?.(a),\n asset,\n index,\n );\n handlers.onDoubleClick(e);\n }}\n renderTopRightOverlay={(item) => (\n <AssetActionsMenu\n asset={item.raw}\n position={viewMode === \"list\" ? \"list\" : \"grid\"}\n onDiscard={() =>\n setAction({ item: item.raw, type: \"Discard\" })\n }\n onDelete={() =>\n setAction({ item: item.raw, type: \"Delete\" })\n }\n />\n )}\n wrapItem={(item, content) => (\n <TooltipProvider key={item.id}>\n <Tooltip>\n <TooltipTrigger asChild>{content}</TooltipTrigger>\n <TooltipContent>{item.name}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n />\n {hasNextPage && (\n <div className=\"mt-4 flex justify-center\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n loadMore();\n }}\n disabled={isFetching}\n >\n {isFetching ? (\n <>\n <LoaderIcon className=\"mr-2 h-3 w-3 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <ChevronDownIcon className=\"mr-2 h-3 w-3\" />\n Load more\n </>\n )}\n </Button>\n </div>\n )}\n </div>\n )}\n\n {folders?.length === 0 &&\n sortedAssets?.length === 0 &&\n !isLoading && (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"mb-4 rounded-full bg-gray-100 p-3\">\n <FolderIcon className=\"h-6 w-6 text-gray-400\" />\n </div>\n <h3 className=\"mb-1 text-sm font-medium text-gray-900\">\n No assets found\n </h3>\n <p className=\"text-xs text-gray-500\">\n Try searching for something else or navigate to a\n different folder\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n </>\n );\n },\n);\n\nDamLibrary.displayName = \"DamLibrary\";\n","import React from \"react\";\nimport { IMPLEMENTED_PICKER_METHODS } from \"@fluid-app/file-picker-core\";\nimport type { PickerMethod } from \"@fluid-app/file-picker-core\";\nimport { Button } from \"@fluid-app/ui-primitives\";\n\ninterface FilePickerSidebarProps {\n currentStep: \"select\" | \"details\";\n activeMethod: PickerMethod;\n onMethodChange: (method: PickerMethod) => void;\n methods: Array<{\n id: PickerMethod;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n description: string;\n }>;\n}\n\nexport const FilePickerSidebar: React.FC<FilePickerSidebarProps> = ({\n currentStep,\n activeMethod,\n onMethodChange,\n methods,\n}) => {\n if (currentStep !== \"select\") {\n return null;\n }\n\n // Desktop-only: a 256px rail doesn't fit a phone, so below `md` the header\n // title doubles as the method switcher.\n return (\n <div className=\"hidden h-full w-64 flex-col rounded-tl-lg border-r border-gray-200 bg-gray-50 md:flex\">\n <div className=\"flex min-h-0 flex-1 flex-col gap-2 overflow-auto p-2\">\n {/* Upload Files Title */}\n <div className=\"flex flex-col gap-2 pt-3\">\n <h3 className=\"text-sm font-semibold text-gray-900\">Choose source</h3>\n </div>\n\n {/* Main Tabs */}\n <div className=\"flex w-full min-w-0 flex-col gap-1 p-2\">\n {methods\n .filter((method) => IMPLEMENTED_PICKER_METHODS.includes(method.id))\n .map((method) => (\n <Button\n variant=\"ghost\"\n key={method.id}\n onClick={() => onMethodChange(method.id)}\n className={`flex h-8 w-full cursor-pointer items-center justify-start gap-2 rounded-md p-2 text-left text-sm transition-colors hover:bg-gray-100 ${\n activeMethod === method.id\n ? \"bg-gray-100 font-medium text-gray-900\"\n : \"text-gray-700\"\n }`}\n >\n {React.createElement(method.icon, {\n className: \"h-4 w-4 text-gray-500\",\n })}\n <span className=\"font-semibold\">{method.label}</span>\n </Button>\n ))}\n </div>\n\n {/* Cloud Storage */}\n {/* <div className=\"flex w-full min-w-0 flex-col gap-1 p-2\">\n <div className=\"mb-1 px-2 text-xs font-medium text-gray-500\">\n Cloud Storage\n </div>\n {methods\n .filter((method) =>\n [\"google-drive\", \"dropbox\"].includes(method.id),\n )\n .map((method) => {\n const Icon = method.icon;\n return (\n <button\n key={method.id}\n onClick={() => onMethodChange(method.id)}\n className={`flex h-8 w-full cursor-pointer items-center gap-2 rounded-md p-2 text-left text-sm transition-colors hover:bg-blue-50 ${\n activeMethod === method.id\n ? \"bg-blue-50 font-medium text-blue-600\"\n : \"text-gray-700\"\n }`}\n >\n <Icon className=\"h-4 w-4 text-gray-500\" />\n <span className=\"font-semibold\">{method.label}</span>\n </button>\n );\n })}\n </div> */}\n\n {/* Social Media */}\n {/* <div className=\"flex w-full min-w-0 flex-col gap-1 p-2\">\n <div className=\"mb-1 px-2 text-xs font-medium text-gray-500\">\n Social Media\n </div>\n {methods\n .filter((method) =>\n [\"instagram\", \"facebook\", \"tiktok\"].includes(method.id),\n )\n .map((method) => {\n const Icon = method.icon;\n return (\n <button\n key={method.id}\n onClick={() => onMethodChange(method.id)}\n className={`flex h-8 w-full cursor-pointer items-center gap-2 rounded-md p-2 text-left text-sm transition-colors hover:bg-blue-50 ${\n activeMethod === method.id\n ? \"bg-blue-50 font-medium text-blue-600\"\n : \"text-gray-700\"\n }`}\n >\n <Icon className=\"h-4 w-4 text-gray-500\" />\n <span className=\"font-semibold\">{method.label}</span>\n </button>\n );\n })}\n </div> */}\n </div>\n </div>\n );\n};\n","import React from \"react\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@fluid-app/ui-primitives\";\nimport {\n DAM_TYPE_FILTER_OPTIONS,\n FILTER_VALUE_ALL,\n} from \"@fluid-app/file-picker-core\";\n\nexport interface FilePickerFiltersState {\n type: string;\n date: string;\n uploadedBy: string;\n usageStatus: string;\n source: string;\n}\n\nconst defaultFilters: FilePickerFiltersState = {\n type: \"\",\n date: \"\",\n uploadedBy: \"\",\n usageStatus: \"\",\n source: \"\",\n};\n\ninterface FilePickerFiltersPanelProps {\n filters: FilePickerFiltersState;\n onFiltersChange: (filters: FilePickerFiltersState) => void;\n onClear: () => void;\n onDone: () => void;\n}\n\nexport const FilePickerFiltersPanel: React.FC<FilePickerFiltersPanelProps> = ({\n filters,\n onFiltersChange,\n onClear,\n onDone,\n}) => {\n const updateFilter = <K extends keyof FilePickerFiltersState>(\n key: K,\n value: FilePickerFiltersState[K],\n ) => {\n onFiltersChange({ ...filters, [key]: value });\n };\n\n const hasActiveFilters =\n filters.type !== \"\" ||\n filters.date !== \"\" ||\n filters.uploadedBy !== \"\" ||\n filters.usageStatus !== \"\" ||\n filters.source !== \"\";\n\n return (\n <div className=\"flex w-80 flex-col gap-4 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium text-gray-900\">Filters</span>\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={onClear}\n className=\"text-sm text-blue-600 hover:underline\"\n >\n Clear\n </button>\n )}\n </div>\n\n <div className=\"grid gap-4\">\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">Type</label>\n <Select\n value={filters.type || FILTER_VALUE_ALL}\n onValueChange={(value) =>\n updateFilter(\"type\", value === FILTER_VALUE_ALL ? \"\" : value)\n }\n >\n <SelectTrigger className=\"z-[350]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent className=\"z-[350]\">\n {DAM_TYPE_FILTER_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Commented out until we have the date, source, uploaded by, and usage status filters implemented */}\n {/* <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">Date</label>\n ...\n </div>\n\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">\n Uploaded by\n </label>\n ...\n </div>\n\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">\n Usage status\n </label>\n ...\n </div>\n\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">Source</label>\n ...\n </div> */}\n </div>\n\n <div className=\"flex justify-end gap-2 border-t border-gray-100 pt-4\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onDone}>\n Done\n </Button>\n </div>\n </div>\n );\n};\n\nFilePickerFiltersPanel.displayName = \"FilePickerFiltersPanel\";\n\nexport { defaultFilters };\n","import React, { useState } from \"react\";\nimport {\n XIcon,\n MinusIcon,\n PlusIcon,\n ChevronDownIcon,\n FolderIcon,\n FolderPlusIcon,\n TypeIcon,\n LayoutGridIcon,\n ListIcon,\n SlidersHorizontalIcon,\n ArrowUpDownIcon,\n CheckIcon,\n} from \"lucide-react\";\nimport {\n Button,\n Input,\n Slider,\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n Popover,\n PopoverContent,\n PopoverTrigger,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { MobileBottomSheet } from \"@fluid-app/ui-components/components/MobileBottomSheet\";\n\nimport { IMPLEMENTED_PICKER_METHODS } from \"@fluid-app/file-picker-core\";\nimport type { PickerMethod } from \"@fluid-app/file-picker-core\";\nimport {\n FilePickerFiltersPanel,\n defaultFilters as defaultFiltersState,\n} from \"./FilePickerFiltersPanel\";\nimport type { FilePickerFiltersState } from \"./FilePickerFiltersPanel\";\nimport { SORT_OPTIONS } from \"@fluid-app/file-picker-core\";\nimport type { SortOptionId } from \"@fluid-app/file-picker-core\";\nimport { useIsMobile } from \"../hooks/use-is-mobile\";\n\nexport type ViewMode = \"grid\" | \"list\";\n\ninterface FilePickerHeaderProps {\n currentStep: \"select\" | \"details\";\n activeMethod: PickerMethod;\n methods: Array<{\n id: PickerMethod;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n description: string;\n }>;\n onClose: () => void;\n /**\n * Switches the active method. Required on mobile, where the header title is\n * the only method switcher — the sidebar that owns it is desktop-only.\n */\n onMethodChange?: (method: PickerMethod) => void;\n damSearchQuery?: string;\n onDamSearchChange?: (value: string) => void;\n damSearching?: boolean;\n thumbnailSize?: number;\n onThumbnailSizeChange?: (size: number) => void;\n viewMode?: ViewMode;\n onViewModeChange?: (mode: ViewMode) => void;\n showNamesOnMedia?: boolean;\n onShowNamesOnMediaChange?: (value: boolean) => void;\n hasSelectedFiles?: boolean;\n damFolders?: string[];\n onAddToNewFolder?: () => void;\n onAddToExistingFolder?: (folderPath: string) => void;\n filters?: FilePickerFiltersState;\n onFiltersChange?: (filters: FilePickerFiltersState) => void;\n onFiltersClear?: () => void;\n sortOption?: SortOptionId;\n onSortChange?: (optionId: SortOptionId) => void;\n unsplashSearchQuery?: string;\n onUnsplashSearchChange?: (value: string) => void;\n unsplashSearching?: boolean;\n isUnsplashUploading?: boolean;\n}\n\nexport const FilePickerHeader: React.FC<FilePickerHeaderProps> = ({\n currentStep,\n activeMethod,\n methods,\n onClose,\n onMethodChange,\n damSearchQuery = \"\",\n onDamSearchChange,\n damSearching = false,\n thumbnailSize = 100,\n onThumbnailSizeChange,\n viewMode = \"grid\",\n onViewModeChange,\n showNamesOnMedia = true,\n onShowNamesOnMediaChange,\n hasSelectedFiles = false,\n damFolders = [],\n onAddToNewFolder,\n onAddToExistingFolder,\n filters,\n onFiltersChange,\n onFiltersClear,\n sortOption,\n onSortChange,\n unsplashSearchQuery = \"\",\n onUnsplashSearchChange,\n unsplashSearching = false,\n isUnsplashUploading = false,\n}) => {\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [folderActionsOpen, setFolderActionsOpen] = useState(false);\n const [sortOpen, setSortOpen] = useState(false);\n const [methodsOpen, setMethodsOpen] = useState(false);\n const isMobile = useIsMobile();\n const showMediaToolbar =\n currentStep === \"select\" &&\n (activeMethod === \"dam\" || activeMethod === \"media\");\n const showUnsplashToolbar =\n currentStep === \"select\" && activeMethod === \"unsplash\";\n const title =\n currentStep === \"select\"\n ? methods.find((method) => method.id === activeMethod)?.label ||\n \"Select Files\"\n : \"Media Details\";\n // The sidebar that switches methods is desktop-only, so below `md` the title\n // doubles as the switcher — otherwise the picker is stuck on whichever tab it\n // opened on and \"Upload from computer\" (the device's camera/photo picker) is\n // unreachable.\n // Same filter the sidebar applies: the cloud-storage and social methods\n // still render a placeholder, so offering them is a dead end.\n const switchableMethods = methods.filter((method) =>\n IMPLEMENTED_PICKER_METHODS.includes(method.id),\n );\n const showMethodSwitcher =\n isMobile &&\n currentStep === \"select\" &&\n onMethodChange !== undefined &&\n switchableMethods.length > 1;\n\n return (\n <div className=\"sticky top-0 z-20 flex w-full flex-wrap items-center justify-between gap-3 border-b border-gray-100 bg-white px-6 py-3\">\n <div className=\"flex min-w-0 flex-1 items-center gap-4\">\n {showMethodSwitcher ? (\n <>\n <Button\n variant=\"ghost\"\n type=\"button\"\n // Named so tests can assert the switcher itself rather than its\n // label; the header already holds another `aria-haspopup=\"dialog\"`\n // trigger, and the label changes with the active method.\n data-testid=\"file-picker-method-switcher\"\n aria-haspopup=\"dialog\"\n aria-expanded={methodsOpen}\n onClick={() => setMethodsOpen(true)}\n className=\"h-8 shrink-0 gap-1 px-2 text-lg font-semibold text-gray-900 hover:bg-gray-100\"\n >\n {title}\n <ChevronDownIcon className=\"h-4 w-4 text-gray-500\" />\n </Button>\n <MobileActionSheet\n open={methodsOpen}\n onOpenChange={setMethodsOpen}\n title=\"Choose source\"\n actions={switchableMethods.map((method) => ({\n id: method.id,\n label: method.label,\n selected: method.id === activeMethod,\n onSelect: () => onMethodChange?.(method.id),\n }))}\n />\n </>\n ) : (\n <span className=\"shrink-0 text-lg font-semibold text-gray-900\">\n {title}\n </span>\n )}\n\n {showMediaToolbar && (\n <div className=\"relative max-w-md min-w-0 flex-1\">\n <Input\n type=\"text\"\n placeholder=\"Search assets...\"\n value={damSearchQuery}\n onChange={(e) => onDamSearchChange?.(e.target.value)}\n className=\"h-9\"\n />\n {damSearching && (\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-blue-600 border-t-transparent\" />\n </div>\n )}\n </div>\n )}\n\n {showUnsplashToolbar && (\n <div className=\"flex min-w-0 flex-1 justify-center\">\n <div className=\"relative w-full max-w-md\">\n <Input\n type=\"text\"\n placeholder=\"Search for images...\"\n value={unsplashSearchQuery}\n onChange={(e) => onUnsplashSearchChange?.(e.target.value)}\n disabled={isUnsplashUploading}\n className=\"h-9\"\n />\n {unsplashSearching && (\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-blue-600 border-t-transparent\" />\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n\n {/* DAM / Media Toolbar Controls */}\n {showMediaToolbar && (\n <div className=\"flex items-center gap-3\">\n {/* Zoom Slider (disabled in list view) */}\n <div\n className={\n viewMode === \"list\"\n ? \"pointer-events-none flex items-center gap-1.5 opacity-50\"\n : \"flex items-center gap-1.5\"\n }\n >\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() => {\n const next = Math.max(25, thumbnailSize - 25);\n onThumbnailSizeChange?.(next);\n }}\n disabled={thumbnailSize <= 25 || viewMode === \"list\"}\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40 disabled:hover:bg-transparent\"\n >\n <MinusIcon className=\"h-3 w-3\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom out</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <Slider\n value={[thumbnailSize]}\n onValueChange={(value) => {\n if (value[0] !== undefined) {\n onThumbnailSizeChange?.(value[0]);\n }\n }}\n min={25}\n max={150}\n step={25}\n className=\"w-20\"\n disabled={viewMode === \"list\"}\n />\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() => {\n const next = Math.min(150, thumbnailSize + 25);\n onThumbnailSizeChange?.(next);\n }}\n disabled={thumbnailSize >= 150 || viewMode === \"list\"}\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40 disabled:hover:bg-transparent\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom in</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n\n <div className=\"h-5 w-px bg-gray-200\" />\n\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onShowNamesOnMediaChange?.(!showNamesOnMedia)}\n disabled={viewMode === \"list\"}\n className={`flex h-8 w-8 items-center justify-center rounded-md bg-transparent transition-colors ${\n viewMode === \"list\"\n ? \"cursor-not-allowed opacity-50\"\n : showNamesOnMedia\n ? \"bg-blue-50 text-blue-600\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <TypeIcon className=\"h-4 w-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {viewMode === \"list\"\n ? \"Not available in list view\"\n : showNamesOnMedia\n ? \"Hide names on media\"\n : \"Show names on media\"}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n\n <div className=\"h-5 w-px bg-gray-200\" />\n\n {/* View Mode Toggles - Grid and List */}\n <div className=\"flex items-center gap-1 rounded-md border border-gray-200 bg-gray-50 p-0.5\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onViewModeChange?.(\"grid\")}\n className={`flex h-7 w-7 items-center justify-center rounded bg-transparent transition-colors ${\n viewMode === \"grid\"\n ? \"bg-white text-blue-600 shadow-sm\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <LayoutGridIcon className=\"h-3.5 w-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Grid View</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onViewModeChange?.(\"list\")}\n className={`flex h-7 w-7 items-center justify-center rounded bg-transparent transition-colors ${\n viewMode === \"list\"\n ? \"bg-white text-blue-600 shadow-sm\"\n : \"text-gray-500 hover:text-gray-700\"\n }`}\n >\n <ListIcon className=\"h-3.5 w-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>List View</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n\n <div className=\"h-5 w-px bg-gray-200\" />\n\n {activeMethod === \"dam\" && (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex\">\n {isMobile ? (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n disabled={!hasSelectedFiles}\n className=\"flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40\"\n onClick={() => setFolderActionsOpen(true)}\n >\n <FolderPlusIcon className=\"h-4 w-4\" />\n </Button>\n <MobileActionSheet\n open={folderActionsOpen}\n onOpenChange={setFolderActionsOpen}\n title=\"Add to folder\"\n actions={[\n {\n id: \"new\",\n label: \"Add to New Folder\",\n onSelect: () => onAddToNewFolder?.(),\n },\n ...damFolders.map((folder) => ({\n id: folder,\n label: folder.split(\".\").pop() || folder,\n onSelect: () => onAddToExistingFolder?.(folder),\n })),\n ]}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n disabled={!hasSelectedFiles}\n className=\"flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40\"\n >\n <FolderPlusIcon className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-350 min-w-[200px]\">\n <DropdownMenuItem\n onClick={() => onAddToNewFolder?.()}\n className=\"cursor-pointer\"\n >\n <FolderPlusIcon className=\"mr-2 h-4 w-4\" />\n Add to New Folder\n </DropdownMenuItem>\n {damFolders.length > 0 && (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className=\"cursor-pointer\">\n <FolderPlusIcon className=\"mr-2 h-4 w-4\" />\n Add to Existing Folder\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"z-350 max-h-64 overflow-y-auto\">\n {damFolders.map((folder) => (\n <DropdownMenuItem\n key={folder}\n onClick={() =>\n onAddToExistingFolder?.(folder)\n }\n className=\"cursor-pointer\"\n >\n <FolderIcon className=\"mr-2 h-4 w-4\" />\n {folder.split(\".\").pop() || folder}\n </DropdownMenuItem>\n ))}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </span>\n </TooltipTrigger>\n <TooltipContent>\n {hasSelectedFiles\n ? \"Add selected files to a folder\"\n : \"Select files to add to a folder\"}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n\n {activeMethod === \"dam\" && <div className=\"h-5 w-px bg-gray-200\" />}\n\n {/* Filters */}\n {isMobile ? (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n aria-label=\"Filters\"\n onClick={() => setFiltersOpen(true)}\n className={`flex h-8 w-8 items-center justify-center rounded-md bg-transparent transition-colors ${\n filters &&\n (filters.type ||\n filters.date ||\n filters.uploadedBy ||\n filters.usageStatus ||\n filters.source)\n ? \"bg-blue-50 text-blue-600\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <SlidersHorizontalIcon className=\"h-4 w-4\" />\n </Button>\n <MobileBottomSheet\n open={filtersOpen}\n onOpenChange={setFiltersOpen}\n title=\"Filters\"\n >\n <FilePickerFiltersPanel\n filters={filters ?? defaultFiltersState}\n onFiltersChange={(nextFilters) =>\n onFiltersChange?.(nextFilters)\n }\n onClear={() => {\n onFiltersClear?.();\n setFiltersOpen(false);\n }}\n onDone={() => setFiltersOpen(false)}\n />\n </MobileBottomSheet>\n </>\n ) : (\n <Popover open={filtersOpen} onOpenChange={setFiltersOpen}>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n className={`flex h-8 w-8 items-center justify-center rounded-md bg-transparent transition-colors ${\n filters &&\n (filters.type ||\n filters.date ||\n filters.uploadedBy ||\n filters.usageStatus ||\n filters.source)\n ? \"bg-blue-50 text-blue-600\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <SlidersHorizontalIcon className=\"h-4 w-4\" />\n </Button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>Filters</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <PopoverContent align=\"end\" className=\"w-auto p-0\">\n <FilePickerFiltersPanel\n filters={filters ?? defaultFiltersState}\n onFiltersChange={(f) => onFiltersChange?.(f)}\n onClear={() => {\n onFiltersClear?.();\n setFiltersOpen(false);\n }}\n onDone={() => setFiltersOpen(false)}\n />\n </PopoverContent>\n </Popover>\n )}\n\n {/* Sort */}\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex\">\n {isMobile ? (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label=\"Sort\"\n onClick={() => setSortOpen(true)}\n className=\"flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700\"\n >\n <ArrowUpDownIcon className=\"h-4 w-4\" />\n </Button>\n <MobileActionSheet\n open={sortOpen}\n onOpenChange={setSortOpen}\n title=\"Sort\"\n actions={SORT_OPTIONS.map((option) => ({\n id: option.id,\n label: option.label,\n onSelect: () => onSortChange?.(option.id),\n }))}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700\"\n >\n <ArrowUpDownIcon className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-350 min-w-[180px]\">\n {SORT_OPTIONS.map((option) => (\n <DropdownMenuItem\n key={option.id}\n onClick={() => onSortChange?.(option.id)}\n >\n <span\n className={\n sortOption === option.id\n ? \"font-medium\"\n : undefined\n }\n >\n {option.label}\n </span>\n {sortOption === option.id && (\n <CheckIcon className=\"ml-auto h-4 w-4 text-blue-600\" />\n )}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </span>\n </TooltipTrigger>\n <TooltipContent>Sort</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n )}\n\n {/* Close button - always visible */}\n <Button variant=\"ghost\" size=\"sm\" onClick={onClose} className=\"ml-2\">\n <XIcon className=\"h-4 w-4\" />\n </Button>\n </div>\n );\n};\n","import React, { useState, useCallback, useEffect, useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n ImageIcon,\n VideoIcon,\n FileIcon,\n LoaderIcon,\n SearchIcon,\n ChevronDownIcon,\n} from \"lucide-react\";\nimport {\n Button,\n Input,\n Dialog,\n DialogOverlay,\n DialogPortal,\n DialogContent,\n} from \"@fluid-app/ui-primitives\";\nimport type {\n FilePickerResult,\n SortOptionId,\n} from \"@fluid-app/file-picker-core\";\nimport { SORT_OPTIONS } from \"@fluid-app/file-picker-core\";\nimport {\n useFilePickerContext,\n useFilePickerSelection,\n} from \"../context/FilePickerContext\";\nimport {\n LibraryAssetGrid,\n type LibraryAssetItem,\n type LibraryAssetViewMode,\n} from \"./LibraryAssetGrid\";\nimport type { MediumResponse } from \"../types/media\";\n\ninterface MediaTabProps {\n config: {\n maxFiles?: number;\n fileTypeFilter?: string[];\n };\n onMediaSelected: (results: FilePickerResult[]) => void;\n onSelectionChange?: (count: number) => void;\n onMediaDataChange?: (data: FilePickerResult[]) => void;\n selectedMediaIds: number[];\n onMediaIdsChange: (ids: number[]) => void;\n searchQuery?: string;\n onSearchChange?: (value: string) => void;\n thumbnailSize?: number;\n viewMode?: LibraryAssetViewMode;\n showNamesOnMedia?: boolean;\n mediaTypeFilter?: string;\n sortOption?: SortOptionId;\n previewContainer?: HTMLDivElement | null;\n previewMedia?: MediumResponse | null;\n onPreviewMediaChange?: (media: MediumResponse | null) => void;\n}\n\nconst DAM_CATEGORY_TO_MEDIA_TYPE: Record<string, string> = {\n images: \"image\",\n videos: \"video\",\n documents: \"document\",\n audio: \"audio\",\n text: \"document\",\n other: \"image\",\n presentations: \"presentation\",\n};\n\nexport const MediaTab: React.FC<MediaTabProps> = ({\n config,\n onSelectionChange,\n onMediaDataChange,\n selectedMediaIds,\n onMediaIdsChange,\n searchQuery: controlledSearchQuery,\n onSearchChange: controlledSetSearchQuery,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n mediaTypeFilter: headerTypeFilter,\n sortOption,\n previewContainer,\n previewMedia,\n onPreviewMediaChange,\n}) => {\n const { shareablesClient } = useFilePickerContext();\n const mediaSourceCacheKey = shareablesClient?.media.cacheKey ?? \"default\";\n\n const [internalSearchQuery, setInternalSearchQuery] = useState(\"\");\n const isSearchControlled =\n controlledSearchQuery !== undefined && controlledSetSearchQuery != null;\n const searchQuery = isSearchControlled\n ? controlledSearchQuery\n : internalSearchQuery;\n const setSearchQuery = isSearchControlled\n ? controlledSetSearchQuery\n : setInternalSearchQuery;\n const [allMediaItems, setAllMediaItems] = useState<MediumResponse[]>([]);\n const [currentPage, setCurrentPage] = useState(1);\n const [loadingMore, setLoadingMore] = useState(false);\n const [hasNextPage, setHasNextPage] = useState(false);\n\n const mediaTypeFilter = useMemo(() => {\n if (headerTypeFilter && DAM_CATEGORY_TO_MEDIA_TYPE[headerTypeFilter]) {\n return DAM_CATEGORY_TO_MEDIA_TYPE[headerTypeFilter];\n }\n if (!config.fileTypeFilter || config.fileTypeFilter.length === 0) {\n return undefined;\n }\n for (const filter of config.fileTypeFilter) {\n if (DAM_CATEGORY_TO_MEDIA_TYPE[filter]) {\n return DAM_CATEGORY_TO_MEDIA_TYPE[filter];\n }\n }\n return undefined;\n }, [headerTypeFilter, config.fileTypeFilter]);\n\n // Fetch all media (same as media index table)\n const {\n data: mediaResponse,\n isLoading,\n error,\n } = useQuery({\n queryKey: [\"media-tab\", searchQuery, mediaTypeFilter, mediaSourceCacheKey],\n queryFn: () => {\n if (!shareablesClient) {\n throw new Error(\"Shareables client is not configured\");\n }\n return shareablesClient.media.list({\n search_query: searchQuery,\n page: 1,\n per_page: 30,\n active: true,\n with_type: mediaTypeFilter,\n }) as Promise<{\n media: MediumResponse[];\n meta?: { next: string | null };\n }>;\n },\n enabled: !!shareablesClient,\n staleTime: 5 * 60 * 1000, // 5 minutes\n });\n\n // Reset pagination when the cache identity, search, or media type filter changes\n useEffect(() => {\n setAllMediaItems([]);\n setCurrentPage(1);\n setHasNextPage(false);\n }, [mediaSourceCacheKey, searchQuery, mediaTypeFilter]);\n\n const requestIdentityRef = React.useRef(\n `${mediaSourceCacheKey}|${searchQuery}|${mediaTypeFilter ?? \"\"}`,\n );\n useEffect(() => {\n requestIdentityRef.current = `${mediaSourceCacheKey}|${searchQuery}|${mediaTypeFilter ?? \"\"}`;\n }, [mediaSourceCacheKey, searchQuery, mediaTypeFilter]);\n\n // Update accumulated media items when new data comes in\n useEffect(() => {\n if (!mediaResponse) return;\n\n setHasNextPage(mediaResponse.meta?.next != null);\n\n const newMediaItems = (mediaResponse.media || []) as MediumResponse[];\n if (newMediaItems.length > 0) {\n setAllMediaItems((prev) => {\n // Merge without duplicates by id\n const map = new Map(prev.map((item) => [item.id, item]));\n for (const item of newMediaItems) {\n map.set(item.id, item);\n }\n return Array.from(map.values());\n });\n }\n }, [mediaResponse]);\n\n // Load more media items\n const loadMore = useCallback(async () => {\n if (!hasNextPage || !shareablesClient) return;\n\n try {\n setLoadingMore(true);\n const nextPage = currentPage + 1;\n const requestIdentity = `${mediaSourceCacheKey}|${searchQuery}|${mediaTypeFilter ?? \"\"}`;\n const response = (await shareablesClient.media.list({\n search_query: searchQuery,\n page: nextPage,\n per_page: 30,\n active: true,\n with_type: mediaTypeFilter,\n })) as { media: MediumResponse[]; meta?: { next: string | null } };\n\n if (requestIdentity !== requestIdentityRef.current) return;\n\n setHasNextPage(response.meta?.next != null);\n setCurrentPage(nextPage);\n\n const newMediaItems = (response.media || []) as MediumResponse[];\n if (newMediaItems.length > 0) {\n setAllMediaItems((prev) => {\n // Merge without duplicates by id\n const map = new Map(prev.map((item) => [item.id, item]));\n for (const item of newMediaItems) {\n map.set(item.id, item);\n }\n return Array.from(map.values());\n });\n }\n } finally {\n setLoadingMore(false);\n }\n }, [\n hasNextPage,\n currentPage,\n searchQuery,\n mediaTypeFilter,\n shareablesClient,\n mediaSourceCacheKey,\n ]);\n\n // Convert MediumResponse to FilePickerResult\n const convertToFilePickerResult = useCallback(\n (media: MediumResponse): FilePickerResult => {\n // Determine the file URL based on media type\n let fileUrl = \"\";\n let mimeType = \"\";\n let fileName = media.title;\n let thumbnailUrl = \"\";\n let description = \"\";\n\n // Almost all media have image_url that stores the url for thumbnail/preview\n // so, we'll check the presence of image_url at the end of the logic\n if (media.video_url) {\n fileUrl = media.video_url;\n mimeType = \"video/mp4\"; // Default, could be improved with actual detection\n fileName = `${media.title}.mp4`;\n thumbnailUrl = media.image_url || \"\";\n description = media.description?.body || \"\";\n } else if (media.pdf_url) {\n fileUrl = media.pdf_url;\n mimeType = \"application/pdf\";\n fileName = `${media.title}.pdf`;\n } else if (media.image_url) {\n fileUrl = media.image_url;\n mimeType = \"image/jpeg\"; // Default, could be improved with actual detection\n fileName = `${media.title}.jpg`;\n }\n\n return {\n asset_id: media.id,\n asset_code: `media_${media.id}`,\n file_path: fileUrl,\n file_url: fileUrl,\n ...(thumbnailUrl ? { thumbnail_url: thumbnailUrl } : {}),\n ...(description ? { description: description } : {}),\n metadata: {\n file_name: fileName,\n mime_type: mimeType,\n },\n upload_status: \"ready\" as const,\n };\n },\n [],\n );\n\n // Unified cross-tab selection — when the active picker has unifiedSelection\n // enabled, MediaTab writes directly into the cross-tab selection list and\n // skips the per-method `selectedMediaIds` channel.\n const { state: unifiedState, api: unifiedApi } = useFilePickerSelection();\n const isUnified = unifiedState !== null && unifiedApi !== null;\n\n const handleMediaClick = useCallback(\n (_e: React.MouseEvent, id: string, _index: number) => {\n const mediaId = parseInt(id.replace(/^media_/, \"\"), 10);\n if (Number.isNaN(mediaId)) return;\n\n if (isUnified) {\n const media = allMediaItems.find((m) => m.id === mediaId);\n if (!media) return;\n const result = convertToFilePickerResult(media);\n if (unifiedApi!.isSelected(result.asset_code)) {\n unifiedApi!.remove(result.asset_code);\n } else {\n unifiedApi!.add(result);\n }\n return;\n }\n\n const isSelected = selectedMediaIds.includes(mediaId);\n let newSelectedIds: number[];\n\n if (isSelected) {\n newSelectedIds = selectedMediaIds.filter((id) => id !== mediaId);\n } else {\n if (config.maxFiles === 1) {\n newSelectedIds = [mediaId];\n } else if (\n config.maxFiles &&\n selectedMediaIds.length >= config.maxFiles\n ) {\n return;\n } else {\n newSelectedIds = [...selectedMediaIds, mediaId];\n }\n }\n\n onMediaIdsChange(newSelectedIds);\n\n const idToMedia = new Map(allMediaItems.map((m) => [m.id, m]));\n const orderedMedia = newSelectedIds\n .map((id) => idToMedia.get(id))\n .filter((m): m is MediumResponse => m != null);\n const filePickerResults = orderedMedia.map(convertToFilePickerResult);\n onMediaDataChange?.(filePickerResults);\n },\n [\n isUnified,\n unifiedApi,\n selectedMediaIds,\n config.maxFiles,\n allMediaItems,\n convertToFilePickerResult,\n onMediaIdsChange,\n onMediaDataChange,\n ],\n );\n\n // Update selection count when selectedMediaIds changes (per-method only —\n // unified mode reads its count from the cross-tab context).\n React.useEffect(() => {\n if (!isUnified) {\n onSelectionChange?.(selectedMediaIds.length);\n }\n }, [isUnified, selectedMediaIds.length, onSelectionChange]);\n\n const sortedMediaItems = useMemo(() => {\n if (!sortOption || !allMediaItems.length) return allMediaItems;\n const option = SORT_OPTIONS.find((o) => o.id === sortOption);\n if (!option) return allMediaItems;\n return [...allMediaItems].sort((a, b) => {\n if (option.sortBy === \"name\") {\n const cmp = (a.title ?? \"\").localeCompare(b.title ?? \"\", undefined, {\n sensitivity: \"base\",\n });\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n if (option.sortBy === \"created_at\") {\n const aId = a.id ?? 0;\n const bId = b.id ?? 0;\n const cmp = aId - bId;\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n return 0;\n });\n }, [allMediaItems, sortOption]);\n\n const getMediaIcon = (media: MediumResponse) => {\n if (media.image_url) return ImageIcon;\n if (media.video_url) return VideoIcon;\n if (media.pdf_url) return FileIcon;\n return FileIcon;\n };\n\n const renderMediaThumbnail = useCallback(\n (media: MediumResponse, _layout: LibraryAssetViewMode) => {\n if (media.image_url) {\n return (\n <img\n src={media.image_url}\n alt={media.title}\n width={200}\n height={150}\n className=\"h-full w-full object-cover\"\n />\n );\n }\n const MediaIcon = getMediaIcon(media);\n return (\n <div className=\"flex h-full w-full items-center justify-center bg-gray-100\">\n <MediaIcon className=\"h-8 w-8 text-gray-400\" />\n </div>\n );\n },\n [],\n );\n\n const libraryItems = useMemo((): LibraryAssetItem<MediumResponse>[] => {\n return sortedMediaItems.map((media) => {\n const id = `media_${media.id}`;\n let isSelected: boolean;\n let selectionIndex: number;\n if (isUnified) {\n const idx = unifiedState!.items.findIndex((r) => r.asset_code === id);\n isSelected = idx >= 0;\n selectionIndex = isSelected ? idx + 1 : 0;\n } else {\n const indexInSelection = selectedMediaIds.indexOf(media.id);\n isSelected = indexInSelection >= 0;\n selectionIndex = isSelected ? indexInSelection + 1 : 0;\n }\n return {\n id,\n name: media.title ?? \"\",\n subtitle: media.kind ?? \"Media\",\n isSelected,\n selectionIndex,\n raw: media,\n };\n });\n }, [sortedMediaItems, selectedMediaIds, isUnified, unifiedState]);\n\n const handleMediaDoubleClick = useCallback(\n (_e: React.MouseEvent, _id: string, index: number) => {\n const media = sortedMediaItems[index];\n if (media) onPreviewMediaChange?.(media);\n },\n [sortedMediaItems, onPreviewMediaChange],\n );\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"text-center\">\n <p className=\"text-sm text-red-600\">Failed to load media</p>\n <p className=\"mt-1 text-xs text-gray-500\">Please try again later</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n <Dialog\n open={!!previewMedia}\n onOpenChange={(open) => !open && onPreviewMediaChange?.(null)}\n >\n <DialogPortal container={previewContainer ?? undefined}>\n <DialogOverlay className=\"z-9999 bg-black/70 backdrop-blur-none\" />\n <DialogContent\n className=\"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-10000 max-h-[85vh] w-full max-w-4xl -translate-x-1/2 -translate-y-1/2 overflow-hidden bg-white shadow-xl\"\n style={{ zIndex: 10000 }}\n >\n {previewMedia && (\n <div className=\"flex max-h-[85vh] flex-col\">\n {previewMedia.image_url ? (\n <div className=\"relative flex min-h-0 justify-center overflow-hidden bg-gray-100\">\n <img\n src={previewMedia.image_url}\n alt={previewMedia.title}\n className=\"max-h-[85vh] w-auto max-w-full object-contain\"\n />\n </div>\n ) : previewMedia.video_url ? (\n <div className=\"relative aspect-video max-h-[85vh] overflow-hidden bg-black\">\n <video\n src={previewMedia.video_url}\n autoPlay\n muted\n controls\n className=\"h-full w-full object-contain\"\n />\n </div>\n ) : (\n <div className=\"flex aspect-video min-h-[200px] items-center justify-center bg-gray-100 p-8\">\n {(() => {\n const PreviewIcon = getMediaIcon(previewMedia);\n return (\n <PreviewIcon className=\"h-16 w-16 text-gray-400\" />\n );\n })()}\n <p className=\"ml-4 text-sm text-gray-600\">\n {previewMedia.title}\n </p>\n </div>\n )}\n </div>\n )}\n </DialogContent>\n </DialogPortal>\n </Dialog>\n\n {/* Search (only when not controlled from header) */}\n {!isSearchControlled && (\n <div className=\"relative\">\n <Input\n type=\"text\"\n placeholder=\"Search media...\"\n value={searchQuery}\n onChange={(e) => {\n setSearchQuery(e.target.value);\n }}\n className=\"h-9\"\n />\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <SearchIcon className=\"h-4 w-4 text-gray-400\" />\n </div>\n </div>\n )}\n\n {/* Media Grid */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <LoaderIcon className=\"mx-auto mb-2 h-8 w-8 animate-spin text-blue-600\" />\n <p className=\"text-sm text-gray-500\">Loading media...</p>\n </div>\n </div>\n ) : allMediaItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"mb-4 rounded-full bg-gray-100 p-3\">\n <ImageIcon className=\"h-6 w-6 text-gray-400\" />\n </div>\n <h3 className=\"mb-1 text-sm font-medium text-gray-900\">\n No media found\n </h3>\n <p className=\"text-xs text-gray-500\">\n {searchQuery\n ? \"Try adjusting your search terms\"\n : \"Create some media to get started\"}\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <LibraryAssetGrid<MediumResponse>\n items={libraryItems}\n viewMode={viewMode ?? \"grid\"}\n thumbnailSize={thumbnailSize}\n showNamesOnMedia={showNamesOnMedia ?? true}\n renderThumbnail={renderMediaThumbnail}\n onClick={handleMediaClick}\n onDoubleClick={handleMediaDoubleClick}\n />\n\n {/* Load More Button */}\n {hasNextPage && (\n <div className=\"flex justify-center\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={loadMore}\n disabled={loadingMore}\n >\n {loadingMore ? (\n <>\n <LoaderIcon className=\"mr-2 h-3 w-3 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <ChevronDownIcon className=\"mr-2 h-3 w-3\" />\n Load more\n </>\n )}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\nexport default MediaTab;\n","\"use client\";\n\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { ChevronDownIcon, LoaderIcon, SearchIcon } from \"lucide-react\";\nimport { Button, Input } from \"@fluid-app/ui-primitives\";\nimport type {\n FilePickerResult,\n PickerSource,\n} from \"@fluid-app/file-picker-core\";\nimport {\n LibraryAssetGrid,\n type LibraryAssetItem,\n type LibraryAssetViewMode,\n} from \"./LibraryAssetGrid\";\nimport {\n useFilePickerContext,\n useFilePickerSelection,\n} from \"../context/FilePickerContext\";\n\ninterface ContentBrowserTabProps<T> {\n /** Stable id for query keys; one of \"pages\" | \"products\" | \"enrollment_packs\". */\n contentKind: string;\n /** Asset-code prefix used in FilePickerResult, e.g. \"page_\". */\n assetCodePrefix: string;\n /** Plural label used in the empty/search placeholder copy. */\n labelPlural: string;\n /** Host-supplied data source. */\n source: PickerSource<T>;\n /** Extracts the numeric ID from a record. */\n getId: (item: T) => number;\n /** Extracts the human label (page title, product name, …). */\n getLabel: (item: T) => string;\n /** Optional thumbnail URL — falls back to icon when null/empty. */\n getImageUrl: (item: T) => string | null | undefined;\n /** Optional subtitle line for the grid card (e.g. price). */\n getSubtitle?: (item: T) => string | null | undefined;\n /** Icon shown when an item has no image. */\n fallbackIcon: React.ComponentType<{ className?: string }>;\n /** Picker config (maxFiles only used in per-method mode). */\n config: { maxFiles?: number };\n /** Per-method (non-unified) selection state. */\n selectedIds: number[];\n onSelectedIdsChange: (ids: number[]) => void;\n /** Per-method selection results — emitted alongside id changes. */\n onSelectedDataChange: (results: FilePickerResult[]) => void;\n onSelectionCountChange?: (count: number) => void;\n thumbnailSize?: number;\n viewMode?: LibraryAssetViewMode;\n showNamesOnMedia?: boolean;\n}\n\nconst PAGE_SIZE = 30;\n\nexport function ContentBrowserTab<T>({\n contentKind,\n assetCodePrefix,\n labelPlural,\n source,\n getId,\n getLabel,\n getImageUrl,\n getSubtitle,\n fallbackIcon: FallbackIcon,\n config,\n selectedIds,\n onSelectedIdsChange,\n onSelectedDataChange,\n onSelectionCountChange,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n}: ContentBrowserTabProps<T>): React.ReactElement {\n // Hide the search input when the source's backing endpoint doesn't\n // support a title/search filter — typing in a non-functional box gives\n // users false feedback. Defaults to enabled to preserve historical\n // behavior for sources that pre-date the flag.\n const supportsSearch = source.supportsSearch !== false;\n const sourceCacheKey = source.cacheKey ?? \"default\";\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const [items, setItems] = useState<T[]>([]);\n const [cursor, setCursor] = useState<string | null>(null);\n const [loadingMore, setLoadingMore] = useState(false);\n\n // Debounce search to avoid hammering the host endpoint while typing\n useEffect(() => {\n const t = setTimeout(() => setDebouncedQuery(searchQuery), 250);\n return () => clearTimeout(t);\n }, [searchQuery]);\n\n const queryKey = useMemo(\n () => [contentKind, \"list\", debouncedQuery, sourceCacheKey],\n [contentKind, debouncedQuery, sourceCacheKey],\n );\n\n const {\n data: firstPage,\n isLoading,\n error,\n } = useQuery({\n queryKey,\n queryFn: () =>\n source.list({ search: debouncedQuery, cursor: null, limit: PAGE_SIZE }),\n staleTime: 5 * 60 * 1000,\n });\n\n // Reset accumulated items/pagination when the source identity or search changes.\n useEffect(() => {\n setItems([]);\n setCursor(null);\n }, [sourceCacheKey, debouncedQuery]);\n\n useEffect(() => {\n if (!firstPage) return;\n setItems(firstPage.items);\n setCursor(firstPage.nextCursor);\n }, [firstPage]);\n\n // Track the latest search query so an in-flight loadMore can detect a\n // race: if the user changes the search while the request is pending, the\n // search-reset useEffect above replaces `items` with the new query's\n // first page. Without this guard the resolved old-query results would be\n // appended on top, mixing pages from two different searches.\n const requestIdentityRef = useRef(`${sourceCacheKey}|${debouncedQuery}`);\n useEffect(() => {\n requestIdentityRef.current = `${sourceCacheKey}|${debouncedQuery}`;\n }, [sourceCacheKey, debouncedQuery]);\n\n const { toast } = useFilePickerContext();\n\n const loadMore = useCallback(async () => {\n if (!cursor) return;\n const requestQuery = debouncedQuery;\n const requestIdentity = `${sourceCacheKey}|${requestQuery}`;\n try {\n setLoadingMore(true);\n const next = await source.list({\n search: requestQuery,\n cursor,\n limit: PAGE_SIZE,\n });\n // Drop the page if the user changed the source/search while we were waiting.\n if (requestIdentity !== requestIdentityRef.current) return;\n setItems((prev) => {\n const ids = new Set(prev.map(getId));\n const merged = [...prev];\n for (const it of next.items) {\n if (!ids.has(getId(it))) merged.push(it);\n }\n return merged;\n });\n setCursor(next.nextCursor);\n } catch (err) {\n // useQuery only catches errors on the first page — surface load-more\n // failures explicitly so the user knows pagination broke instead of\n // silently leaving the cursor in place.\n toast.error(`Failed to load more ${labelPlural}`, err);\n } finally {\n setLoadingMore(false);\n }\n }, [\n cursor,\n debouncedQuery,\n sourceCacheKey,\n source,\n getId,\n labelPlural,\n toast,\n ]);\n\n // Unified vs per-method selection adapter\n const { state: unifiedState, api: unifiedApi } = useFilePickerSelection();\n const isUnified = unifiedState !== null && unifiedApi !== null;\n\n const toResult = useCallback(\n (item: T): FilePickerResult => {\n const id = getId(item);\n const label = getLabel(item);\n const image = getImageUrl(item) ?? \"\";\n return {\n asset_id: id,\n asset_code: `${assetCodePrefix}${id}`,\n file_path: image,\n file_url: image,\n ...(image ? { thumbnail_url: image } : {}),\n metadata: {\n file_name: label,\n mime_type: \"\",\n },\n upload_status: \"ready\" as const,\n };\n },\n [assetCodePrefix, getId, getLabel, getImageUrl],\n );\n\n const isSelected = useCallback(\n (id: number) => {\n if (isUnified) {\n return unifiedState!.items.some(\n (r) => r.asset_code === `${assetCodePrefix}${id}`,\n );\n }\n return selectedIds.includes(id);\n },\n [isUnified, unifiedState, assetCodePrefix, selectedIds],\n );\n\n const selectionIndex = useCallback(\n (id: number): number => {\n if (isUnified) {\n const code = `${assetCodePrefix}${id}`;\n return unifiedState!.items.findIndex((r) => r.asset_code === code) + 1;\n }\n return selectedIds.indexOf(id) + 1;\n },\n [isUnified, unifiedState, assetCodePrefix, selectedIds],\n );\n\n // Toggle handler — branches on unified vs per-method\n const handleClick = useCallback(\n (_e: React.MouseEvent, gridId: string) => {\n // Use a literal string slice rather than a RegExp built from the\n // prefix prop — a future caller passing regex metacharacters in the\n // prefix would otherwise silently match more than intended and parse\n // the wrong numeric ID.\n const numericId = Number.parseInt(\n gridId.startsWith(assetCodePrefix)\n ? gridId.slice(assetCodePrefix.length)\n : gridId,\n 10,\n );\n if (Number.isNaN(numericId)) return;\n const item = items.find((it) => getId(it) === numericId);\n if (!item) return;\n const result = toResult(item);\n\n if (isUnified) {\n if (unifiedApi!.isSelected(result.asset_code)) {\n unifiedApi!.remove(result.asset_code);\n } else {\n unifiedApi!.add(result);\n }\n return;\n }\n\n const already = selectedIds.includes(numericId);\n let nextIds: number[];\n if (already) {\n nextIds = selectedIds.filter((x) => x !== numericId);\n } else if (config.maxFiles === 1) {\n nextIds = [numericId];\n } else if (\n config.maxFiles !== undefined &&\n selectedIds.length >= config.maxFiles\n ) {\n return;\n } else {\n nextIds = [...selectedIds, numericId];\n }\n onSelectedIdsChange(nextIds);\n const idToItem = new Map(items.map((it) => [getId(it), it]));\n const ordered = nextIds\n .map((id) => idToItem.get(id))\n .filter((it): it is T => it != null)\n .map(toResult);\n onSelectedDataChange(ordered);\n },\n [\n items,\n assetCodePrefix,\n isUnified,\n unifiedApi,\n selectedIds,\n config.maxFiles,\n onSelectedIdsChange,\n onSelectedDataChange,\n toResult,\n getId,\n ],\n );\n\n // Keep parent count in sync (per-method mode only — unified panel reads context)\n useEffect(() => {\n if (!isUnified) {\n onSelectionCountChange?.(selectedIds.length);\n }\n }, [isUnified, selectedIds.length, onSelectionCountChange]);\n\n const renderThumbnail = useCallback(\n (item: T) => {\n const url = getImageUrl(item);\n if (url) {\n return (\n <img\n src={url}\n alt={getLabel(item)}\n width={200}\n height={150}\n className=\"h-full w-full object-cover\"\n />\n );\n }\n return (\n <div className=\"flex h-full w-full items-center justify-center bg-gray-100\">\n <FallbackIcon className=\"h-8 w-8 text-gray-400\" />\n </div>\n );\n },\n [FallbackIcon, getImageUrl, getLabel],\n );\n\n const libraryItems = useMemo(\n (): LibraryAssetItem<T>[] =>\n items.map((item) => {\n const id = getId(item);\n const idx = selectionIndex(id);\n return {\n id: `${assetCodePrefix}${id}`,\n name: getLabel(item),\n subtitle: getSubtitle?.(item) ?? undefined,\n isSelected: isSelected(id),\n selectionIndex: idx,\n raw: item,\n };\n }),\n [\n items,\n assetCodePrefix,\n isSelected,\n selectionIndex,\n getId,\n getLabel,\n getSubtitle,\n ],\n );\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <p className=\"text-sm text-red-600\">Failed to load {labelPlural}</p>\n <p className=\"mt-1 text-xs text-gray-500\">Please try again later</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {supportsSearch && (\n <div className=\"relative\">\n <Input\n type=\"text\"\n placeholder={`Search ${labelPlural}...`}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"h-9\"\n />\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <SearchIcon className=\"h-4 w-4 text-gray-400\" />\n </div>\n </div>\n )}\n\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <LoaderIcon className=\"mx-auto mb-2 h-8 w-8 animate-spin text-blue-600\" />\n <p className=\"text-sm text-gray-500\">Loading {labelPlural}...</p>\n </div>\n </div>\n ) : libraryItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"mb-4 rounded-full bg-gray-100 p-3\">\n <FallbackIcon className=\"h-6 w-6 text-gray-400\" />\n </div>\n <h3 className=\"mb-1 text-sm font-medium text-gray-900\">\n No {labelPlural} found\n </h3>\n <p className=\"text-xs text-gray-500\">\n {debouncedQuery\n ? \"Try adjusting your search terms\"\n : `No ${labelPlural} available`}\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <LibraryAssetGrid<T>\n items={libraryItems}\n viewMode={viewMode}\n thumbnailSize={thumbnailSize}\n showNamesOnMedia={showNamesOnMedia}\n renderThumbnail={renderThumbnail}\n onClick={handleClick}\n />\n {cursor && (\n <div className=\"flex justify-center\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={loadMore}\n disabled={loadingMore}\n >\n {loadingMore ? (\n <>\n <LoaderIcon className=\"mr-2 h-3 w-3 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <ChevronDownIcon className=\"mr-2 h-3 w-3\" />\n Load more\n </>\n )}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { FileTextIcon } from \"lucide-react\";\nimport type {\n FilePickerResult,\n PickerPageItem,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { ContentBrowserTab } from \"./ContentBrowserTab\";\nimport type { LibraryAssetViewMode } from \"./LibraryAssetGrid\";\n\nexport interface PagesTabProps {\n config: { maxFiles?: number };\n selectedIds: number[];\n onSelectedIdsChange: (ids: number[]) => void;\n onSelectedDataChange: (results: FilePickerResult[]) => void;\n onSelectionCountChange?: (count: number) => void;\n thumbnailSize?: number;\n viewMode?: LibraryAssetViewMode;\n showNamesOnMedia?: boolean;\n}\n\nexport const PagesTab: React.FC<PagesTabProps> = (props) => {\n const { shareablesClient } = useFilePickerContext();\n const source = shareablesClient?.pages;\n if (!source) {\n // Defensive: FilePicker should not render this tab without a source.\n return null;\n }\n return (\n <ContentBrowserTab<PickerPageItem>\n contentKind=\"pages\"\n assetCodePrefix=\"page_\"\n labelPlural=\"pages\"\n source={source}\n getId={(p) => p.id}\n getLabel={(p) => p.title || `Page #${p.id}`}\n getImageUrl={(p) => p.image_url ?? null}\n fallbackIcon={FileTextIcon}\n {...props}\n />\n );\n};\n","\"use client\";\n\nimport React from \"react\";\nimport { PackageIcon } from \"lucide-react\";\nimport type {\n FilePickerResult,\n PickerProductItem,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { ContentBrowserTab } from \"./ContentBrowserTab\";\nimport type { LibraryAssetViewMode } from \"./LibraryAssetGrid\";\n\nexport interface ProductsTabProps {\n config: { maxFiles?: number };\n selectedIds: number[];\n onSelectedIdsChange: (ids: number[]) => void;\n onSelectedDataChange: (results: FilePickerResult[]) => void;\n onSelectionCountChange?: (count: number) => void;\n thumbnailSize?: number;\n viewMode?: LibraryAssetViewMode;\n showNamesOnMedia?: boolean;\n}\n\nexport const ProductsTab: React.FC<ProductsTabProps> = (props) => {\n const { shareablesClient } = useFilePickerContext();\n const source = shareablesClient?.products;\n if (!source) return null;\n return (\n <ContentBrowserTab<PickerProductItem>\n contentKind=\"products\"\n assetCodePrefix=\"product_\"\n labelPlural=\"products\"\n source={source}\n getId={(p) => p.id}\n getLabel={(p) => p.name || `Product #${p.id}`}\n getImageUrl={(p) => p.image_url ?? null}\n getSubtitle={(p) => (p.price ? `$${p.price}` : undefined)}\n fallbackIcon={PackageIcon}\n {...props}\n />\n );\n};\n","\"use client\";\n\nimport React from \"react\";\nimport { GraduationCapIcon } from \"lucide-react\";\nimport type {\n FilePickerResult,\n PickerEnrollmentPackItem,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { ContentBrowserTab } from \"./ContentBrowserTab\";\nimport type { LibraryAssetViewMode } from \"./LibraryAssetGrid\";\n\nexport interface EnrollmentPacksTabProps {\n config: { maxFiles?: number };\n selectedIds: number[];\n onSelectedIdsChange: (ids: number[]) => void;\n onSelectedDataChange: (results: FilePickerResult[]) => void;\n onSelectionCountChange?: (count: number) => void;\n thumbnailSize?: number;\n viewMode?: LibraryAssetViewMode;\n showNamesOnMedia?: boolean;\n}\n\nexport const EnrollmentPacksTab: React.FC<EnrollmentPacksTabProps> = (\n props,\n) => {\n const { shareablesClient } = useFilePickerContext();\n const source = shareablesClient?.enrollment_packs;\n if (!source) return null;\n return (\n <ContentBrowserTab<PickerEnrollmentPackItem>\n contentKind=\"enrollment_packs\"\n assetCodePrefix=\"enrollment_pack_\"\n labelPlural=\"enrollment packs\"\n source={source}\n getId={(p) => p.id}\n getLabel={(p) => p.title || `Enrollment Pack #${p.id}`}\n getImageUrl={(p) => p.image_url ?? null}\n fallbackIcon={GraduationCapIcon}\n {...props}\n />\n );\n};\n","import { useState, useCallback } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n damQueryKeys,\n getDamAssetUrl,\n getSanitizedAssetName,\n type CreateDamAssetParams,\n type FilePickerResult,\n type UnsplashImage,\n type UploadProgress,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerApi } from \"../context/FilePickerApiContext\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\ninterface UseUnsplashPickerOptions {\n controlledSearchQuery?: string;\n onControlledSearchQueryChange?: (value: string) => void;\n}\n\nexport const useUnsplashPicker = (\n options?: UseUnsplashPickerOptions,\n): {\n searchQuery: string;\n setSearchQuery: (value: string) => void;\n searchResults: UnsplashImage[];\n isSearching: boolean;\n searchImages: (query: string, page?: number) => void;\n downloadAndUpload: (image: UnsplashImage) => Promise<FilePickerResult>;\n uploadProgress: UploadProgress[];\n isUploading: boolean;\n clearProgress: () => void;\n hasMoreResults: boolean;\n loadMore: () => void;\n isLoadingMore: boolean;\n} => {\n const { controlledSearchQuery, onControlledSearchQueryChange } =\n options ?? {};\n const [internalSearchQuery, setInternalSearchQuery] = useState(\"\");\n const searchQuery = controlledSearchQuery ?? internalSearchQuery;\n const setSearchQuery =\n onControlledSearchQueryChange ?? setInternalSearchQuery;\n const [searchResults, setSearchResults] = useState<UnsplashImage[]>([]);\n const [isSearching, setIsSearching] = useState(false);\n const [isLoadingMore, setIsLoadingMore] = useState(false);\n const [currentPage, setCurrentPage] = useState(1);\n const [hasMoreResults, setHasMoreResults] = useState(true);\n const [uploadProgress, setUploadProgress] = useState<\n Map<string, UploadProgress>\n >(new Map());\n const queryClient = useQueryClient();\n const api = useFilePickerApi();\n const { apiBaseUrl, toast } = useFilePickerContext();\n\n const uploadMutation = useMutation({\n mutationFn: (params: CreateDamAssetParams) => api.createDamAsset(params),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n });\n\n const searchImages = useCallback(\n async (query: string, page = 1) => {\n if (!query.trim()) {\n setSearchResults([]);\n setCurrentPage(1);\n setHasMoreResults(true);\n return;\n }\n\n if (page === 1) {\n setIsSearching(true);\n setCurrentPage(1);\n } else {\n setIsLoadingMore(true);\n }\n\n try {\n const data = await api.searchUnsplash(query, page);\n\n setSearchResults((prevResults) =>\n page === 1 ? data.results : [...prevResults, ...data.results],\n );\n\n setCurrentPage(page);\n setHasMoreResults(page < data.total_pages);\n } catch (err) {\n if (page === 1) {\n setSearchResults([]);\n }\n toast.error(\n err instanceof Error ? err.message : \"Unsplash search failed\",\n );\n } finally {\n setIsSearching(false);\n setIsLoadingMore(false);\n }\n },\n [api, toast],\n );\n\n const downloadAndUpload = useCallback(\n async (image: UnsplashImage): Promise<FilePickerResult> => {\n const uploadId = `unsplash-${image.id}`;\n const fileName = `unsplash-${image.id}.jpg`;\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 0,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 25,\n status: \"uploading\",\n });\n return next;\n });\n\n const downloadUrlObj = new URL(image.urls.regular);\n downloadUrlObj.searchParams.set(\"fm\", \"jpg\");\n downloadUrlObj.searchParams.set(\"q\", \"80\");\n const downloadUrl = downloadUrlObj.toString();\n const response = await fetch(downloadUrl);\n\n if (!response.ok) {\n throw new Error(`Failed to download image: ${response.statusText}`);\n }\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 50,\n status: \"uploading\",\n });\n return next;\n });\n\n const blob = await response.blob();\n const file = new File([blob], fileName, { type: \"image/jpeg\" });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 75,\n status: \"uploading\",\n });\n return next;\n });\n\n const damResponse = await uploadMutation.mutateAsync({\n file,\n name: getSanitizedAssetName(\n image.alt_description ||\n image.description ||\n `Unsplash ${image.id}`,\n ),\n description: `Downloaded from Unsplash. Photo by ${image.user.name} (@${image.user.username})`,\n tags: [\"unsplash\", image.user.username],\n });\n\n if (!damResponse.asset) {\n throw new Error(\n \"Invalid DAM response structure - missing asset property\",\n );\n }\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 100,\n status: \"ready\",\n });\n return next;\n });\n\n const asset = damResponse.asset;\n const fileUrl = getDamAssetUrl(asset, apiBaseUrl);\n\n return {\n asset_id: asset.id,\n asset_code: asset.code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: fileName,\n mime_type: \"image/jpeg\",\n dimensions: {\n width: image.width,\n height: image.height,\n },\n },\n upload_status: \"ready\",\n variant_id: asset.default_variant_id\n ? asset.default_variant_id.toString()\n : undefined,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Upload failed\";\n toast.error(errorMessage);\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 0,\n status: \"error\",\n error: errorMessage,\n });\n return next;\n });\n\n return {\n asset_id: 0,\n asset_code: \"\",\n file_path: \"\",\n file_url: \"\",\n metadata: {\n file_name: fileName,\n mime_type: \"image/jpeg\",\n dimensions: {\n width: image.width,\n height: image.height,\n },\n },\n upload_status: \"error\",\n };\n }\n },\n [uploadMutation, apiBaseUrl, toast],\n );\n\n const clearProgress = useCallback(() => {\n setUploadProgress(new Map());\n }, []);\n\n const loadMore = useCallback(() => {\n if (searchQuery.trim() && hasMoreResults && !isLoadingMore) {\n searchImages(searchQuery, currentPage + 1);\n }\n }, [searchQuery, hasMoreResults, isLoadingMore, currentPage, searchImages]);\n\n return {\n searchQuery,\n setSearchQuery,\n searchResults,\n isSearching,\n searchImages,\n downloadAndUpload,\n uploadProgress: Array.from(uploadProgress.values()),\n isUploading: uploadMutation.isPending,\n clearProgress,\n hasMoreResults,\n loadMore,\n isLoadingMore,\n };\n};\n","import React, { useState, useEffect } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport {\n DownloadIcon,\n CheckIcon,\n AlertTriangleIcon,\n UserIcon,\n LoaderIcon,\n} from \"lucide-react\";\nimport { useUnsplashPicker } from \"../hooks/use-unsplash-picker\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n} from \"@fluid-app/file-picker-core\";\n\ninterface UnsplashImage {\n id: string;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n alt_description: string | null;\n description: string | null;\n user: {\n name: string;\n username: string;\n };\n width: number;\n height: number;\n}\n\ninterface UnsplashPickerProps {\n config: FilePickerConfig;\n onFileSelected: (result: FilePickerResult) => void;\n searchQuery?: string;\n onSearchQueryChange?: (value: string) => void;\n onSearchingChange?: (searching: boolean) => void;\n onUploadingChange?: (uploading: boolean) => void;\n}\n\nexport const UnsplashPicker: React.FC<UnsplashPickerProps> = ({\n config,\n onFileSelected,\n searchQuery: controlledSearchQuery,\n onSearchQueryChange: onControlledSearchQueryChange,\n onSearchingChange,\n onUploadingChange,\n}) => {\n const {\n searchQuery,\n searchResults,\n isSearching,\n searchImages,\n downloadAndUpload,\n uploadProgress,\n isUploading,\n clearProgress,\n hasMoreResults,\n loadMore,\n isLoadingMore,\n } = useUnsplashPicker(\n controlledSearchQuery !== undefined && onControlledSearchQueryChange\n ? {\n controlledSearchQuery,\n onControlledSearchQueryChange,\n }\n : undefined,\n );\n\n const [selectedImageId, setSelectedImageId] = useState<string | null>(null);\n\n useEffect(() => {\n const timeoutId = setTimeout(() => {\n if (searchQuery.trim()) {\n searchImages(searchQuery);\n }\n }, 500);\n\n return () => clearTimeout(timeoutId);\n }, [searchQuery, searchImages]);\n\n useEffect(() => {\n onSearchingChange?.(isSearching);\n }, [isSearching, onSearchingChange]);\n\n useEffect(() => {\n onUploadingChange?.(isUploading);\n }, [isUploading, onUploadingChange]);\n\n const handleImageSelect = async (image: UnsplashImage) => {\n setSelectedImageId(image.id);\n clearProgress();\n\n const result = await downloadAndUpload(image);\n\n if (result && result.upload_status === \"ready\") {\n setSelectedImageId(null);\n onFileSelected(result);\n } else {\n setSelectedImageId(null);\n }\n };\n\n return (\n <div className=\"space-y-4\">\n {isSearching && (\n <div className=\"py-4 text-center\">\n <p className=\"text-gray-500\">Searching Unsplash...</p>\n </div>\n )}\n\n {searchResults.length > 0 && (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4\">\n {searchResults.map((image) => (\n <div\n key={image.id}\n className={`group relative cursor-pointer overflow-hidden rounded-lg border-2 transition-all ${selectedImageId === image.id ? \"border-blue-500\" : \"border-transparent hover:border-gray-300\"} ${isUploading && selectedImageId === image.id ? \"opacity-50\" : \"\"} `}\n onClick={() => !isUploading && handleImageSelect(image)}\n >\n <img\n src={image.urls.small}\n alt={\n image.alt_description || image.description || \"Unsplash image\"\n }\n width={400}\n height={300}\n className=\"h-32 w-full object-cover\"\n onError={(e) => {\n const target = e.target as HTMLImageElement;\n target.src = image.urls.thumb;\n }}\n />\n\n <div\n className=\"group-hover:bg-opacity-30 absolute inset-0 flex items-center justify-center transition-all\"\n style={{\n backgroundColor: \"rgba(0, 0, 0, 0)\",\n }}\n >\n <div className=\"opacity-0 transition-opacity group-hover:opacity-100\">\n <DownloadIcon className=\"h-6 w-6 text-white\" />\n </div>\n </div>\n\n <div className=\"absolute right-0 bottom-0 left-0 bg-linear-to-t from-black to-transparent p-2\">\n <div className=\"flex items-center space-x-1 text-xs text-white\">\n <UserIcon className=\"h-3 w-3\" />\n <span>\n Photo by{\" \"}\n <a\n href={`https://unsplash.com/@${image.user.username}?utm_source=fluid&utm_medium=referral`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-gray-200\"\n onClick={(e) => e.stopPropagation()}\n >\n {image.user.name}\n </a>{\" \"}\n on{\" \"}\n <a\n href=\"https://unsplash.com/?utm_source=fluid&utm_medium=referral\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-gray-200\"\n onClick={(e) => e.stopPropagation()}\n >\n Unsplash\n </a>\n </span>\n </div>\n </div>\n\n {selectedImageId === image.id && isUploading && (\n <div className=\"bg-opacity-75 absolute inset-0 flex items-center justify-center bg-blue-500\">\n <div className=\"text-center text-white\">\n <DownloadIcon className=\"mb-1 h-6 w-6 animate-pulse\" />\n <p className=\"text-xs\">Uploading...</p>\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n\n {searchQuery && !isSearching && searchResults.length === 0 && (\n <div className=\"py-8 text-center\">\n <p className=\"text-gray-500\">\n No images found for &quot;{searchQuery}&quot;\n </p>\n </div>\n )}\n\n {hasMoreResults && searchResults.length > 0 && (\n <div className=\"flex justify-center pt-4\">\n <Button onClick={loadMore} disabled={isLoadingMore} variant=\"outline\">\n {isLoadingMore ? (\n <>\n <LoaderIcon className=\"mr-2 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <DownloadIcon className=\"mr-2\" />\n Load More\n </>\n )}\n </Button>\n </div>\n )}\n\n {config.showProgress && uploadProgress.length > 0 && (\n <div className=\"space-y-2\">\n <h4 className=\"text-sm font-medium text-gray-900\">Upload Progress</h4>\n {uploadProgress.map((progress) => (\n <div\n key={`${progress.file_name}-${progress.status}`}\n className=\"rounded-lg bg-gray-50 p-3\"\n >\n <div className=\"mb-2 flex items-center justify-between\">\n <div className=\"flex items-center space-x-2\">\n {(() => {\n const Icon =\n progress.status === \"ready\"\n ? CheckIcon\n : progress.status === \"error\"\n ? AlertTriangleIcon\n : DownloadIcon;\n return (\n <Icon\n className={`h-4 w-4 ${\n progress.status === \"ready\"\n ? \"text-green-500\"\n : progress.status === \"error\"\n ? \"text-red-500\"\n : \"text-blue-500\"\n }`}\n />\n );\n })()}\n <span className=\"text-sm font-medium text-gray-900\">\n {progress.file_name}\n </span>\n </div>\n <span className=\"text-xs text-gray-500 capitalize\">\n {progress.status}\n </span>\n </div>\n\n {progress.status === \"uploading\" && (\n <div className=\"h-2 w-full rounded-full bg-gray-200\">\n <div\n className=\"h-2 rounded-full bg-blue-600 transition-all duration-300\"\n style={{ width: `${progress.progress}%` }}\n />\n </div>\n )}\n\n {progress.error && (\n <p className=\"mt-1 text-xs text-red-600\">{progress.error}</p>\n )}\n </div>\n ))}\n </div>\n )}\n\n <div className=\"text-center text-xs text-gray-400\">\n Photos provided by{\" \"}\n <a\n href=\"https://unsplash.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-gray-600\"\n >\n Unsplash\n </a>\n </div>\n </div>\n );\n};\n","import { useState, useCallback } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n damQueryKeys,\n getDamAssetUrl,\n sanitizeFilename,\n getSanitizedAssetName,\n type CreateDamAssetParams,\n type FilePickerResult,\n type UploadProgress,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerApi } from \"../context/FilePickerApiContext\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nexport const useUrlUpload = (): {\n uploadFromUrl: (url: string, fileName?: string) => Promise<FilePickerResult>;\n uploadProgress: UploadProgress[];\n isUploading: boolean;\n clearProgress: () => void;\n} => {\n const [uploadProgress, setUploadProgress] = useState<\n Map<string, UploadProgress>\n >(new Map());\n const queryClient = useQueryClient();\n const api = useFilePickerApi();\n const { companyId, apiBaseUrl, toast } = useFilePickerContext();\n\n const uploadMutation = useMutation({\n mutationFn: (params: CreateDamAssetParams) => api.createDamAsset(params),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n });\n\n const uploadFromUrl = useCallback(\n async (url: string, fileName?: string): Promise<FilePickerResult> => {\n const uploadId = `url-${Date.now()}`;\n const displayName =\n fileName ||\n url.split(\"/\").pop()?.split(\"?\")[0]?.split(\"#\")[0] ||\n \"url-upload\";\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 0,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 25,\n status: \"uploading\",\n });\n return next;\n });\n\n let blob: Blob;\n let contentType: string;\n\n try {\n const response = await fetch(url, {\n mode: \"cors\",\n credentials: \"omit\",\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch file: ${response.statusText}`);\n }\n\n blob = await response.blob();\n contentType =\n response.headers.get(\"content-type\") || \"application/octet-stream\";\n } catch (fetchError) {\n const isCorsError =\n fetchError instanceof TypeError &&\n (fetchError.message.includes(\"CORS\") ||\n fetchError.message.includes(\"Failed to fetch\") ||\n fetchError.message.includes(\"NetworkError\"));\n\n if (isCorsError) {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 30,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n const proxyResponse = await api.proxyUrlFetch(url);\n const binaryString = atob(proxyResponse.data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n blob = new Blob([bytes], { type: proxyResponse.contentType });\n contentType = proxyResponse.contentType;\n } catch (proxyError) {\n let origin = url;\n try {\n origin = new URL(url).origin;\n } catch {\n // fallback to raw url\n }\n throw new Error(\n `CORS error: The server at ${origin} does not allow cross-origin requests, and the proxy also failed: ${\n proxyError instanceof Error\n ? proxyError.message\n : \"Unknown error\"\n }`,\n );\n }\n } else {\n throw fetchError;\n }\n }\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 50,\n status: \"uploading\",\n });\n return next;\n });\n const rawFileName = displayName;\n const finalFileName = sanitizeFilename(rawFileName);\n\n const file = new File([blob], finalFileName, { type: contentType });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: finalFileName,\n progress: 75,\n status: \"uploading\",\n });\n return next;\n });\n\n const damResponse = await uploadMutation.mutateAsync({\n file,\n name: getSanitizedAssetName(finalFileName),\n description: `Uploaded from URL: ${url}`,\n companyId,\n });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: finalFileName,\n progress: 100,\n status: \"ready\",\n });\n return next;\n });\n\n const asset = damResponse.asset;\n const fileUrl = getDamAssetUrl(asset, apiBaseUrl);\n\n return {\n asset_id: asset.id,\n asset_code: asset.code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: finalFileName,\n mime_type: contentType,\n file_size: blob.size,\n },\n upload_status: \"ready\",\n variant_id: asset.default_variant_id\n ? asset.default_variant_id.toString()\n : undefined,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Upload failed\";\n toast.error(errorMessage);\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 0,\n status: \"error\",\n error: errorMessage,\n });\n return next;\n });\n\n return {\n asset_id: 0,\n asset_code: \"\",\n file_path: \"\",\n file_url: \"\",\n metadata: {\n file_name: displayName,\n mime_type: \"\",\n },\n upload_status: \"error\",\n };\n }\n },\n [uploadMutation, companyId, apiBaseUrl, api, toast],\n );\n\n const clearProgress = useCallback(() => {\n setUploadProgress(new Map());\n }, []);\n\n return {\n uploadFromUrl,\n uploadProgress: Array.from(uploadProgress.values()),\n isUploading: uploadMutation.isPending,\n clearProgress,\n };\n};\n","import React, { useState } from \"react\";\nimport { Input, Switch, Button } from \"@fluid-app/ui-primitives\";\nimport {\n LinkIcon,\n CheckIcon,\n AlertTriangleIcon,\n LoaderIcon,\n FileIcon,\n ArrowRightIcon,\n} from \"lucide-react\";\nimport { useUrlUpload } from \"../hooks/use-url-upload\";\nimport {\n isFileTypeAccepted,\n getAcceptedTypesDescription,\n} from \"@fluid-app/file-picker-core\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\ninterface UrlUploadProps {\n config: FilePickerConfig;\n onFileSelected: (result: FilePickerResult) => void;\n enableShareableOption?: boolean;\n shareableEnabled?: boolean;\n onShareableChange?: (enabled: boolean) => void;\n /** When provided (e.g. FilePicker with selection panel), progress is only shown when there are selected files or an upload is in progress */\n selectedFilesCount?: number;\n}\n\nexport const UrlUpload: React.FC<UrlUploadProps> = ({\n config,\n onFileSelected,\n enableShareableOption = false,\n shareableEnabled = false,\n onShareableChange,\n selectedFilesCount,\n}) => {\n const { toast } = useFilePickerContext();\n const [url, setUrl] = useState(\"\");\n const [fileName, setFileName] = useState(\"\");\n const { uploadFromUrl, uploadProgress, isUploading, clearProgress } =\n useUrlUpload();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!url.trim()) return;\n\n clearProgress();\n\n try {\n // Validate file type if restrictions are configured\n if (config.accept && config.accept.length > 0) {\n try {\n // Fetch the file to check its MIME type\n const response = await fetch(url.trim(), {\n method: \"HEAD\",\n mode: \"cors\",\n credentials: \"omit\",\n });\n const contentType = response.headers.get(\"content-type\") || \"\";\n const finalFileName =\n fileName.trim() || getFileNameFromUrl(url.trim());\n\n if (!isFileTypeAccepted(contentType, finalFileName, config.accept)) {\n const acceptedTypesDesc = getAcceptedTypesDescription(\n config.accept,\n );\n toast.error(\n `This file type is not allowed. Please upload ${acceptedTypesDesc.toLowerCase()}.`,\n );\n return;\n }\n } catch (error) {\n // If we can't check the file type due to CORS, proceed with upload\n // The server will handle validation, but warn the user about potential CORS issues\n if (\n error instanceof TypeError &&\n (error.message.includes(\"CORS\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"NetworkError\"))\n ) {\n // CORS error - we'll proceed but the actual upload might also fail\n // The uploadFromUrl function will handle the error more gracefully\n }\n // For other errors, proceed with upload - the server will handle validation\n }\n }\n\n const result = await uploadFromUrl(\n url.trim(),\n fileName.trim() || undefined,\n );\n\n // Only call onFileSelected if the upload was successful\n // If there's an error, the error will be shown in the progress section\n // and the modal should stay open so the user can see it\n if (result.upload_status === \"ready\") {\n onFileSelected(result);\n setUrl(\"\");\n setFileName(\"\");\n } else {\n // Explicitly do nothing on error - the error is already shown in progress\n // This ensures the modal stays open\n return;\n }\n } catch (error) {\n // Catch any unexpected errors to prevent the modal from closing\n // The error should already be handled by uploadFromUrl and shown in progress\n // This is just a safety net to ensure the modal stays open\n toast.error(error instanceof Error ? error.message : \"Unknown error\");\n }\n };\n\n const isValidUrl = (urlString: string): boolean => {\n try {\n new URL(urlString);\n return true;\n } catch {\n return false;\n }\n };\n\n const getFileNameFromUrl = (urlString: string): string => {\n try {\n const urlObj = new URL(urlString);\n const pathname = urlObj.pathname;\n const filename = pathname.split(\"/\").pop() || \"\";\n return filename.split(\"?\")[0] || \"\";\n } catch {\n return \"\";\n }\n };\n\n const handleUrlChange = (value: string) => {\n setUrl(value);\n if (value && isValidUrl(value) && !fileName) {\n const suggestedName = getFileNameFromUrl(value);\n if (suggestedName) {\n setFileName(suggestedName);\n }\n }\n };\n\n return (\n <div className=\"mx-auto max-w-5xl space-y-6 overflow-y-auto px-4 py-6 md:px-10 md:py-8\">\n {/* Enhanced header with better visual hierarchy */}\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100\">\n <LinkIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">Upload from URL</h2>\n </div>\n\n {/* Enhanced form with better interaction design */}\n\n <form\n onSubmit={handleSubmit}\n className=\"mx-auto w-full space-y-5 md:w-1/2\"\n >\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <Input\n id=\"url\"\n type=\"url\"\n placeholder=\"https://example.com/image.jpg\"\n value={url}\n onChange={(e) => handleUrlChange(e.target.value)}\n disabled={isUploading}\n required\n className={`w-full pr-10 transition-all duration-200 ${\n url && !isValidUrl(url)\n ? \"border-red-300 bg-red-50/30 focus:border-red-500 focus:ring-red-500/20\"\n : \"focus:border-blue-500 focus:ring-blue-500/20\"\n }`}\n />\n {url && (\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n {isValidUrl(url) ? (\n <div className=\"flex h-5 w-5 items-center justify-center rounded-full bg-green-100\">\n <CheckIcon className=\"h-3 w-3 text-green-600\" />\n </div>\n ) : (\n <div className=\"flex h-5 w-5 items-center justify-center rounded-full bg-red-100\">\n <AlertTriangleIcon className=\"h-3 w-3 text-red-600\" />\n </div>\n )}\n </div>\n )}\n </div>\n {url.trim() && isValidUrl(url) && (\n <Button variant=\"default\" type=\"submit\" disabled={isUploading}>\n {isUploading ? (\n <>\n <LoaderIcon className=\"h-3 w-3 animate-spin\" />\n </>\n ) : (\n <>\n <ArrowRightIcon className=\"h-3 w-3\" />\n </>\n )}\n </Button>\n )}\n </div>\n {url && !isValidUrl(url) && (\n <p className=\"text-xs text-red-600\">Please enter a valid URL</p>\n )}\n\n {/* Auto-suggest filename section */}\n {url && isValidUrl(url) && (\n <div className=\"space-y-2\">\n <label\n htmlFor=\"fileName\"\n className=\"flex items-center gap-2 text-sm font-medium text-gray-900\"\n >\n <FileIcon className=\"h-3.5 w-3.5 text-gray-400\" />\n File Name\n <span className=\"text-xs font-normal text-gray-500\">\n (optional)\n </span>\n </label>\n <Input\n id=\"fileName\"\n type=\"text\"\n placeholder={\n getFileNameFromUrl(url) ||\n \"Leave empty to use original filename\"\n }\n value={fileName}\n onChange={(e) => setFileName(e.target.value)}\n disabled={isUploading}\n className=\"w-full transition-all duration-200 focus:border-blue-500 focus:ring-blue-500/20\"\n />\n {getFileNameFromUrl(url) && !fileName && (\n <p className=\"text-xs text-gray-500\">\n Will use filename:{\" \"}\n <span className=\"font-medium\">{getFileNameFromUrl(url)}</span>\n </p>\n )}\n </div>\n )}\n </form>\n\n {/* Enhanced progress feedback */}\n {config.showProgress &&\n uploadProgress.length > 0 &&\n (selectedFilesCount === undefined ||\n selectedFilesCount > 0 ||\n isUploading) && (\n <div className=\"space-y-4\">\n {uploadProgress.map((progress, index) => (\n <div\n key={index}\n className=\"mx-auto w-full overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm md:w-1/2\"\n >\n <div className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-10 w-10 items-center justify-center rounded-xl ${\n progress.status === \"ready\"\n ? \"bg-green-100\"\n : progress.status === \"error\"\n ? \"bg-red-100\"\n : \"bg-blue-100\"\n }`}\n >\n {(() => {\n const Icon =\n progress.status === \"ready\"\n ? CheckIcon\n : progress.status === \"error\"\n ? AlertTriangleIcon\n : LoaderIcon;\n return (\n <Icon\n className={`h-5 w-5 ${\n progress.status === \"ready\"\n ? \"text-green-600\"\n : progress.status === \"error\"\n ? \"text-red-600\"\n : \"animate-spin text-blue-600\"\n }`}\n />\n );\n })()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-gray-900\">\n {progress.file_name}\n </p>\n <div className=\"mt-1 flex items-center gap-2\">\n <p className=\"text-xs text-gray-500 capitalize\">\n {progress.status === \"uploading\"\n ? \"Uploading...\"\n : progress.status === \"ready\"\n ? \"Complete\"\n : progress.status}\n </p>\n {progress.status === \"uploading\" && (\n <span className=\"text-xs font-medium text-blue-600\">\n {progress.progress}%\n </span>\n )}\n </div>\n </div>\n </div>\n\n {progress.status === \"uploading\" && (\n <div className=\"mt-3\">\n <div className=\"h-2 w-full overflow-hidden rounded-full bg-gray-100\">\n <div\n className=\"h-2 rounded-full bg-linear-to-r from-blue-500 to-blue-600 transition-all duration-500 ease-out\"\n style={{ width: `${progress.progress}%` }}\n />\n </div>\n </div>\n )}\n\n {progress.error && (\n <div className=\"mt-3 rounded-lg bg-red-50 p-3\">\n <p className=\"text-sm text-red-700\">{progress.error}</p>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Footer with Shareable Option */}\n {enableShareableOption && (\n <div className=\"mx-auto flex w-full items-center justify-between md:w-1/2\">\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={shareableEnabled}\n onCheckedChange={onShareableChange}\n id=\"shareable-toggle-url\"\n />\n <label\n htmlFor=\"shareable-toggle-url\"\n className=\"text-sm text-gray-700\"\n >\n Create as shareable media\n </label>\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { useState, useCallback } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n damQueryKeys,\n getDamAssetUrl,\n getSanitizedAssetName,\n getFileMimeType,\n type CreateDamAssetParams,\n type FilePickerResult,\n type UploadProgress,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerApi } from \"../context/FilePickerApiContext\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nexport const useComputerUpload = (): {\n uploadFiles: (files: File[]) => Promise<FilePickerResult[]>;\n uploadProgress: UploadProgress[];\n isUploading: boolean;\n clearProgress: () => void;\n} => {\n const [uploadProgress, setUploadProgress] = useState<\n Map<string, UploadProgress>\n >(new Map());\n const queryClient = useQueryClient();\n const api = useFilePickerApi();\n const { companyId, toast, apiBaseUrl } = useFilePickerContext();\n\n const uploadMutation = useMutation({\n mutationFn: (params: CreateDamAssetParams) => api.createDamAsset(params),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n });\n\n const uploadFiles = useCallback(\n async (files: File[]): Promise<FilePickerResult[]> => {\n const results: FilePickerResult[] = [];\n\n for (const file of files) {\n const fileId = `${file.name}-${Date.now()}`;\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 0,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 50,\n status: \"uploading\",\n });\n return next;\n });\n\n const response = await uploadMutation.mutateAsync({\n file,\n name: getSanitizedAssetName(file.name),\n description: `Uploaded via FilePicker: ${file.name}`,\n companyId,\n });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 100,\n status: \"ready\",\n });\n return next;\n });\n\n const asset = response.asset;\n const fileUrl = getDamAssetUrl(asset, apiBaseUrl);\n\n results.push({\n asset_id: asset.id,\n asset_code: asset.code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: file.name,\n mime_type: getFileMimeType(file),\n file_size: file.size,\n },\n upload_status: \"ready\",\n variant_id: asset.default_variant_id\n ? asset.default_variant_id.toString()\n : undefined,\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Upload failed\";\n const status =\n error instanceof Error && \"status\" in error\n ? (error as { status: number }).status\n : undefined;\n const isFileSizeError =\n status === 413 ||\n (status === 400 &&\n errorMessage.toLowerCase().includes(\"file size\"));\n if (isFileSizeError) {\n toast.error(\n \"File size too large: Please compress or choose another file\",\n );\n } else {\n toast.error(errorMessage);\n }\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 0,\n status: \"error\",\n error: errorMessage,\n });\n return next;\n });\n\n results.push({\n asset_id: 0,\n asset_code: \"\",\n file_path: \"\",\n file_url: \"\",\n metadata: {\n file_name: file.name,\n mime_type: getFileMimeType(file),\n file_size: file.size,\n },\n upload_status: \"error\",\n });\n }\n }\n\n return results;\n },\n [uploadMutation, companyId, toast, apiBaseUrl],\n );\n\n const clearProgress = useCallback(() => {\n setUploadProgress(new Map());\n }, []);\n\n return {\n uploadFiles,\n uploadProgress: Array.from(uploadProgress.values()),\n isUploading: uploadMutation.isPending,\n clearProgress,\n };\n};\n","import React, {\n useState,\n useRef,\n useCallback,\n useEffect,\n useMemo,\n} from \"react\";\nimport ReactCrop, { type Crop, type PixelCrop } from \"react-image-crop\";\nimport \"react-image-crop/dist/ReactCrop.css\";\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogTitle,\n Slider,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport {\n MoveHorizontalIcon,\n RotateCwIcon,\n ChevronDownIcon,\n MinusIcon,\n PlusIcon,\n} from \"lucide-react\";\nimport type {\n ImageCropModalProps,\n AspectValue,\n} from \"@fluid-app/file-picker-core\";\nimport {\n ASPECT_OPTIONS,\n ROTATION_MIN,\n ROTATION_MAX,\n computeInitialCrop,\n getCroppedImgFromViewport,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { useIsMobile } from \"../hooks/use-is-mobile\";\n\nexport const ImageCropModal: React.FC<ImageCropModalProps> = ({\n isOpen,\n onClose,\n onCrop,\n imageFile,\n aspectRatio: aspectRatioProp,\n minWidth = 50,\n minHeight = 50,\n maxWidth = 2000,\n maxHeight = 2000,\n}) => {\n const { toast } = useFilePickerContext();\n const isMobile = useIsMobile();\n const [isAspectOpen, setIsAspectOpen] = useState(false);\n const [crop, setCrop] = useState<Crop>();\n const [completedCrop, setCompletedCrop] = useState<PixelCrop>();\n const [imgSrc, setImgSrc] = useState<string>(\"\");\n const [isProcessing, setIsProcessing] = useState(false);\n const [aspectOption, setAspectOption] = useState<AspectValue>(\n aspectRatioProp ?? \"free\",\n );\n const [rotation, setRotation] = useState(0);\n const [flipHorizontal, setFlipHorizontal] = useState(false);\n const [zoom, setZoom] = useState(1);\n const [cropModified, setCropModified] = useState(false);\n const [cropViewportWidthPx, setCropViewportWidthPx] = useState<number | null>(\n null,\n );\n const imgRef = useRef<HTMLImageElement>(null);\n const cropContainerRef = useRef<HTMLDivElement>(null);\n const cropWrapperRef = useRef<HTMLDivElement>(null);\n\n const effectiveAspect = aspectOption === \"free\" ? undefined : aspectOption;\n\n const outputDimensions = useMemo(() => {\n const img = imgRef.current;\n if (!completedCrop || !img) return null;\n const nw = img.naturalWidth;\n const nh = img.naturalHeight;\n const vw = img.clientWidth;\n const vh = img.clientHeight;\n if (!vw || !vh) return null;\n const upscale = Math.max(nw / vw, nh / vh);\n return {\n width: Math.floor(completedCrop.width * upscale),\n height: Math.floor(completedCrop.height * upscale),\n };\n }, [completedCrop]);\n\n const initialAspect = aspectRatioProp ?? \"free\";\n const hasChanges =\n rotation !== 0 ||\n flipHorizontal !== false ||\n zoom !== 1 ||\n aspectOption !== initialAspect ||\n cropModified;\n\n useEffect(() => {\n if (isOpen && imageFile) {\n const reader = new FileReader();\n reader.addEventListener(\"load\", () => {\n setImgSrc(reader.result as string);\n });\n reader.readAsDataURL(imageFile);\n }\n if (!isOpen) {\n setRotation(0);\n setFlipHorizontal(false);\n setZoom(1);\n setCropModified(false);\n setCropViewportWidthPx(null);\n setAspectOption(aspectRatioProp ?? \"free\");\n }\n }, [isOpen, imageFile, aspectRatioProp]);\n\n /** Apply crop + completedCrop for given aspect, reading container dimensions. */\n const applyCropForAspect = useCallback((aspect: number | undefined) => {\n const img = imgRef.current;\n if (!img) return;\n const { crop: newCrop, completedCrop: newCompleted } = computeInitialCrop(\n img.clientWidth,\n img.clientHeight,\n aspect,\n );\n setCrop(newCrop);\n setTimeout(() => setCompletedCrop(newCompleted), 0);\n }, []);\n\n const onImageLoad = useCallback(() => {\n if (!imgRef.current) return;\n applyCropForAspect(effectiveAspect);\n }, [effectiveAspect, applyCropForAspect]);\n\n const handleCrop = async () => {\n if (!imgRef.current) return;\n setIsProcessing(true);\n try {\n const vw = imgRef.current.clientWidth;\n const vh = imgRef.current.clientHeight;\n\n let px: number, py: number, pw: number, ph: number;\n if (completedCrop) {\n px = completedCrop.x;\n py = completedCrop.y;\n pw = completedCrop.width;\n ph = completedCrop.height;\n } else if (crop) {\n if (crop.unit === \"%\") {\n px = (crop.x / 100) * vw;\n py = (crop.y / 100) * vh;\n pw = (crop.width / 100) * vw;\n ph = (crop.height / 100) * vh;\n } else {\n px = (crop as PixelCrop).x;\n py = (crop as PixelCrop).y;\n pw = (crop as PixelCrop).width;\n ph = (crop as PixelCrop).height;\n }\n } else {\n px = 0;\n py = 0;\n pw = vw;\n ph = vh;\n }\n\n // Use viewport-based export so rotation, flip, and zoom match the preview.\n // Pass sourceFile so the bitmap is decoded at full resolution from the\n // original bytes, not from the <img> element (which the browser may have\n // decoded at a reduced resolution due to its small CSS display size).\n const croppedFile = await getCroppedImgFromViewport(\n imgRef.current,\n vw,\n vh,\n { x: px, y: py, width: pw, height: ph },\n zoom,\n rotation,\n flipHorizontal,\n imageFile.name,\n imageFile.type,\n 1,\n imageFile,\n );\n await Promise.resolve(onCrop(croppedFile));\n onClose();\n } catch (error) {\n toast.error(\n error instanceof Error ? error.message : \"Failed to crop image\",\n );\n } finally {\n setIsProcessing(false);\n }\n };\n\n const resetCrop = () => {\n setRotation(0);\n setFlipHorizontal(false);\n setZoom(1);\n setCropModified(false);\n setAspectOption(aspectRatioProp ?? \"free\");\n applyCropForAspect(aspectRatioProp ?? undefined);\n };\n\n const aspectLabel =\n ASPECT_OPTIONS.find(\n (o) =>\n o.value === aspectOption ||\n (typeof o.value === \"number\" && o.value === aspectOption),\n )?.label ?? \"Free\";\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()} modal>\n <DialogContent\n overlayClassName=\"bg-gray-500/75\"\n showCloseButton={false}\n className=\"border-border text-foreground z-400 flex h-[90vh]! w-[95vw]! max-w-[1600px]! flex-col overflow-auto border bg-white p-0\"\n >\n <DialogTitle className=\"sr-only\">Crop Image</DialogTitle>\n <div className=\"bg-muted/30 flex shrink-0 flex-wrap items-center justify-center gap-2 px-4 py-2\">\n {isMobile ? (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n onClick={() => setIsAspectOpen(true)}\n >\n {aspectLabel}\n <ChevronDownIcon className=\"h-3 w-3\" />\n </Button>\n <MobileActionSheet\n open={isAspectOpen}\n onOpenChange={setIsAspectOpen}\n title=\"Aspect ratio\"\n actions={ASPECT_OPTIONS.map((option) => ({\n id: String(option.value),\n label: option.label,\n onSelect: () => {\n setAspectOption(option.value);\n applyCropForAspect(\n option.value === \"free\" ? undefined : option.value,\n );\n },\n }))}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger className=\"inline-flex items-center gap-1 rounded-md border border-gray-200 bg-white px-3 py-1.5 text-sm text-gray-700 shadow-sm transition-colors hover:bg-gray-50\">\n {aspectLabel}\n <ChevronDownIcon className=\"h-3 w-3\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-410\">\n {ASPECT_OPTIONS.map((opt) => (\n <DropdownMenuItem\n key={String(opt.value)}\n onClick={() => {\n setAspectOption(opt.value);\n applyCropForAspect(\n opt.value === \"free\" ? undefined : opt.value,\n );\n }}\n >\n {opt.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => setFlipHorizontal((f) => !f)}\n aria-label=\"Flip horizontal\"\n className=\"gap-1 bg-transparent\"\n >\n <MoveHorizontalIcon className=\"h-4 w-4\" />\n <span className=\"text-xs\">Flip</span>\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => {\n setRotation((r) => {\n const next = r + 90;\n if (next > ROTATION_MAX) return next - 360;\n if (next < ROTATION_MIN) return next + 360;\n return next;\n });\n }}\n aria-label=\"Rotate 90deg\"\n className=\"gap-1 bg-transparent\"\n >\n <RotateCwIcon className=\"h-4 w-4\" />\n <span className=\"text-xs\">Rotate</span>\n </Button>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() =>\n setZoom((z) => Math.max(0.5, Math.round((z - 0.1) * 10) / 10))\n }\n className=\"bg-transparent\"\n aria-label=\"Zoom out\"\n >\n <MinusIcon className=\"h-3 w-3\" />\n </Button>\n <Slider\n min={0.5}\n max={2}\n step={0.1}\n value={[zoom]}\n onValueChange={([value]) => {\n if (value !== undefined) setZoom(value);\n }}\n className=\"accent-contrast h-2 w-24 shrink-0\"\n aria-label=\"Zoom\"\n />\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() =>\n setZoom((z) => Math.min(2, Math.round((z + 0.1) * 10) / 10))\n }\n className=\"bg-transparent\"\n aria-label=\"Zoom in\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n </Button>\n <span\n className=\"text-muted-foreground min-w-10 text-xs\"\n aria-hidden\n >\n {Math.round(zoom * 100)}%\n </span>\n </div>\n </div>\n\n <div className=\"relative flex w-full flex-1 items-center justify-center overflow-auto p-4\">\n {imgSrc && (\n <div\n ref={cropWrapperRef}\n className=\"mx-auto\"\n style={{\n width:\n cropViewportWidthPx != null\n ? `${cropViewportWidthPx}px`\n : \"100%\",\n }}\n >\n <div\n ref={cropContainerRef}\n className=\"image-crop-modal-crop [&_.ReactCrop__crop-selection]:border-contrast [&_.ReactCrop__rule]:border-border flex w-full items-center justify-center [&_.ReactCrop__crop-selection]:border-2 [&_.ReactCrop__crop-selection]:border-dashed [&_.ReactCrop__crop-selection]:shadow-[0_0_0_9999px_rgba(0,0,0,0.4)]\"\n >\n <ReactCrop\n crop={crop}\n onChange={(pixelCrop, percentCrop) => {\n setCrop(percentCrop);\n setCompletedCrop(pixelCrop);\n setCropModified(true);\n }}\n onComplete={(c) => setCompletedCrop(c)}\n aspect={effectiveAspect}\n minWidth={minWidth}\n minHeight={minHeight}\n maxWidth={maxWidth}\n maxHeight={maxHeight}\n >\n <img\n ref={imgRef}\n alt=\"Crop\"\n src={imgSrc}\n className=\"block max-h-full max-w-full object-contain sm:h-[300px] xl:h-[500px]\"\n style={{\n transform: [\n `rotate(${rotation}deg)`,\n `scale(${zoom})`,\n flipHorizontal ? \"scaleX(-1)\" : null,\n ]\n .filter(Boolean)\n .join(\" \"),\n transformOrigin: \"center center\",\n }}\n onLoad={onImageLoad}\n />\n </ReactCrop>\n </div>\n </div>\n )}\n </div>\n\n <div className=\"bg-muted/30 flex shrink-0 flex-col gap-3 px-4 py-3\">\n <div className=\"flex justify-center\">\n <span className=\"text-muted-foreground text-xs\" aria-hidden>\n {outputDimensions\n ? `${outputDimensions.width} x ${outputDimensions.height} px · ${rotation}deg`\n : `${rotation}deg`}\n </span>\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={resetCrop}\n disabled={isProcessing || !hasChanges}\n className=\"border-0 shadow-none\"\n >\n Reset to Default\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"border-0 shadow-none\"\n >\n Cancel\n </Button>\n <Button\n variant=\"default\"\n size=\"sm\"\n onClick={handleCrop}\n disabled={!crop || isProcessing}\n className=\"border-0\"\n >\n {isProcessing ? \"Processing...\" : \"Apply Crop\"}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n );\n};\n","import React, { useCallback, useState } from \"react\";\nimport { useDropzone, type FileRejection } from \"react-dropzone\";\nimport { Button, Card, CardContent, Switch } from \"@fluid-app/ui-primitives\";\nimport {\n CheckIcon,\n AlertTriangleIcon,\n LoaderIcon,\n ArrowUpIcon,\n} from \"lucide-react\";\nimport { useComputerUpload } from \"../hooks/use-computer-upload\";\nimport { ImageCropModal } from \"./ImageCropModal\";\nimport { getFileMimeType } from \"@fluid-app/file-picker-core\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nfunction formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i];\n}\n\ninterface ComputerUploadProps {\n config: FilePickerConfig;\n onFilesSelected: (results: FilePickerResult[]) => void;\n enableShareableOption?: boolean;\n shareableEnabled?: boolean;\n onShareableChange?: (enabled: boolean) => void;\n skipCropper?: boolean;\n selectedFilesCount?: number;\n}\n\nexport const ComputerUpload: React.FC<ComputerUploadProps> = ({\n config,\n onFilesSelected,\n enableShareableOption = false,\n shareableEnabled = false,\n onShareableChange,\n skipCropper = false,\n selectedFilesCount,\n}) => {\n const { toast } = useFilePickerContext();\n const { uploadFiles, uploadProgress, isUploading, clearProgress } =\n useComputerUpload();\n const [dragActive, setDragActive] = useState(false);\n const [cropModalOpen, setCropModalOpen] = useState(false);\n const [pendingImageFile, setPendingImageFile] = useState<File | null>(null);\n const [pendingFiles, setPendingFiles] = useState<File[]>([]);\n\n const handleDrop = useCallback(\n async (acceptedFiles: File[]) => {\n setDragActive(false);\n clearProgress();\n\n const filesToUpload = config.maxFiles\n ? acceptedFiles.slice(0, config.maxFiles)\n : acceptedFiles;\n\n // Check if any files are images that can be cropped\n // HEIC/HEIF are excluded because <canvas> cannot render them natively\n const UNCROPABLE_TYPES = [\n \"image/heic\",\n \"image/heic-sequence\",\n \"image/heif\",\n \"image/svg+xml\",\n ];\n const isCropableImage = (file: File) => {\n const mime = getFileMimeType(file);\n return mime.startsWith(\"image/\") && !UNCROPABLE_TYPES.includes(mime);\n };\n const imageFiles = filesToUpload.filter(isCropableImage);\n const nonImageFiles = filesToUpload.filter((f) => !isCropableImage(f));\n\n if (imageFiles.length === 1 && !skipCropper) {\n // Only show crop tool for single image uploads\n // Store non-image files for later upload\n setPendingFiles(nonImageFiles);\n // Open crop modal for the single image\n setPendingImageFile(imageFiles[0] || null);\n setCropModalOpen(true);\n } else {\n // Multiple files or no images to crop or cropper is disabled, upload directly\n const results = await uploadFiles(filesToUpload);\n onFilesSelected(results);\n }\n },\n [uploadFiles, onFilesSelected, config.maxFiles, clearProgress, skipCropper],\n );\n\n const handleCropComplete = useCallback(\n async (croppedFile: File) => {\n setCropModalOpen(false);\n\n // Combine cropped file with any pending non-image files\n const allFiles = [croppedFile, ...pendingFiles];\n const results = await uploadFiles(allFiles);\n onFilesSelected(results);\n\n // Reset state\n setPendingImageFile(null);\n setPendingFiles([]);\n },\n [uploadFiles, onFilesSelected, pendingFiles],\n );\n\n const handleCropCancel = useCallback(() => {\n setCropModalOpen(false);\n setPendingImageFile(null);\n\n // Upload non-image files without cropping\n if (pendingFiles.length > 0) {\n uploadFiles(pendingFiles).then(onFilesSelected);\n }\n setPendingFiles([]);\n }, [uploadFiles, onFilesSelected, pendingFiles]);\n\n const handleDropRejected = useCallback(\n (fileRejections: FileRejection[]) => {\n fileRejections.forEach(({ file, errors }) => {\n errors.forEach((error) => {\n if (error.code === \"file-too-large\") {\n toast.error(\n `File size too large: \"${file.name}\" (${formatFileSize(file.size)}) exceeds the maximum allowed size.`,\n );\n } else if (error.code === \"file-invalid-type\") {\n toast.error(\n `Invalid file type: \"${file.name}\" is not an accepted file type.`,\n );\n } else if (error.code === \"too-many-files\") {\n toast.error(\n `Too many files: You can only upload ${config.maxFiles} file${config.maxFiles === 1 ? \"\" : \"s\"} at a time.`,\n );\n } else {\n toast.error(error.message);\n }\n });\n });\n },\n [config.maxFiles, toast],\n );\n\n const { getRootProps, getInputProps, isDragActive } = useDropzone({\n onDrop: handleDrop,\n onDropRejected: (fileRejections: FileRejection[]) =>\n handleDropRejected(fileRejections),\n accept: config.accept\n ? config.accept.reduce(\n (acc, type) => {\n // Handle both MIME types (e.g., \"image/*\") and file extensions (e.g., \".jpg\")\n if (type.startsWith(\".\")) {\n // File extension - add to a generic MIME type\n acc[\"*/*\"] = acc[\"*/*\"] || [];\n acc[\"*/*\"].push(type);\n } else {\n // MIME type - add as is\n acc[type] = [];\n }\n return acc;\n },\n {} as Record<string, string[]>,\n )\n : undefined,\n maxSize: config.maxSize,\n maxFiles: config.maxFiles,\n onDragEnter: () => setDragActive(true),\n onDragLeave: () => setDragActive(false),\n });\n\n return (\n <div className=\"mx-auto max-w-7xl space-y-6 overflow-y-auto px-4 py-6 md:px-10 md:py-8\">\n {/* Enhanced dropzone card */}\n <Card className=\"mx-auto w-full border-none shadow-none md:w-1/2\">\n <CardContent className=\"p-6\">\n <div\n {...getRootProps()}\n className={`group cursor-pointer rounded-lg border-2 border-dashed p-8 text-center transition-all duration-200 ${\n isDragActive || dragActive\n ? \"scale-[1.02] border-blue-500 bg-blue-50/50\"\n : \"border-gray-300 hover:border-blue-400 hover:bg-blue-50/20\"\n } ${isUploading ? \"pointer-events-none opacity-50\" : \"\"}`}\n >\n <input {...getInputProps()} />\n <div className=\"space-y-4\">\n {/* Header with icon moved inside dropzone */}\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100 group-hover:bg-blue-200\">\n <ArrowUpIcon className=\"h-10 w-10 text-gray-600 group-hover:text-blue-500\" />\n </div>\n </div>\n\n {/* File constraints */}\n <p className=\"text-center text-xs text-gray-500\">\n 50MB for Images and Documents. 2GB for Videos.\n </p>\n\n <Button\n variant=\"default\"\n disabled={isUploading}\n className=\"transition-all duration-200\"\n >\n {isUploading ? (\n <>\n <LoaderIcon className=\"mr-2 h-4 w-4 animate-spin\" />\n Uploading...\n </>\n ) : (\n \"Choose Files\"\n )}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Enhanced progress feedback */}\n {config.showProgress &&\n uploadProgress.length > 0 &&\n (selectedFilesCount === undefined ||\n selectedFilesCount > 0 ||\n isUploading) && (\n <div className=\"space-y-4\">\n {uploadProgress.map((progress) => (\n <div\n key={`${progress.file_name}-${progress.status}`}\n className=\"mx-auto w-full overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm md:w-1/2\"\n >\n <div className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-10 w-10 items-center justify-center rounded-xl ${\n progress.status === \"ready\"\n ? \"bg-green-100\"\n : progress.status === \"error\"\n ? \"bg-red-100\"\n : \"bg-blue-100\"\n }`}\n >\n {(() => {\n const Icon =\n progress.status === \"ready\"\n ? CheckIcon\n : progress.status === \"error\"\n ? AlertTriangleIcon\n : LoaderIcon;\n return (\n <Icon\n className={`h-5 w-5 ${\n progress.status === \"ready\"\n ? \"text-green-600\"\n : progress.status === \"error\"\n ? \"text-red-600\"\n : \"animate-spin text-blue-600\"\n }`}\n />\n );\n })()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-gray-900\">\n {progress.file_name}\n </p>\n <div className=\"mt-1 flex items-center gap-2\">\n <p className=\"text-xs text-gray-500 capitalize\">\n {progress.status === \"uploading\"\n ? \"Uploading...\"\n : progress.status === \"ready\"\n ? \"Complete\"\n : progress.status}\n </p>\n {progress.status === \"uploading\" && (\n <span className=\"text-xs font-medium text-blue-600\">\n {progress.progress}%\n </span>\n )}\n </div>\n </div>\n </div>\n\n {progress.status === \"uploading\" && (\n <div className=\"mt-3\">\n <div className=\"h-2 w-full overflow-hidden rounded-full bg-gray-100\">\n <div\n className=\"h-2 rounded-full bg-linear-to-r from-blue-500 to-blue-600 transition-all duration-500 ease-out\"\n style={{ width: `${progress.progress}%` }}\n />\n </div>\n </div>\n )}\n\n {progress.error && (\n <div className=\"mt-3 rounded-lg bg-red-50 p-3\">\n <p className=\"text-sm text-red-700\">{progress.error}</p>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Footer with Shareable Option */}\n {enableShareableOption && (\n <div className=\"mx-auto flex w-full items-center justify-between px-4 md:w-1/2\">\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={shareableEnabled}\n onCheckedChange={onShareableChange}\n id=\"shareable-toggle-computer\"\n />\n <label\n htmlFor=\"shareable-toggle-computer\"\n className=\"text-sm text-gray-700\"\n >\n Create as shareable media\n </label>\n </div>\n </div>\n )}\n\n {/* Image Crop Modal */}\n {pendingImageFile && (\n <ImageCropModal\n isOpen={cropModalOpen}\n onClose={handleCropCancel}\n onCrop={handleCropComplete}\n imageFile={pendingImageFile}\n minWidth={50}\n minHeight={50}\n maxWidth={2000}\n maxHeight={2000}\n />\n )}\n </div>\n );\n};\n","import React from \"react\";\nimport {\n GoogleIcon,\n InstagramIcon,\n FacebookIcon,\n TiktokIcon,\n DropboxIcon,\n} from \"../utils/brand-icons\";\nimport { DamLibrary, type DamLibraryRef } from \"./DamLibrary\";\nimport { MediaTab } from \"./MediaTab\";\nimport { PagesTab } from \"./PagesTab\";\nimport { ProductsTab } from \"./ProductsTab\";\nimport { EnrollmentPacksTab } from \"./EnrollmentPacksTab\";\nimport { UnsplashPicker } from \"./UnsplashPicker\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n PickerMethod,\n DamAsset,\n} from \"@fluid-app/file-picker-core\";\nimport type { ViewMode } from \"./FilePickerHeader\";\nimport type { SortOptionId } from \"@fluid-app/file-picker-core\";\nimport { UrlUpload } from \"./UrlUpload\";\nimport { ComputerUpload } from \"./ComputerUpload\";\nimport type { MediumResponse } from \"../types/media\";\n\nexport type { MediumResponse };\n\ninterface FilePickerContentProps {\n activeMethod: PickerMethod;\n config: FilePickerConfig;\n damTypeFilter?: string;\n sortOption?: SortOptionId;\n onFilesSelected: (results: FilePickerResult[]) => void;\n onUploadConfirmAndMaybeCreate: (results: FilePickerResult[]) => void;\n onSingleFileSelected: (result: FilePickerResult) => void;\n enableShareableOption?: boolean;\n shareableEnabled?: boolean;\n onShareableChange?: (enabled: boolean) => void;\n damLibraryRef: React.RefObject<DamLibraryRef | null>;\n onDamConfirmAndMaybeCreate: (results: FilePickerResult[]) => void;\n selectedMediaIds: number[];\n onMediaIdsChange: (ids: number[]) => void;\n onMediaSelectionChange: (count: number) => void;\n onMediaDataChange: (data: FilePickerResult[]) => void;\n selectedPageIds?: number[];\n onPageIdsChange?: (ids: number[]) => void;\n onPageSelectionChange?: (count: number) => void;\n onPageDataChange?: (data: FilePickerResult[]) => void;\n selectedProductIds?: number[];\n onProductIdsChange?: (ids: number[]) => void;\n onProductSelectionChange?: (count: number) => void;\n onProductDataChange?: (data: FilePickerResult[]) => void;\n selectedEnrollmentPackIds?: number[];\n onEnrollmentPackIdsChange?: (ids: number[]) => void;\n onEnrollmentPackSelectionChange?: (count: number) => void;\n onEnrollmentPackDataChange?: (data: FilePickerResult[]) => void;\n onDamSelectionChange: (\n count: number,\n files?: FilePickerResult[],\n variantCounts?: Record<string, number>,\n ) => void;\n onDamSearchingChange?: (searching: boolean) => void;\n skipCropper?: boolean;\n damSearchQuery?: string;\n onDamSearchChange?: (value: string) => void;\n mediaSearchQuery?: string;\n onMediaSearchChange?: (value: string) => void;\n unsplashSearchQuery?: string;\n onUnsplashSearchChange?: (value: string) => void;\n onUnsplashSearchingChange?: (searching: boolean) => void;\n onUnsplashUploadingChange?: (uploading: boolean) => void;\n thumbnailSize?: number;\n viewMode?: ViewMode;\n showNamesOnMedia?: boolean;\n onDamFoldersChange?: (folders: string[]) => void;\n previewContainer?: HTMLDivElement | null;\n previewMedia?: MediumResponse | null;\n onPreviewMediaChange?: (media: MediumResponse | null) => void;\n previewAsset?: DamAsset | null;\n onPreviewAssetChange?: (asset: DamAsset | null) => void;\n uploadSelectionCount?: number;\n}\n\nexport const FilePickerContent: React.FC<FilePickerContentProps> = ({\n activeMethod,\n config,\n damTypeFilter,\n sortOption,\n onFilesSelected,\n onSingleFileSelected,\n enableShareableOption,\n shareableEnabled,\n onShareableChange,\n damLibraryRef,\n onDamConfirmAndMaybeCreate,\n selectedMediaIds,\n onMediaIdsChange,\n onMediaSelectionChange,\n onMediaDataChange,\n selectedPageIds,\n onPageIdsChange,\n onPageSelectionChange,\n onPageDataChange,\n selectedProductIds,\n onProductIdsChange,\n onProductSelectionChange,\n onProductDataChange,\n selectedEnrollmentPackIds,\n onEnrollmentPackIdsChange,\n onEnrollmentPackSelectionChange,\n onEnrollmentPackDataChange,\n onDamSelectionChange,\n onDamSearchingChange,\n skipCropper = false,\n damSearchQuery = \"\",\n onDamSearchChange,\n mediaSearchQuery,\n onMediaSearchChange,\n unsplashSearchQuery,\n onUnsplashSearchChange,\n onUnsplashSearchingChange,\n onUnsplashUploadingChange,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n onDamFoldersChange,\n previewContainer,\n previewMedia,\n onPreviewMediaChange,\n previewAsset,\n onPreviewAssetChange,\n uploadSelectionCount,\n}) => {\n const damConfig = React.useMemo(\n () => ({\n ...config,\n fileTypeFilter: damTypeFilter ? [damTypeFilter] : config.fileTypeFilter,\n }),\n [config, damTypeFilter],\n );\n return (\n <div className=\"flex-1 overflow-x-hidden overflow-y-auto p-6\">\n {(activeMethod === \"upload\" || activeMethod === \"computer\") && (\n <ComputerUpload\n config={config}\n onFilesSelected={onFilesSelected}\n enableShareableOption={enableShareableOption}\n shareableEnabled={shareableEnabled}\n onShareableChange={onShareableChange}\n skipCropper={skipCropper}\n selectedFilesCount={uploadSelectionCount}\n />\n )}\n {activeMethod === \"url\" && (\n <UrlUpload\n config={config}\n onFileSelected={onSingleFileSelected}\n enableShareableOption={enableShareableOption}\n shareableEnabled={shareableEnabled}\n onShareableChange={onShareableChange}\n selectedFilesCount={uploadSelectionCount}\n />\n )}\n\n {activeMethod === (\"dam\" as PickerMethod) && (\n <DamLibrary\n ref={damLibraryRef}\n config={damConfig}\n sortOption={sortOption}\n onFoldersChange={onDamFoldersChange}\n onAssetsSelected={onFilesSelected}\n onSelectionChange={onDamSelectionChange}\n onConfirmAndClose={(results) => {\n void onDamConfirmAndMaybeCreate(results);\n }}\n onSearchingChange={onDamSearchingChange}\n showVariants={config.showVariants}\n searchQuery={damSearchQuery}\n onSearchChange={onDamSearchChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n previewContainer={previewContainer}\n previewAsset={previewAsset}\n onPreviewAssetChange={onPreviewAssetChange}\n />\n )}\n\n {activeMethod === (\"media\" as PickerMethod) && (\n <MediaTab\n config={config}\n onMediaSelected={() => {}} // Not used for media tab\n onSelectionChange={onMediaSelectionChange}\n onMediaDataChange={onMediaDataChange}\n selectedMediaIds={selectedMediaIds}\n onMediaIdsChange={onMediaIdsChange}\n searchQuery={mediaSearchQuery}\n onSearchChange={onMediaSearchChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n mediaTypeFilter={damTypeFilter}\n sortOption={sortOption}\n previewContainer={previewContainer}\n previewMedia={previewMedia}\n onPreviewMediaChange={onPreviewMediaChange}\n />\n )}\n\n {activeMethod === \"pages\" && (\n <PagesTab\n config={config}\n selectedIds={selectedPageIds ?? []}\n onSelectedIdsChange={onPageIdsChange ?? (() => {})}\n onSelectedDataChange={onPageDataChange ?? (() => {})}\n onSelectionCountChange={onPageSelectionChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n />\n )}\n\n {activeMethod === \"products\" && (\n <ProductsTab\n config={config}\n selectedIds={selectedProductIds ?? []}\n onSelectedIdsChange={onProductIdsChange ?? (() => {})}\n onSelectedDataChange={onProductDataChange ?? (() => {})}\n onSelectionCountChange={onProductSelectionChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n />\n )}\n\n {activeMethod === \"enrollment_packs\" && (\n <EnrollmentPacksTab\n config={config}\n selectedIds={selectedEnrollmentPackIds ?? []}\n onSelectedIdsChange={onEnrollmentPackIdsChange ?? (() => {})}\n onSelectedDataChange={onEnrollmentPackDataChange ?? (() => {})}\n onSelectionCountChange={onEnrollmentPackSelectionChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n />\n )}\n\n {activeMethod === \"unsplash\" && (\n <UnsplashPicker\n config={config}\n onFileSelected={onSingleFileSelected}\n searchQuery={unsplashSearchQuery}\n onSearchQueryChange={onUnsplashSearchChange}\n onSearchingChange={onUnsplashSearchingChange}\n onUploadingChange={onUnsplashUploadingChange}\n />\n )}\n\n {activeMethod === \"google-drive\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-blue-50 to-indigo-50 ring-4 ring-blue-50/50\">\n <GoogleIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Google Drive\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Google Drive account to import files\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">\n Google Drive integration coming soon...\n </p>\n </div>\n </div>\n )}\n\n {activeMethod === \"instagram\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-pink-50 to-purple-50 ring-4 ring-pink-50/50\">\n <InstagramIcon className=\"h-6 w-6 text-pink-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Instagram\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Instagram account to import your posts\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">\n Instagram integration coming soon...\n </p>\n </div>\n </div>\n )}\n\n {activeMethod === \"facebook\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-blue-50 to-indigo-50 ring-4 ring-blue-50/50\">\n <FacebookIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Facebook\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Facebook account to import your photos\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">Facebook integration coming soon...</p>\n </div>\n </div>\n )}\n\n {activeMethod === \"tiktok\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-gray-50 to-gray-100 ring-4 ring-gray-50/50\">\n <TiktokIcon className=\"h-6 w-6 text-gray-900\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from TikTok\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your TikTok account to import your videos\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">TikTok integration coming soon...</p>\n </div>\n </div>\n )}\n\n {activeMethod === \"dropbox\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-blue-50 to-indigo-50 ring-4 ring-blue-50/50\">\n <DropboxIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Dropbox\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Dropbox account to import files\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">Dropbox integration coming soon...</p>\n </div>\n </div>\n )}\n </div>\n );\n};\n","import React, { useState, useCallback, useEffect } from \"react\";\nimport {\n XIcon,\n FileIcon,\n ImageIcon,\n VideoIcon,\n EllipsisVerticalIcon,\n CropIcon,\n LayersIcon,\n} from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Dialog,\n DialogPortal,\n DialogOverlay,\n cn,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { MobileBottomSheet } from \"@fluid-app/ui-components/components/MobileBottomSheet\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport type {\n FilePickerResult,\n DamAsset,\n DamVariant,\n} from \"@fluid-app/file-picker-core\";\nimport { ImageCropModal } from \"./ImageCropModal\";\nimport { getCategoryIcon } from \"../utils/icons\";\nimport { useIsMobile } from \"../hooks/use-is-mobile\";\n\ninterface SelectionPanelProps {\n selectedFiles: FilePickerResult[];\n onRemoveFile: (assetCode: string) => void;\n onClear: () => void;\n onConfirm: () => void;\n shareableEnabled?: boolean;\n isVisible: boolean;\n hasVariants?: (assetCode: string) => boolean;\n /** Get full asset with variants for the variant picker modal (used with onVariantChange). */\n getAssetWithVariants?: (assetCode: string) => DamAsset | null;\n /** Called when user selects a different variant in the modal. */\n onVariantChange?: (assetCode: string, variantId: string) => void;\n /** @deprecated Use getAssetWithVariants + onVariantChange and open modal from SelectionPanel. */\n onOpenVariantModal?: (assetCode: string) => void;\n onCropImage?: (assetCode: string, croppedFile: File) => void;\n}\n\nconst getFileIcon = (mimeType: string | undefined): LucideIcon => {\n if (!mimeType) return FileIcon;\n if (mimeType.startsWith(\"image/\")) return ImageIcon;\n if (mimeType.startsWith(\"video/\")) return VideoIcon;\n return FileIcon;\n};\n\nconst isImageFile = (file: FilePickerResult): boolean =>\n Boolean(file.metadata.mime_type?.startsWith(\"image/\"));\n\nexport const SelectionPanel: React.FC<SelectionPanelProps> = ({\n selectedFiles,\n onRemoveFile,\n onClear,\n onConfirm,\n shareableEnabled = false,\n isVisible,\n hasVariants,\n getAssetWithVariants,\n onVariantChange,\n onOpenVariantModal,\n onCropImage,\n}) => {\n const isMobile = useIsMobile();\n const [actionFile, setActionFile] = useState<FilePickerResult | null>(null);\n const [cropTarget, setCropTarget] = useState<{\n assetCode: string;\n file: FilePickerResult;\n } | null>(null);\n const [imageFileForCrop, setImageFileForCrop] = useState<File | null>(null);\n const [cropModalOpen, setCropModalOpen] = useState(false);\n const [variantModalAsset, setVariantModalAsset] = useState<{\n asset: DamAsset;\n assetCode: string;\n } | null>(null);\n\n const openVariantModal = useCallback(\n (assetCode: string) => {\n if (getAssetWithVariants && onVariantChange) {\n const asset = getAssetWithVariants(assetCode);\n if (asset && (asset.variants?.length ?? 0) > 1) {\n setVariantModalAsset({ asset, assetCode });\n return;\n }\n }\n onOpenVariantModal?.(assetCode);\n },\n [getAssetWithVariants, onVariantChange, onOpenVariantModal],\n );\n\n const handleVariantSelect = useCallback(\n (variant: DamVariant) => {\n if (variantModalAsset) {\n onVariantChange?.(variantModalAsset.assetCode, variant.id);\n setVariantModalAsset(null);\n }\n },\n [variantModalAsset, onVariantChange],\n );\n\n // When user clicks \"Crop image\", fetch the image and open crop modal\n useEffect(() => {\n if (!cropTarget?.file.file_url || !isImageFile(cropTarget.file)) {\n return;\n }\n let cancelled = false;\n const { file_url } = cropTarget.file;\n const fileName = cropTarget.file.metadata.file_name ?? \"image.jpg\";\n const mimeType = cropTarget.file.metadata.mime_type ?? \"image/jpeg\";\n fetch(file_url)\n .then((r) => r.blob())\n .then((blob) => {\n if (cancelled) return;\n const file = new File([blob], fileName, { type: mimeType });\n setImageFileForCrop(file);\n setCropModalOpen(true);\n })\n .catch(() => {\n if (!cancelled) setCropTarget(null);\n });\n return () => {\n cancelled = true;\n };\n }, [cropTarget]);\n\n const handleCropComplete = useCallback(\n async (croppedFile: File) => {\n if (cropTarget) {\n const result = onCropImage?.(cropTarget.assetCode, croppedFile);\n if (\n result != null &&\n typeof (result as Promise<unknown>)?.then === \"function\"\n ) {\n await (result as Promise<unknown>);\n }\n }\n setCropTarget(null);\n setImageFileForCrop(null);\n setCropModalOpen(false);\n },\n [cropTarget, onCropImage],\n );\n\n const handleCropCancel = useCallback(() => {\n setCropTarget(null);\n setImageFileForCrop(null);\n setCropModalOpen(false);\n }, []);\n\n if (!isVisible || selectedFiles.length === 0) {\n return null;\n }\n\n return (\n <div className=\"flex max-h-[45%] w-full shrink-0 flex-col border-t border-gray-200 bg-white md:h-full md:max-h-none md:w-72 md:border-t-0 md:border-l\">\n <div className=\"border-b border-gray-200 px-4 py-3\">\n <h3 className=\"text-sm font-semibold text-gray-900\">Selection</h3>\n </div>\n\n <div className=\"flex-1 overflow-y-auto p-3\">\n <div className=\"space-y-2\">\n {selectedFiles.map((file) => {\n const canCrop = isImageFile(file) && onCropImage;\n const hasVariantOption =\n hasVariants?.(file.asset_code) &&\n (onOpenVariantModal || (getAssetWithVariants && onVariantChange));\n return (\n <div\n key={file.asset_code}\n className=\"group flex items-center gap-3 rounded-lg border border-gray-100 bg-gray-50 p-2 transition-colors hover:border-gray-200 hover:bg-gray-100\"\n >\n <div className=\"relative h-10 w-10 shrink-0 overflow-hidden rounded bg-gray-200\">\n {file.file_url &&\n file.metadata.mime_type?.startsWith(\"image/\") ? (\n <img\n src={file.file_url}\n alt={file.metadata.file_name}\n width={40}\n height={40}\n className=\"h-full w-full object-cover\"\n />\n ) : file.file_url &&\n file.metadata.mime_type?.startsWith(\"video/\") ? (\n <img\n src={`${file.file_url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={file.metadata.file_name}\n width={40}\n height={40}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n {React.createElement(\n getFileIcon(file.metadata.mime_type),\n {\n className: \"h-4 w-4 text-gray-400\",\n },\n )}\n </div>\n )}\n </div>\n\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-xs font-medium text-gray-900\">\n {file.metadata.file_name}\n </p>\n <p className=\"truncate text-xs text-gray-500\">\n {file.metadata.mime_type?.split(\"/\")[1]?.toUpperCase() ||\n \"FILE\"}\n </p>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-0.5\">\n {isMobile ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`Actions for ${file.metadata.file_name}`}\n className=\"flex h-6 w-6 items-center justify-center rounded-full text-gray-400 hover:bg-gray-200 hover:text-gray-600\"\n onClick={(event) => {\n event.stopPropagation();\n setActionFile(file);\n }}\n >\n <EllipsisVerticalIcon className=\"h-3 w-3\" />\n </Button>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger\n className=\"flex h-6 w-6 items-center justify-center rounded-full text-gray-400 opacity-0 transition-all group-hover:opacity-100 hover:bg-gray-200 hover:text-gray-600\"\n onClick={(e) => e.stopPropagation()}\n >\n <EllipsisVerticalIcon className=\"h-3 w-3\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"z-350\">\n <DropdownMenuItem\n disabled={!canCrop}\n onClick={(e) => {\n e.stopPropagation();\n if (canCrop && isImageFile(file))\n setCropTarget({\n assetCode: file.asset_code,\n file,\n });\n }}\n title={\n canCrop\n ? undefined\n : \"Crop is only available for image files\"\n }\n >\n <CropIcon className=\"mr-2 h-3.5 w-3.5 text-gray-600\" />\n Crop image\n </DropdownMenuItem>\n <DropdownMenuItem\n disabled={!hasVariantOption}\n onClick={(e) => {\n e.stopPropagation();\n if (hasVariantOption)\n openVariantModal(file.asset_code);\n }}\n >\n <LayersIcon\n className={`mr-2 h-3.5 w-3.5 ${hasVariantOption ? \"text-gray-600\" : \"text-gray-400\"}`}\n />\n Change variant\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <button\n type=\"button\"\n aria-label={`Remove ${file.metadata.file_name}`}\n onClick={(e) => {\n e.stopPropagation();\n onRemoveFile(file.asset_code);\n }}\n className={cn(\n \"flex h-6 w-6 items-center justify-center rounded-full text-gray-400 transition-all hover:bg-gray-200 hover:text-gray-600\",\n !isMobile && \"opacity-0 group-hover:opacity-100\",\n )}\n >\n <XIcon className=\"h-3 w-3\" />\n </button>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <MobileActionSheet\n open={actionFile !== null}\n onOpenChange={(open) => {\n if (!open) setActionFile(null);\n }}\n title={actionFile?.metadata.file_name ?? \"File actions\"}\n actions={[\n {\n id: \"crop\",\n label: \"Crop image\",\n disabled: !actionFile || !isImageFile(actionFile) || !onCropImage,\n onSelect: () => {\n if (actionFile && isImageFile(actionFile) && onCropImage) {\n setCropTarget({\n assetCode: actionFile.asset_code,\n file: actionFile,\n });\n }\n },\n },\n {\n id: \"variant\",\n label: \"Change variant\",\n disabled:\n !actionFile ||\n !hasVariants?.(actionFile.asset_code) ||\n !(\n onOpenVariantModal ||\n (getAssetWithVariants && onVariantChange)\n ),\n onSelect: () => {\n if (actionFile) openVariantModal(actionFile.asset_code);\n },\n },\n ]}\n />\n\n <div className=\"flex items-center justify-between gap-2 border-t border-gray-200 p-4\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onClear}>\n Clear\n </Button>\n <Button variant=\"default\" size=\"sm\" onClick={onConfirm}>\n {shareableEnabled\n ? \"Next\"\n : `Use ${selectedFiles.length} File${selectedFiles.length !== 1 ? \"s\" : \"\"}`}\n </Button>\n </div>\n\n {imageFileForCrop && (\n <ImageCropModal\n isOpen={cropModalOpen}\n onClose={handleCropCancel}\n onCrop={handleCropComplete}\n imageFile={imageFileForCrop}\n />\n )}\n\n {variantModalAsset && isMobile ? (\n <MobileBottomSheet\n open={true}\n onOpenChange={(open) => !open && setVariantModalAsset(null)}\n title=\"Select variant\"\n >\n <p className=\"text-sm text-gray-500\">\n Choose a variant for &quot;\n {variantModalAsset.asset.name || \"Unknown Asset\"}&quot;\n </p>\n <div className=\"grid grid-cols-1 gap-4\">\n {variantModalAsset.asset.variants?.map((variant) => (\n <button\n key={variant.id}\n type=\"button\"\n onClick={() => handleVariantSelect(variant)}\n className=\"rounded-lg border border-gray-200 bg-white p-4 text-left\"\n >\n <p className=\"truncate font-medium text-gray-900\">\n {variant.file_name || \"Unknown File\"}\n </p>\n <p className=\"mt-1 text-xs text-gray-500\">\n {variant.mime_type || \"Unknown Type\"}\n </p>\n </button>\n ))}\n </div>\n </MobileBottomSheet>\n ) : variantModalAsset ? (\n <Dialog\n open={true}\n onOpenChange={(open) => !open && setVariantModalAsset(null)}\n >\n <DialogPortal>\n <DialogOverlay className=\"z-9999 bg-black/70 backdrop-blur-none\" />\n <DialogPrimitive.Content\n className=\"fixed top-1/2 left-1/2 z-10000 max-h-[80vh] w-full max-w-3xl -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border border-gray-200 bg-white shadow-xl\"\n style={{ zIndex: 10000 }}\n >\n <div className=\"border-b bg-white p-6\">\n <h3 className=\"text-lg font-semibold text-gray-900\">\n Select variant\n </h3>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose a variant for &quot;\n {variantModalAsset.asset.name || \"Unknown Asset\"}&quot;\n </p>\n </div>\n <div className=\"max-h-[60vh] overflow-y-auto p-6\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {variantModalAsset.asset.variants?.map((variant) => (\n <button\n key={variant?.id}\n type=\"button\"\n onClick={() => handleVariantSelect(variant)}\n className=\"group rounded-lg border border-gray-200 bg-white p-4 text-left transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex items-start gap-4\">\n <div className=\"shrink-0\">\n {variant.url &&\n variantModalAsset.asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={variant.file_name}\n width={64}\n height={64}\n className=\"h-16 w-16 rounded-md object-cover\"\n />\n ) : variantModalAsset.asset.category === \"videos\" &&\n variant?.url ? (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-md bg-gray-50\">\n <img\n src={`${variant?.url?.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={variant.file_name}\n width={64}\n height={64}\n className=\"h-16 w-16 rounded-md object-cover\"\n />\n </div>\n ) : (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-md bg-gray-50\">\n {React.createElement(\n getCategoryIcon(\n variantModalAsset.asset?.category ||\n \"unknown\",\n ),\n { className: \"h-6 w-6 text-gray-400\" },\n )}\n </div>\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-gray-900 group-hover:text-gray-700\">\n {variant?.file_name || \"Unknown File\"}\n </p>\n <p className=\"mt-1 text-xs text-gray-500\">\n {variant?.mime_type || \"Unknown Type\"}\n </p>\n {typeof variant.metadata?.width === \"number\" &&\n typeof variant.metadata?.height === \"number\" && (\n <p className=\"mt-1 text-xs font-medium text-gray-600\">\n {variant.metadata.width}px ×{\" \"}\n {variant.metadata.height}px\n </p>\n )}\n </div>\n </div>\n </button>\n ))}\n </div>\n </div>\n <div className=\"border-t bg-gray-50 p-6\">\n <div className=\"flex justify-end\">\n <Button\n variant=\"secondary\"\n onClick={() => setVariantModalAsset(null)}\n >\n Cancel\n </Button>\n </div>\n </div>\n </DialogPrimitive.Content>\n </DialogPortal>\n </Dialog>\n ) : null}\n </div>\n );\n};\n\nexport default SelectionPanel;\n","import React, {\n useState,\n useCallback,\n useEffect,\n useRef,\n useMemo,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n ImagesIcon,\n LaptopIcon,\n CloudIcon,\n LinkIcon,\n FileTextIcon,\n PackageIcon,\n GraduationCapIcon,\n} from \"lucide-react\";\nimport {\n GoogleIcon,\n InstagramIcon,\n FacebookIcon,\n TiktokIcon,\n DropboxIcon,\n UnsplashIcon,\n} from \"../utils/brand-icons\";\nimport { Dialog, DialogContent, DialogTitle } from \"@fluid-app/ui-primitives\";\nimport {\n filePickerConfigSchema,\n type FilePickerResult,\n type FilePickerConfigInput,\n type PickerMethod,\n type DamAsset,\n type SortOptionId,\n} from \"@fluid-app/file-picker-core\";\nimport { type DamLibraryRef } from \"./DamLibrary\";\nimport { FilePickerSidebar } from \"./FilePickerSidebar\";\nimport { FilePickerHeader, type ViewMode } from \"./FilePickerHeader\";\nimport { FilePickerContent } from \"./FilePickerContent\";\nimport {\n defaultFilters as defaultDamFilters,\n type FilePickerFiltersState,\n} from \"./FilePickerFiltersPanel\";\nimport { SelectionPanel } from \"./SelectionPanel\";\nimport {\n FilePickerSelectionScope,\n useFilePickerContext,\n type UnifiedSelectionApi,\n} from \"../context/FilePickerContext\";\n\nexport interface FilePickerProps {\n config?: FilePickerConfigInput;\n onFilesSelected: (results: FilePickerResult[]) => void;\n onClose?: () => void;\n open: boolean;\n showMediaTab?: boolean;\n className?: string;\n skipCropper?: boolean;\n defaultMethod?: PickerMethod;\n}\n\nexport const FilePicker: React.FC<FilePickerProps> = ({\n config: configInput = {},\n onFilesSelected,\n onClose,\n open,\n showMediaTab = false,\n className,\n skipCropper = false,\n defaultMethod,\n}) => {\n const config = useMemo(() => {\n const result = filePickerConfigSchema.safeParse(configInput);\n return result.success ? result.data : filePickerConfigSchema.parse({});\n }, [configInput]);\n\n const defaultTab = defaultMethod ?? \"dam\";\n const [activeMethod, setActiveMethod] = useState<PickerMethod>(() => {\n if (config.allowedMethods && config.allowedMethods.length > 0) {\n const allowed = config.allowedMethods as PickerMethod[];\n if (allowed.includes(defaultTab)) return defaultTab;\n return allowed[0] ?? \"dam\";\n }\n return defaultTab;\n });\n // Re-sync activeMethod if allowedMethods changes and current tab is no longer allowed\n useEffect(() => {\n if (\n config.allowedMethods &&\n config.allowedMethods.length > 0 &&\n !config.allowedMethods.includes(activeMethod)\n ) {\n setActiveMethod((config.allowedMethods[0] as PickerMethod) ?? \"dam\");\n }\n }, [config.allowedMethods, activeMethod]);\n\n const [selectedResults, setSelectedResults] = useState<FilePickerResult[]>(\n [],\n );\n const [damSelectionCount, setDamSelectionCount] = useState(0);\n const [damSelectedFiles, setDamSelectedFiles] = useState<FilePickerResult[]>(\n [],\n );\n const [damVariantCounts, setDamVariantCounts] = useState<\n Record<string, number>\n >({});\n const [damSearchQuery, setDamSearchQuery] = useState(\"\");\n const [damSearching, setDamSearching] = useState(false);\n const [mediaSearchQuery, setMediaSearchQuery] = useState(\"\");\n const [unsplashSearchQuery, setUnsplashSearchQuery] = useState(\"\");\n const [unsplashSearching, setUnsplashSearching] = useState(false);\n const [isUnsplashUploading, setIsUnsplashUploading] = useState(false);\n const [mediaSelectionCount, setMediaSelectionCount] = useState(0);\n const [damFilters, setDamFilters] =\n useState<FilePickerFiltersState>(defaultDamFilters);\n const [sortOption, setSortOption] = useState<SortOptionId | undefined>(\n undefined,\n );\n const [damFolders, setDamFolders] = useState<string[]>([]);\n const [selectedMediaIds, setSelectedMediaIds] = useState<number[]>([]);\n const [selectedMediaData, setSelectedMediaData] = useState<\n FilePickerResult[]\n >([]);\n const [selectedPageIds, setSelectedPageIds] = useState<number[]>([]);\n const [selectedPageData, setSelectedPageData] = useState<FilePickerResult[]>(\n [],\n );\n const [pageSelectionCount, setPageSelectionCount] = useState(0);\n const [selectedProductIds, setSelectedProductIds] = useState<number[]>([]);\n const [selectedProductData, setSelectedProductData] = useState<\n FilePickerResult[]\n >([]);\n const [productSelectionCount, setProductSelectionCount] = useState(0);\n const [selectedEnrollmentPackIds, setSelectedEnrollmentPackIds] = useState<\n number[]\n >([]);\n const [selectedEnrollmentPackData, setSelectedEnrollmentPackData] = useState<\n FilePickerResult[]\n >([]);\n const [enrollmentPackSelectionCount, setEnrollmentPackSelectionCount] =\n useState(0);\n // Unified selection (used when config.unifiedSelection is true)\n const [unifiedItems, setUnifiedItems] = useState<FilePickerResult[]>([]);\n const damLibraryRef = useRef<DamLibraryRef | null>(null);\n const [previewContainer, setPreviewContainer] =\n useState<HTMLDivElement | null>(null);\n const [previewAsset, setPreviewAsset] = useState<DamAsset | null>(null);\n\n // DAM toolbar state\n const [thumbnailSize, setThumbnailSize] = useState(100);\n const [viewMode, setViewMode] = useState<ViewMode>(\"grid\");\n const [showNamesOnMedia, setShowNamesOnMedia] = useState(true);\n\n const resetPicker = useCallback(() => {\n setActiveMethod(defaultTab);\n setSelectedResults([]);\n setDamSelectionCount(0);\n setDamSelectedFiles([]);\n setDamVariantCounts({});\n setDamSearchQuery(\"\");\n setMediaSearchQuery(\"\");\n setUnsplashSearchQuery(\"\");\n setUnsplashSearching(false);\n setIsUnsplashUploading(false);\n setMediaSelectionCount(0);\n setSelectedMediaIds([]);\n setSelectedMediaData([]);\n setSelectedPageIds([]);\n setSelectedPageData([]);\n setPageSelectionCount(0);\n setSelectedProductIds([]);\n setSelectedProductData([]);\n setProductSelectionCount(0);\n setSelectedEnrollmentPackIds([]);\n setSelectedEnrollmentPackData([]);\n setEnrollmentPackSelectionCount(0);\n setUnifiedItems([]);\n setDamFilters(defaultDamFilters);\n setSortOption(undefined);\n damLibraryRef.current?.clearSelection?.();\n setPreviewAsset(null);\n }, [defaultTab]);\n\n const handleMethodChange = useCallback(\n (newMethod: PickerMethod) => {\n // In unified-selection mode, switching tabs preserves the cross-tab\n // selection list — only the active tab's local UI state needs updating.\n if (!config.unifiedSelection) {\n setSelectedResults([]);\n setDamSelectionCount(0);\n setDamSelectedFiles([]);\n setDamVariantCounts({});\n setDamSearchQuery(\"\");\n setUnsplashSearchQuery(\"\");\n setUnsplashSearching(false);\n setIsUnsplashUploading(false);\n setMediaSelectionCount(0);\n setSelectedMediaIds([]);\n setSelectedMediaData([]);\n setSelectedPageIds([]);\n setSelectedPageData([]);\n setPageSelectionCount(0);\n setSelectedProductIds([]);\n setSelectedProductData([]);\n setProductSelectionCount(0);\n setSelectedEnrollmentPackIds([]);\n setSelectedEnrollmentPackData([]);\n setEnrollmentPackSelectionCount(0);\n damLibraryRef.current?.clearSelection?.();\n }\n setActiveMethod(newMethod);\n },\n [config.unifiedSelection],\n );\n\n const handleFilesSelected = useCallback(\n (results: FilePickerResult[]) => {\n const validResults = results.filter(\n (result) => result.upload_status !== \"error\",\n );\n\n if (config.maxFiles === 1) {\n setSelectedResults(validResults.slice(0, 1));\n } else {\n setSelectedResults((prev) => {\n const totalFiles = prev.length + validResults.length;\n if (config.maxFiles && totalFiles > config.maxFiles) {\n const remainingSlots = config.maxFiles - prev.length;\n return [...prev, ...validResults.slice(0, remainingSlots)];\n }\n return [...prev, ...validResults];\n });\n }\n },\n [config.maxFiles],\n );\n\n const handleUploadConfirm = useCallback(\n (results: FilePickerResult[]) => {\n const validResults = results.filter(\n (result) => result.upload_status !== \"error\",\n );\n if (validResults.length === 0) return;\n onFilesSelected(validResults);\n onClose?.();\n },\n [onFilesSelected, onClose],\n );\n\n const handleSingleFileSelected = useCallback(\n (result: FilePickerResult) => {\n handleFilesSelected([result]);\n },\n [handleFilesSelected],\n );\n\n const handleDamSelectionChange = useCallback(\n (\n count: number,\n files?: FilePickerResult[],\n variantCounts?: Record<string, number>,\n ) => {\n setDamSelectionCount(count);\n setDamSelectedFiles(files ?? []);\n setDamVariantCounts(variantCounts ?? {});\n },\n [],\n );\n\n const handleMediaSelectionChange = useCallback((count: number) => {\n setMediaSelectionCount(count);\n }, []);\n\n const handleMediaDataChange = useCallback((data: FilePickerResult[]) => {\n setSelectedMediaData(data);\n }, []);\n\n const handleThumbnailSizeChange = useCallback((size: number) => {\n setThumbnailSize(size);\n }, []);\n\n const handleViewModeChange = useCallback((mode: ViewMode) => {\n setViewMode(mode);\n }, []);\n\n const handleDamConfirm = useCallback(\n (results: FilePickerResult[]) => {\n onFilesSelected(results);\n onClose?.();\n },\n [onFilesSelected, onClose],\n );\n\n const { shareablesClient, toast } = useFilePickerContext();\n\n const methods: Array<{\n id: PickerMethod;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n description: string;\n }> = useMemo(() => {\n const allMethods = [\n {\n id: \"dam\" as PickerMethod,\n label: \"Library\",\n icon: CloudIcon,\n description: \"Choose from DAM library\",\n },\n ...(showMediaTab ||\n (config.allowedMethods && config.allowedMethods.includes(\"media\"))\n ? [\n {\n id: \"media\" as PickerMethod,\n label: \"Media\",\n icon: ImagesIcon,\n description: \"Choose from existing media\",\n },\n ]\n : []),\n {\n id: \"pages\" as PickerMethod,\n label: \"Pages\",\n icon: FileTextIcon,\n description: \"Choose from existing pages\",\n },\n {\n id: \"products\" as PickerMethod,\n label: \"Products\",\n icon: PackageIcon,\n description: \"Choose from existing products\",\n },\n {\n id: \"enrollment_packs\" as PickerMethod,\n label: \"Enrollment Packs\",\n icon: GraduationCapIcon,\n description: \"Choose from existing enrollment packs\",\n },\n {\n id: \"upload\" as PickerMethod,\n label: \"Local Files\",\n icon: LaptopIcon,\n description: \"Upload files from computer or URL\",\n },\n {\n id: \"url\" as PickerMethod,\n label: \"URL\",\n icon: LinkIcon,\n description: \"Upload files from web URLs\",\n },\n {\n id: \"unsplash\" as PickerMethod,\n label: \"Unsplash\",\n icon: UnsplashIcon,\n description: \"Search Unsplash images\",\n },\n {\n id: \"google-drive\" as PickerMethod,\n label: \"Google Drive\",\n icon: GoogleIcon,\n description: \"Import from Google Drive\",\n },\n {\n id: \"instagram\" as PickerMethod,\n label: \"Instagram\",\n icon: InstagramIcon,\n description: \"Import from Instagram\",\n },\n {\n id: \"facebook\" as PickerMethod,\n label: \"Facebook\",\n icon: FacebookIcon,\n description: \"Import from Facebook\",\n },\n {\n id: \"tiktok\" as PickerMethod,\n label: \"TikTok\",\n icon: TiktokIcon,\n description: \"Import from TikTok\",\n },\n {\n id: \"dropbox\" as PickerMethod,\n label: \"Dropbox\",\n icon: DropboxIcon,\n description: \"Import from Dropbox\",\n },\n ];\n\n // Hide content-browser tabs when host hasn't supplied a data source.\n // This is the contract for \"tab is opt-in per host\" — extension methods\n // appear only when the host's shareablesClient declares the matching\n // listing function.\n const filteredBySource = allMethods.filter((m) => {\n if (m.id === \"pages\") return shareablesClient?.pages != null;\n if (m.id === \"products\") return shareablesClient?.products != null;\n if (m.id === \"enrollment_packs\")\n return shareablesClient?.enrollment_packs != null;\n return true;\n });\n\n if (config.allowedMethods && config.allowedMethods.length > 0) {\n return filteredBySource.filter((method) =>\n config.allowedMethods!.includes(method.id),\n );\n }\n\n return filteredBySource;\n }, [showMediaTab, config.allowedMethods, shareablesClient]);\n\n const showPreview = previewAsset !== null;\n\n // Unified selection API — built once and passed to FilePickerSelectionScope\n // so descendant tabs can read/write through context. Only relevant when\n // config.unifiedSelection is true.\n // Memoized so the FilePickerSelectionScope provider value's identity is\n // stable when unifiedItems hasn't changed — without this, every state\n // update in FilePicker (e.g. setActiveMethod) would re-render every\n // FilePickerSelectionReadContext consumer (the new tab bodies).\n const unifiedSelectionState = useMemo(\n () => ({ items: unifiedItems }),\n [unifiedItems],\n );\n\n // Ref-tracked items so the unifiedApi object identity stays stable across\n // selection changes. isSelected/selectionIndex are only called from event\n // handlers — never during render — so reading from the ref is safe; the\n // render path reads selection from unifiedState directly.\n const unifiedItemsRef = useRef(unifiedItems);\n useEffect(() => {\n unifiedItemsRef.current = unifiedItems;\n }, [unifiedItems]);\n\n const unifiedApi: UnifiedSelectionApi = useMemo(\n () => ({\n add: (item) => {\n // Decide synchronously against the ref so the limit toast actually\n // fires — React's functional setter runs the updater later (during\n // commit), so reading `hitLimit` after `setUnifiedItems(...)` would\n // always observe the pre-update value. The setter still does its\n // own dedupe/cap check as a defense against rapid double-clicks.\n const current = unifiedItemsRef.current;\n if (current.some((r) => r.asset_code === item.asset_code)) return;\n if (config.maxFiles && current.length >= config.maxFiles) {\n toast.error(\n `You can select up to ${config.maxFiles} items at a time.`,\n );\n return;\n }\n setUnifiedItems((prev) => {\n if (prev.some((r) => r.asset_code === item.asset_code)) return prev;\n if (config.maxFiles && prev.length >= config.maxFiles) return prev;\n return [...prev, item];\n });\n },\n remove: (assetCode) => {\n setUnifiedItems((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n },\n clear: () => setUnifiedItems([]),\n isSelected: (assetCode) =>\n unifiedItemsRef.current.some((r) => r.asset_code === assetCode),\n selectionIndex: (assetCode) =>\n unifiedItemsRef.current.findIndex((r) => r.asset_code === assetCode) +\n 1,\n }),\n [config.maxFiles, toast],\n );\n\n // Body assembled as a JSX value (not a component type) so the reconciler\n // doesn't remount the entire subtree on every state update. A nested\n // `function FilePickerBody()` here would create a new component\n // identity each render, unmounting ContentBrowserTab and clearing its\n // search field on every selection change.\n const body = (\n <div className=\"flex flex-1 overflow-y-auto\">\n <FilePickerSidebar\n currentStep=\"select\"\n activeMethod={activeMethod}\n onMethodChange={handleMethodChange}\n methods={methods}\n />\n\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n <FilePickerHeader\n currentStep=\"select\"\n activeMethod={activeMethod}\n methods={methods}\n onMethodChange={handleMethodChange}\n onClose={() => {\n resetPicker();\n onClose?.();\n }}\n damSearchQuery={\n activeMethod === \"dam\" ? damSearchQuery : mediaSearchQuery\n }\n onDamSearchChange={\n activeMethod === \"dam\" ? setDamSearchQuery : setMediaSearchQuery\n }\n damSearching={damSearching}\n thumbnailSize={thumbnailSize}\n onThumbnailSizeChange={handleThumbnailSizeChange}\n viewMode={viewMode}\n onViewModeChange={handleViewModeChange}\n showNamesOnMedia={showNamesOnMedia}\n onShowNamesOnMediaChange={setShowNamesOnMedia}\n hasSelectedFiles={damSelectionCount > 0}\n damFolders={damFolders}\n onAddToNewFolder={() => damLibraryRef.current?.openAddFolderModal()}\n onAddToExistingFolder={(folderPath) =>\n damLibraryRef.current?.addToExistingFolder?.(folderPath)\n }\n filters={damFilters}\n onFiltersChange={setDamFilters}\n onFiltersClear={() => setDamFilters(defaultDamFilters)}\n sortOption={sortOption}\n onSortChange={setSortOption}\n unsplashSearchQuery={unsplashSearchQuery}\n onUnsplashSearchChange={setUnsplashSearchQuery}\n unsplashSearching={unsplashSearching}\n isUnsplashUploading={isUnsplashUploading}\n />\n {/* Stacked below `md` so the selection panel docks under the grid\n instead of taking a fixed rail out of an already narrow screen. */}\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden md:flex-row\">\n <div className=\"min-h-0 min-w-0 flex-1 overflow-y-auto\">\n <FilePickerContent\n activeMethod={activeMethod}\n config={config}\n damTypeFilter={damFilters.type ? damFilters.type : undefined}\n sortOption={sortOption}\n onFilesSelected={handleFilesSelected}\n onUploadConfirmAndMaybeCreate={handleUploadConfirm}\n onSingleFileSelected={handleSingleFileSelected}\n damLibraryRef={damLibraryRef}\n onDamConfirmAndMaybeCreate={handleDamConfirm}\n selectedMediaIds={selectedMediaIds}\n onMediaIdsChange={setSelectedMediaIds}\n onMediaSelectionChange={handleMediaSelectionChange}\n onMediaDataChange={handleMediaDataChange}\n selectedPageIds={selectedPageIds}\n onPageIdsChange={setSelectedPageIds}\n onPageSelectionChange={setPageSelectionCount}\n onPageDataChange={setSelectedPageData}\n selectedProductIds={selectedProductIds}\n onProductIdsChange={setSelectedProductIds}\n onProductSelectionChange={setProductSelectionCount}\n onProductDataChange={setSelectedProductData}\n selectedEnrollmentPackIds={selectedEnrollmentPackIds}\n onEnrollmentPackIdsChange={setSelectedEnrollmentPackIds}\n onEnrollmentPackSelectionChange={setEnrollmentPackSelectionCount}\n onEnrollmentPackDataChange={setSelectedEnrollmentPackData}\n onDamSelectionChange={handleDamSelectionChange}\n onDamSearchingChange={setDamSearching}\n skipCropper={skipCropper}\n damSearchQuery={damSearchQuery}\n onDamSearchChange={setDamSearchQuery}\n mediaSearchQuery={mediaSearchQuery}\n onMediaSearchChange={setMediaSearchQuery}\n unsplashSearchQuery={unsplashSearchQuery}\n onUnsplashSearchChange={setUnsplashSearchQuery}\n onUnsplashSearchingChange={setUnsplashSearching}\n onUnsplashUploadingChange={setIsUnsplashUploading}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n onDamFoldersChange={setDamFolders}\n previewContainer={previewContainer}\n previewAsset={previewAsset}\n onPreviewAssetChange={setPreviewAsset}\n uploadSelectionCount={\n activeMethod === \"upload\" ||\n activeMethod === \"url\" ||\n activeMethod === \"unsplash\"\n ? selectedResults.length\n : undefined\n }\n />\n </div>\n {activeMethod === \"dam\" && (\n <SelectionPanel\n selectedFiles={damSelectedFiles}\n onRemoveFile={(assetCode) => {\n damLibraryRef.current?.deselectAssetByCode?.(assetCode);\n }}\n onClear={() => {\n damLibraryRef.current?.clearSelection();\n setDamSelectionCount(0);\n setDamSelectedFiles([]);\n setDamVariantCounts({});\n }}\n onConfirm={() => {\n damLibraryRef.current?.confirmAndClose();\n }}\n isVisible={damSelectionCount > 0}\n hasVariants={(assetCode) =>\n (damVariantCounts[assetCode] ?? 0) > 1\n }\n getAssetWithVariants={(assetCode) =>\n damLibraryRef.current?.getAssetWithVariants?.(assetCode) ?? null\n }\n onVariantChange={(assetCode, variantId) => {\n damLibraryRef.current?.setVariantForAsset?.(\n assetCode,\n variantId,\n );\n }}\n />\n )}\n {!config.unifiedSelection && activeMethod === \"media\" && (\n <SelectionPanel\n selectedFiles={selectedMediaData}\n onRemoveFile={(assetCode) => {\n const id = parseInt(assetCode.replace(/^media_/, \"\"), 10);\n if (!Number.isNaN(id)) {\n setSelectedMediaIds((prev) =>\n prev.filter((mediaId) => mediaId !== id),\n );\n setSelectedMediaData((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }\n }}\n onClear={() => {\n setSelectedMediaIds([]);\n setSelectedMediaData([]);\n setMediaSelectionCount(0);\n }}\n onConfirm={() => {\n onFilesSelected(selectedMediaData);\n onClose?.();\n }}\n isVisible={mediaSelectionCount > 0}\n hasVariants={() => false}\n />\n )}\n {!config.unifiedSelection && activeMethod === \"pages\" && (\n <SelectionPanel\n selectedFiles={selectedPageData}\n onRemoveFile={(assetCode) => {\n const id = parseInt(assetCode.replace(/^page_/, \"\"), 10);\n if (!Number.isNaN(id)) {\n setSelectedPageIds((prev) =>\n prev.filter((pageId) => pageId !== id),\n );\n setSelectedPageData((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }\n }}\n onClear={() => {\n setSelectedPageIds([]);\n setSelectedPageData([]);\n setPageSelectionCount(0);\n }}\n onConfirm={() => {\n onFilesSelected(selectedPageData);\n onClose?.();\n }}\n isVisible={pageSelectionCount > 0}\n hasVariants={() => false}\n />\n )}\n {!config.unifiedSelection && activeMethod === \"products\" && (\n <SelectionPanel\n selectedFiles={selectedProductData}\n onRemoveFile={(assetCode) => {\n const id = parseInt(assetCode.replace(/^product_/, \"\"), 10);\n if (!Number.isNaN(id)) {\n setSelectedProductIds((prev) =>\n prev.filter((productId) => productId !== id),\n );\n setSelectedProductData((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }\n }}\n onClear={() => {\n setSelectedProductIds([]);\n setSelectedProductData([]);\n setProductSelectionCount(0);\n }}\n onConfirm={() => {\n onFilesSelected(selectedProductData);\n onClose?.();\n }}\n isVisible={productSelectionCount > 0}\n hasVariants={() => false}\n />\n )}\n {!config.unifiedSelection && activeMethod === \"enrollment_packs\" && (\n <SelectionPanel\n selectedFiles={selectedEnrollmentPackData}\n onRemoveFile={(assetCode) => {\n const id = parseInt(\n assetCode.replace(/^enrollment_pack_/, \"\"),\n 10,\n );\n if (!Number.isNaN(id)) {\n setSelectedEnrollmentPackIds((prev) =>\n prev.filter((packId) => packId !== id),\n );\n setSelectedEnrollmentPackData((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }\n }}\n onClear={() => {\n setSelectedEnrollmentPackIds([]);\n setSelectedEnrollmentPackData([]);\n setEnrollmentPackSelectionCount(0);\n }}\n onConfirm={() => {\n onFilesSelected(selectedEnrollmentPackData);\n onClose?.();\n }}\n isVisible={enrollmentPackSelectionCount > 0}\n hasVariants={() => false}\n />\n )}\n {!config.unifiedSelection &&\n (activeMethod === \"upload\" ||\n activeMethod === \"url\" ||\n activeMethod === \"unsplash\") && (\n <SelectionPanel\n selectedFiles={selectedResults}\n onRemoveFile={(assetCode) => {\n setSelectedResults((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }}\n onClear={() => {\n setSelectedResults([]);\n }}\n onConfirm={() => {\n handleUploadConfirm(selectedResults);\n }}\n isVisible={selectedResults.length > 0}\n hasVariants={() => false}\n />\n )}\n {config.unifiedSelection && (\n <SelectionPanel\n selectedFiles={unifiedItems}\n onRemoveFile={(assetCode) => {\n setUnifiedItems((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }}\n onClear={() => {\n setUnifiedItems([]);\n }}\n onConfirm={() => {\n const selectedItems = [...unifiedItems];\n onFilesSelected(selectedItems);\n resetPicker();\n onClose?.();\n }}\n isVisible={unifiedItems.length > 0}\n hasVariants={() => false}\n />\n )}\n </div>\n </div>\n </div>\n );\n\n return (\n <>\n {typeof document !== \"undefined\" &&\n createPortal(\n <div\n ref={setPreviewContainer}\n className=\"fixed inset-0\"\n style={{\n zIndex: 9999,\n pointerEvents: showPreview ? \"auto\" : \"none\",\n }}\n aria-hidden={!showPreview}\n />,\n document.body,\n )}\n <Dialog\n open={open}\n onOpenChange={(isOpen) => {\n if (!isOpen) {\n resetPicker();\n onClose?.();\n }\n }}\n >\n <DialogContent\n // The header carries its own close button, so suppress the dialog's:\n // both sit top-right, and full-bleed on mobile stacks them.\n showCloseButton={false}\n // `w-full` rather than `w-screen`: viewport units are sized as if\n // scrollbars don't exist, so `100vw` overflows by the scrollbar on a\n // narrow desktop window. A fixed element's 100% already excludes it.\n className={`data-[state=closed]:animate-out! data-[state=closed]:fade-out-0! data-[state=closed]:zoom-out-95! data-[state=open]:animate-in! data-[state=open]:fade-in-0! data-[state=open]:zoom-in-95! z-300! flex! h-dvh! max-h-none! w-full! max-w-none! flex-col! gap-0! overflow-y-auto! rounded-none! border-0! bg-white p-0! data-[state=closed]:duration-150! data-[state=open]:duration-200! md:h-[90vh]! md:max-h-[1000px]! md:w-[95vw]! md:max-w-[1600px]! md:rounded-lg! ${className || \"\"}`}\n >\n <DialogTitle className=\"sr-only\">File Picker</DialogTitle>\n {config.unifiedSelection ? (\n <FilePickerSelectionScope\n state={unifiedSelectionState}\n api={unifiedApi}\n >\n {body}\n </FilePickerSelectionScope>\n ) : (\n body\n )}\n </DialogContent>\n </Dialog>\n </>\n );\n};\n","import { useState, useEffect, useCallback } from \"react\";\n\nexport interface ThumbnailMeta {\n width: number;\n height: number;\n size: string | null;\n}\n\nexport function useThumbnailMeta(src: string | null | undefined) {\n const [meta, setMeta] = useState<ThumbnailMeta | null>(null);\n const [loadError, setLoadError] = useState(false);\n\n const fileName = src?.split(\"/\").pop()?.split(\"?\")[0] ?? \"thumbnail\";\n\n const handleLoad = useCallback(\n (e: React.SyntheticEvent<HTMLImageElement>) => {\n setLoadError(false);\n const img = e.currentTarget;\n setMeta((prev) => ({\n width: img.naturalWidth,\n height: img.naturalHeight,\n size: prev?.size ?? null,\n }));\n },\n [],\n );\n\n const handleError = useCallback(() => {\n setLoadError(true);\n }, []);\n\n useEffect(() => {\n if (!src) {\n setMeta(null);\n return;\n }\n setMeta(null);\n const controller = new AbortController();\n fetch(src, { method: \"HEAD\", signal: controller.signal })\n .then((res) => {\n if (!res.ok) return;\n const len = res.headers.get(\"content-length\");\n if (len) {\n const bytes = Number(len);\n const kb = bytes / 1024;\n const sizeStr =\n kb >= 1024 ? `${(kb / 1024).toFixed(1)} MB` : `${kb.toFixed(1)} KB`;\n setMeta((prev) => ({\n width: prev?.width ?? 0,\n height: prev?.height ?? 0,\n size: sizeStr,\n }));\n }\n })\n .catch((err: unknown) => {\n if (err instanceof DOMException && err.name === \"AbortError\") return;\n console.warn(\"[useThumbnailMeta] HEAD failed:\", err);\n });\n return () => controller.abort();\n }, [src]);\n\n useEffect(() => {\n setLoadError(false);\n }, [src]);\n\n return { meta, fileName, handleLoad, handleError, loadError };\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useEffect } from \"react\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Underline from \"@tiptap/extension-underline\";\nimport {\n AlignLeft,\n AlignCenter,\n AlignRight,\n AlignJustify,\n List,\n ListOrdered,\n} from \"lucide-react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport interface RichTextEditorProps {\n value: string;\n onChange: (html: string) => void;\n placeholder?: string;\n className?: string;\n editorClassName?: string;\n}\n\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n className,\n editorClassName,\n}: RichTextEditorProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n bulletList: { keepMarks: true, keepAttributes: false },\n orderedList: { keepMarks: true, keepAttributes: false },\n }),\n Placeholder.configure({\n placeholder: placeholder ?? t(\"common_start_writing_placeholder\"),\n }),\n TextAlign.configure({\n types: [\"paragraph\"],\n alignments: [\"left\", \"center\", \"right\", \"justify\"],\n }),\n Underline,\n ],\n content: value || \"\",\n onUpdate: ({ editor }) => {\n onChange(editor.getHTML());\n },\n });\n\n useEffect(() => {\n if (!editor) return;\n if (editor.getHTML() !== value) {\n editor.commands.setContent(value || \"\", false);\n }\n }, [editor, value]);\n\n const buttonBase =\n \"flex h-7 w-7 items-center justify-center rounded text-xs transition-colors\";\n const buttonActive = \"bg-gray-100 text-primary\";\n const buttonInactive = \"text-gray-400 hover:bg-gray-50\";\n const separator = <div className=\"mx-1 h-5 w-px bg-gray-200\" />;\n\n return (\n <div\n className={cn(\n \"border-border flex flex-col overflow-hidden rounded-lg border\",\n className,\n )}\n >\n {/* Toolbar */}\n <div className=\"border-border flex items-center gap-0.5 border-b bg-gray-50 px-2 py-1.5\">\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleBold().run()}\n disabled={!editor?.can().chain().focus().toggleBold().run()}\n className={cn(\n buttonBase,\n \"font-bold\",\n editor?.isActive(\"bold\") ? buttonActive : buttonInactive,\n )}\n title={t(\"editor_bold\")}\n >\n B\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleItalic().run()}\n disabled={!editor?.can().chain().focus().toggleItalic().run()}\n className={cn(\n buttonBase,\n \"italic\",\n editor?.isActive(\"italic\") ? buttonActive : buttonInactive,\n )}\n title={t(\"editor_italic\")}\n >\n I\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleUnderline().run()}\n disabled={!editor?.can().chain().focus().toggleUnderline().run()}\n className={cn(\n buttonBase,\n \"underline\",\n editor?.isActive(\"underline\") ? buttonActive : buttonInactive,\n )}\n title={t(\"editor_underline\")}\n >\n U\n </button>\n {separator}\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"left\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"left\" })\n ? buttonActive\n : buttonInactive,\n )}\n title={t(\"editor_align_left\")}\n >\n <AlignLeft className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"center\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"center\" })\n ? buttonActive\n : buttonInactive,\n )}\n title={t(\"editor_align_center\")}\n >\n <AlignCenter className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"right\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"right\" })\n ? buttonActive\n : buttonInactive,\n )}\n title={t(\"editor_align_right\")}\n >\n <AlignRight className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"justify\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"justify\" })\n ? buttonActive\n : buttonInactive,\n )}\n title={t(\"editor_justify\")}\n >\n <AlignJustify className=\"size-3.5\" />\n </button>\n {separator}\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleBulletList().run()}\n className={cn(\n buttonBase,\n editor?.isActive(\"bulletList\") ? buttonActive : buttonInactive,\n )}\n title={t(\"editor_bullet_list\")}\n >\n <List className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleOrderedList().run()}\n className={cn(\n buttonBase,\n editor?.isActive(\"orderedList\") ? buttonActive : buttonInactive,\n )}\n title={t(\"editor_ordered_list\")}\n >\n <ListOrdered className=\"size-3.5\" />\n </button>\n </div>\n\n {/* Editor area */}\n <EditorContent\n editor={editor}\n className={cn(\"flex-1 overflow-y-auto p-4\", editorClassName)}\n />\n\n <style>{`\n .ProseMirror {\n outline: none;\n }\n .ProseMirror p.is-empty::before {\n color: #9ca3af;\n content: attr(data-placeholder);\n float: left;\n height: 0;\n pointer-events: none;\n }\n .ProseMirror ul {\n list-style-type: disc;\n padding-left: 1.5rem;\n margin: 0.5rem 0;\n }\n .ProseMirror ol {\n list-style-type: decimal;\n padding-left: 1.5rem;\n margin: 0.5rem 0;\n }\n .ProseMirror li {\n margin: 0.25rem 0;\n display: list-item;\n }\n .ProseMirror li > p {\n margin: 0;\n }\n `}</style>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport { Button, Input, Spinner } from \"@fluid-app/ui-primitives\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { Package, Plus, Search } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\n\ninterface TaggedProduct {\n id: number;\n name?: string;\n slug?: string | null;\n image_url?: string | null;\n price?: string | null;\n sku?: string | null;\n}\n\ninterface SearchResult {\n id: number;\n name?: string;\n image_url?: string | null;\n price?: string | null;\n}\n\nexport interface MediaProductTaggingProps {\n products: TaggedProduct[];\n onAddProduct: (productId: number) => Promise<void>;\n onRemoveProduct: (productId: number) => Promise<void>;\n onSearchProducts: (\n query: string,\n options?: { cursor?: string | null; limit?: number },\n ) => Promise<{\n products: SearchResult[];\n nextCursor?: string | null;\n }>;\n disabled?: boolean;\n maxProducts?: number;\n}\n\nconst PAGE_SIZE = 25;\n\nfunction ProductImage({\n imageUrl,\n name,\n}: {\n imageUrl?: string | null;\n name?: string;\n}) {\n const { t } = useShareablesTranslation();\n const [errored, setErrored] = useState(false);\n\n if (imageUrl && imageUrl.length > 0 && !errored) {\n return (\n <img\n src={imageUrl}\n alt={name ?? t(\"common_product\")}\n width={40}\n height={40}\n className=\"size-10 shrink-0 rounded object-cover\"\n onError={() => setErrored(true)}\n />\n );\n }\n\n return (\n <div className=\"flex size-10 shrink-0 items-center justify-center rounded bg-gray-100\">\n <Package className=\"size-5 text-gray-400\" />\n </div>\n );\n}\n\nexport function MediaProductTagging({\n products,\n onAddProduct,\n onRemoveProduct,\n onSearchProducts,\n disabled = false,\n maxProducts = 100,\n}: MediaProductTaggingProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const { showToast } = useShareablesUI();\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [searchResults, setSearchResults] = useState<SearchResult[]>([]);\n const [searching, setSearching] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const [nextCursor, setNextCursor] = useState<string | null>(null);\n const [removingId, setRemovingId] = useState<number | null>(null);\n const [addingId, setAddingId] = useState<number | null>(null);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const loadMoreSentinelRef = useRef<HTMLDivElement | null>(null);\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n\n const taggedIds = useMemo(\n () => new Set(products.map((p) => p.id)),\n [products],\n );\n const isAtMaxLimit = products.length >= maxProducts;\n\n const runSearch = useCallback(\n async (q: string) => {\n setSearching(true);\n try {\n const { products: results, nextCursor: cursor } =\n await onSearchProducts(q, { limit: PAGE_SIZE });\n setSearchResults(results);\n setNextCursor(cursor ?? null);\n } catch (error) {\n setSearchResults([]);\n setNextCursor(null);\n showToast({\n title: t(\"products_load_error\"),\n type: \"error\",\n error,\n });\n } finally {\n setSearching(false);\n }\n },\n [onSearchProducts, showToast],\n );\n\n const loadMore = useCallback(async () => {\n if (!nextCursor || loadingMore || searching) return;\n setLoadingMore(true);\n try {\n const { products: results, nextCursor: cursor } = await onSearchProducts(\n query,\n { cursor: nextCursor, limit: PAGE_SIZE },\n );\n setSearchResults((prev) => [...prev, ...results]);\n setNextCursor(cursor ?? null);\n } catch (error) {\n // Stop further auto-fetch attempts for this query; user can scroll-up\n // and -back-down to retry or refine the query to reset the cursor.\n setNextCursor(null);\n showToast({\n title: t(\"products_load_more_error\"),\n type: \"error\",\n error,\n });\n } finally {\n setLoadingMore(false);\n }\n }, [nextCursor, loadingMore, searching, onSearchProducts, query, showToast]);\n\n useEffect(() => {\n // Intentionally runs on modal open with an empty query so users see\n // initial product suggestions (backend returns a default list) rather\n // than an empty state. Greptile flagged this previously, but product\n // direction is: show defaults on open, refine as the user types.\n if (!isModalOpen) return;\n\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n\n debounceRef.current = setTimeout(() => {\n void runSearch(query);\n }, 300);\n\n return () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n };\n }, [query, isModalOpen, runSearch]);\n\n // Infinite scroll — load the next page when the sentinel becomes visible\n // within the scroll container. Must pass `root` explicitly because the\n // sentinel lives inside an overflow:auto container, not the viewport.\n useEffect(() => {\n if (!isModalOpen) return;\n const sentinel = loadMoreSentinelRef.current;\n const root = scrollContainerRef.current;\n if (!sentinel || !root) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting) void loadMore();\n },\n { root, rootMargin: \"100px\" },\n );\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [isModalOpen, loadMore, searchResults.length]);\n\n function handleOpenModal() {\n setQuery(\"\");\n setSearchResults([]);\n setIsModalOpen(true);\n }\n\n async function handleAdd(productId: number) {\n setAddingId(productId);\n try {\n await onAddProduct(productId);\n void runSearch(query);\n } catch (error) {\n // Defensive — host apps are expected to toast from their own handler,\n // but if they re-throw we still surface a message here so the spinner\n // doesn't just reset with no explanation.\n showToast({\n title: t(\"products_add_error\"),\n type: \"error\",\n error,\n });\n } finally {\n setAddingId(null);\n }\n }\n\n async function handleRemove(productId: number) {\n setRemovingId(productId);\n try {\n await onRemoveProduct(productId);\n } catch (error) {\n showToast({\n title: t(\"products_remove_error\"),\n type: \"error\",\n error,\n });\n } finally {\n setRemovingId(null);\n }\n }\n\n return (\n <div className=\"border-border bg-card rounded-lg border p-5\">\n <h3 className=\"text-foreground mb-4 text-base font-semibold\">\n {t(\"media_tagged_products_heading\")}\n </h3>\n\n <div className=\"space-y-3\">\n {products.length === 0 ? (\n /* Empty state — matches admin: dashed border + centered button */\n <div className=\"rounded-lg border border-dashed border-gray-200 p-4 text-center\">\n <p className=\"mb-3 text-sm text-gray-500\">\n {t(\"media_no_products_tagged\")}\n </p>\n {!disabled && (\n <Button\n type=\"button\"\n onClick={handleOpenModal}\n size=\"sm\"\n className=\"gap-1.5\"\n >\n <Plus className=\"size-3\" />\n {t(\"media_add_product_button\")}\n </Button>\n )}\n </div>\n ) : (\n <div className=\"relative space-y-4\">\n {/* Counter + Add button row */}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-gray-500\">\n {t(\"media_products_selected_count\", {\n current: String(products.length),\n max: String(maxProducts),\n })}\n </span>\n {!disabled && !isAtMaxLimit && (\n <Button\n type=\"button\"\n onClick={handleOpenModal}\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-1.5 text-xs\"\n >\n <Plus className=\"size-3\" />\n {t(\"media_add_products_button\")}\n </Button>\n )}\n </div>\n\n <p className=\"text-xs text-gray-500\">\n {t(\"media_products_country_note\")}\n </p>\n\n {/* Product list — matches admin: card per product */}\n <div className=\"max-h-96 space-y-2 overflow-y-auto pb-2\">\n {products.map((product) => (\n <div\n key={product.id}\n className=\"flex items-center justify-between rounded-lg border border-gray-200 bg-white p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <ProductImage\n imageUrl={product.image_url}\n name={product.name}\n />\n <div>\n <p className=\"text-sm font-medium text-gray-900\">\n {product.name ??\n t(\"media_product_fallback_name\", {\n id: String(product.id),\n })}\n </p>\n <p className=\"text-xs text-gray-500\">\n {product.price ? `$${product.price}` : t(\"common_na\")}\n </p>\n </div>\n </div>\n {!disabled && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handleRemove(product.id)}\n disabled={removingId === product.id}\n className=\"text-xs text-red-600 hover:bg-red-50 hover:text-red-700\"\n >\n {removingId === product.id\n ? t(\"common_removing\")\n : t(\"common_remove\")}\n </Button>\n )}\n </div>\n ))}\n </div>\n\n {isAtMaxLimit && (\n <p className=\"py-2 text-center text-xs text-gray-500\">\n {t(\"media_max_products_reached\", { max: String(maxProducts) })}\n </p>\n )}\n </div>\n )}\n </div>\n\n {/* Product Selection Modal — matches admin layout */}\n <ResponsiveDialog\n open={isModalOpen}\n onOpenChange={setIsModalOpen}\n title={t(\"media_select_products_title\")}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n size=\"wide\"\n >\n <p className=\"text-muted-foreground text-sm\">\n {t(\"media_products_selected_ratio\", {\n current: String(products.length),\n max: String(maxProducts),\n })}\n </p>\n\n {isAtMaxLimit && (\n <div className=\"rounded-md border border-yellow-200 bg-yellow-50 p-3\">\n <p className=\"text-sm text-yellow-800\">\n {t(\"media_max_products_warning\", { max: String(maxProducts) })}\n </p>\n </div>\n )}\n\n {/* Search */}\n <div className=\"relative\">\n {searching ? (\n <Spinner className=\"absolute top-1/2 left-3 size-4 -translate-y-1/2\" />\n ) : (\n <Search className=\"absolute top-1/2 left-3 size-4 -translate-y-1/2 text-gray-400\" />\n )}\n <Input\n placeholder={t(\"products_search_placeholder\")}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n\n {/* Results */}\n <div ref={scrollContainerRef} className=\"max-h-96 overflow-y-auto\">\n {searching && searchResults.length === 0 ? (\n <div className=\"flex items-center justify-center py-8\">\n <Spinner className=\"size-5\" />\n </div>\n ) : searchResults.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-gray-500\">\n {query.length > 0\n ? t(\"products_search_no_results\")\n : t(\"products_search_start_typing\")}\n </p>\n ) : (\n <div className=\"space-y-2 px-1\">\n {/* Render an initial slot so the fragment is grouped — the\n infinite-scroll sentinel appears after the list. */}\n {searchResults.map((result) => {\n const isTagged = taggedIds.has(result.id);\n const canAdd = !isTagged && !isAtMaxLimit;\n\n return (\n <div\n key={result.id}\n className={`flex items-center justify-between rounded-lg border p-3 ${\n isTagged\n ? \"border-green-200 bg-green-50\"\n : canAdd\n ? \"border-gray-200 hover:border-gray-300\"\n : \"border-gray-100 bg-gray-50\"\n }`}\n >\n <div className=\"flex items-center gap-3\">\n <ProductImage\n imageUrl={result.image_url}\n name={result.name}\n />\n <div>\n <p\n className={`text-sm font-medium ${\n canAdd ? \"text-gray-900\" : \"text-gray-500\"\n }`}\n >\n {result.name ??\n t(\"media_product_fallback_name\", {\n id: String(result.id),\n })}\n </p>\n {result.price && (\n <p className=\"text-xs text-gray-500\">\n ${result.price}\n </p>\n )}\n </div>\n </div>\n <Button\n type=\"button\"\n variant={isTagged ? \"ghost\" : \"outline\"}\n size=\"sm\"\n onClick={() => {\n if (canAdd) void handleAdd(result.id);\n }}\n disabled={!canAdd || addingId === result.id}\n className={isTagged ? \"text-green-600\" : \"\"}\n >\n {addingId === result.id\n ? t(\"common_adding\")\n : isTagged\n ? t(\"common_added\")\n : canAdd\n ? t(\"common_add\")\n : t(\"media_limit_reached\")}\n </Button>\n </div>\n );\n })}\n {/* Infinite-scroll sentinel + loading indicator */}\n {nextCursor !== null && (\n <div\n ref={loadMoreSentinelRef}\n className=\"flex items-center justify-center py-4\"\n >\n {loadingMore && <Spinner className=\"size-5\" />}\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Done button */}\n <div className=\"flex justify-end pt-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setIsModalOpen(false)}\n >\n {t(\"common_done\")}\n </Button>\n </div>\n </ResponsiveDialog>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useMemo } from \"react\";\nimport {\n Button,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n} from \"@fluid-app/ui-primitives\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { Globe, HelpCircle, Mail, Phone, ShoppingCart } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\ntype CtaType = \"link\" | \"cart\" | \"email\" | \"phone\";\n\ninterface CtaState {\n enabled: boolean;\n type: CtaType;\n buttonText: string;\n url: string;\n}\n\nexport interface CtaModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n ctaEnabled: boolean;\n ctaType: CtaType | string;\n ctaButtonText: string;\n ctaUrl: string;\n onDone: (state: CtaState) => void;\n}\n\nexport function CtaModal({\n open,\n onOpenChange,\n ctaEnabled,\n ctaType,\n ctaButtonText,\n ctaUrl,\n onDone,\n}: CtaModalProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n\n const CTA_TYPES: { value: CtaType; label: string; icon: React.ReactNode }[] =\n useMemo(\n () => [\n {\n value: \"link\",\n label: t(\"cta_type_page\"),\n icon: <Globe className=\"size-3.5 text-gray-500\" />,\n },\n {\n value: \"cart\",\n label: t(\"cta_type_cart\"),\n icon: <ShoppingCart className=\"size-3.5 text-gray-500\" />,\n },\n {\n value: \"email\",\n label: t(\"cta_type_email\"),\n icon: <Mail className=\"size-3.5 text-gray-500\" />,\n },\n {\n value: \"phone\",\n label: t(\"cta_type_phone\"),\n icon: <Phone className=\"size-3.5 text-gray-500\" />,\n },\n ],\n [t],\n );\n\n const [enabled, setEnabled] = useState(ctaEnabled);\n const [type, setType] = useState<CtaType>((ctaType as CtaType) || \"link\");\n const [buttonText, setButtonText] = useState(ctaButtonText);\n const [url, setUrl] = useState(ctaUrl);\n\n // Sync local state when modal opens\n useEffect(() => {\n if (open) {\n setEnabled(ctaEnabled);\n setType((ctaType as CtaType) || \"link\");\n setButtonText(ctaButtonText || t(\"cta_default_button_text\"));\n setUrl(ctaUrl);\n }\n }, [open, ctaEnabled, ctaType, ctaButtonText, ctaUrl]);\n\n const handleEnable = (checked: boolean) => {\n setEnabled(checked);\n if (checked && !buttonText) {\n setButtonText(t(\"cta_default_button_text\"));\n }\n };\n\n const handleCancel = () => {\n onOpenChange(false);\n };\n\n const handleDone = () => {\n // Always forward the current url value regardless of type — the backend\n // uses it only for type=\"link\", but preserving it here means users don't\n // lose their destination when toggling to email/phone/cart and back.\n onDone({ enabled, type, buttonText, url });\n onOpenChange(false);\n };\n\n const fieldsDisabled = !enabled;\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={onOpenChange}\n title={t(\"cta_dialog_title\")}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n >\n <p className=\"mt-1 text-sm text-gray-500\">\n {t(\"cta_dialog_description\")}\n </p>\n\n {/* Body */}\n <div className=\"space-y-5\">\n {/* Enable toggle */}\n <div className=\"flex items-center justify-between border-b border-gray-200 pb-4\">\n <span className=\"text-sm font-medium text-gray-700\">\n {t(\"cta_enable_toggle_label\")}\n </span>\n <Switch checked={enabled} onCheckedChange={handleEnable} />\n </div>\n\n {/* Action Type + Button Label — side by side */}\n <div className=\"grid grid-cols-2 items-start gap-4\">\n <div className=\"flex flex-col gap-1.5\">\n <Label\n className={fieldsDisabled ? \"text-gray-400\" : \"text-gray-700\"}\n >\n {t(\"cta_action_type_label\")}\n </Label>\n <Select\n value={type}\n onValueChange={(v) => setType(v as CtaType)}\n disabled={fieldsDisabled}\n >\n <SelectTrigger\n className={`w-full ${fieldsDisabled ? \"opacity-50\" : \"\"}`}\n >\n <SelectValue placeholder={t(\"cta_type_placeholder\")} />\n </SelectTrigger>\n <SelectContent>\n {CTA_TYPES.map((ctaOption) => (\n <SelectItem key={ctaOption.value} value={ctaOption.value}>\n <span className=\"flex items-center gap-2\">\n {ctaOption.icon}\n {ctaOption.label}\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Button Label — hidden for cart */}\n {type !== \"cart\" ? (\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center gap-1.5\">\n <Label\n className={fieldsDisabled ? \"text-gray-400\" : \"text-gray-700\"}\n >\n {t(\"cta_button_label\")}\n </Label>\n <HelpCircle className=\"size-3.5 text-gray-400\" />\n </div>\n <Input\n value={buttonText}\n onChange={(e) => setButtonText(e.target.value)}\n placeholder={t(\"cta_default_button_text\")}\n disabled={fieldsDisabled}\n className={fieldsDisabled ? \"opacity-50\" : \"\"}\n />\n </div>\n ) : (\n <div />\n )}\n </div>\n\n {/* Link-specific: URL input */}\n {type === \"link\" && (\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center gap-1.5\">\n <Label\n className={fieldsDisabled ? \"text-gray-400\" : \"text-gray-700\"}\n >\n {t(\"cta_url_label\")}\n </Label>\n <HelpCircle className=\"size-3.5 text-gray-400\" />\n </div>\n <Input\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder={t(\"cta_url_placeholder\")}\n type=\"url\"\n disabled={fieldsDisabled}\n className={fieldsDisabled ? \"opacity-50\" : \"\"}\n />\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"-mx-6 -mb-6 flex items-center justify-end gap-3 rounded-b-lg border-t border-gray-200 bg-gray-50 px-6 py-4\">\n <Button variant=\"outline\" onClick={handleCancel}>\n {t(\"common_cancel\")}\n </Button>\n <Button onClick={handleDone}>{t(\"common_done\")}</Button>\n </div>\n </ResponsiveDialog>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useRef, useCallback } from \"react\";\nimport { Button, Spinner } from \"@fluid-app/ui-primitives\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { Camera, ImageIcon, Pause, Play } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport { useThumbnailMeta } from \"../../hooks/use-thumbnail-meta\";\n\nexport interface VideoThumbnailSelectorProps {\n videoUrl: string;\n thumbnailUrl?: string | null;\n onThumbnailCaptured: (blob: Blob) => void;\n onThumbnailSelected?: () => void;\n disabled?: boolean;\n}\n\nfunction formatTime(seconds: number): string {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, \"0\")}`;\n}\n\nexport function VideoThumbnailSelector({\n videoUrl,\n thumbnailUrl,\n onThumbnailCaptured,\n onThumbnailSelected,\n disabled = false,\n}: VideoThumbnailSelectorProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const { showToast } = useShareablesUI();\n const [frameSelectorOpen, setFrameSelectorOpen] = useState(false);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [capturing, setCapturing] = useState(false);\n\n const videoRef = useRef<HTMLVideoElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n const handleLoadedMetadata = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n setDuration(video.duration);\n // Seek to 1 second or 10% of video\n video.currentTime = Math.min(1, video.duration * 0.1);\n }, []);\n\n const handleTimeUpdate = useCallback(() => {\n if (videoRef.current) {\n setCurrentTime(videoRef.current.currentTime);\n }\n }, []);\n\n const handlePlayPause = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n if (video.paused) {\n void video.play();\n setIsPlaying(true);\n } else {\n video.pause();\n setIsPlaying(false);\n }\n }, []);\n\n const handleScrub = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const video = videoRef.current;\n if (!video) return;\n const time = Number(e.target.value);\n video.currentTime = time;\n setCurrentTime(time);\n if (!video.paused) {\n video.pause();\n setIsPlaying(false);\n }\n }, []);\n\n const handleCapture = useCallback(() => {\n const video = videoRef.current;\n const canvas = canvasRef.current;\n if (!video || !canvas) return;\n\n setCapturing(true);\n try {\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n setCapturing(false);\n return;\n }\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n canvas.toBlob(\n (blob) => {\n if (blob) {\n onThumbnailCaptured(blob);\n setFrameSelectorOpen(false);\n } else {\n // toBlob can return null on memory/canvas errors — surface it so\n // the user knows why the dialog didn't produce a thumbnail.\n showToast({\n title: t(\"media_frame_capture_error\"),\n type: \"error\",\n });\n }\n setCapturing(false);\n },\n \"image/jpeg\",\n 0.92,\n );\n } catch (error) {\n // Cross-origin or other canvas error — log + surface to the user.\n showToast({\n title: t(\"media_frame_capture_error\"),\n type: \"error\",\n error,\n });\n setCapturing(false);\n }\n }, [onThumbnailCaptured, showToast, t]);\n\n const handleOpenDialog = useCallback(() => {\n setIsPlaying(false);\n setCurrentTime(0);\n setDuration(0);\n setFrameSelectorOpen(true);\n }, []);\n\n const isMetadataLoaded = duration > 0;\n\n const {\n meta: imgMeta,\n fileName: thumbFileName,\n handleLoad: handleImgLoad,\n handleError: handleImgError,\n loadError: imgLoadError,\n } = useThumbnailMeta(thumbnailUrl);\n\n return (\n <div className=\"space-y-3\">\n {/* Current thumbnail display */}\n <div>\n <label className=\"text-foreground mb-1.5 block text-sm font-medium\">\n {t(\"media_thumbnail_label\")}\n </label>\n {thumbnailUrl ? (\n <div className=\"flex items-start gap-4\">\n {imgLoadError ? (\n <div className=\"bg-muted flex h-28 w-48 shrink-0 items-center justify-center rounded-md border\">\n <ImageIcon className=\"text-muted-foreground size-8\" />\n </div>\n ) : (\n <img\n src={thumbnailUrl}\n alt={t(\"media_video_thumbnail_alt\")}\n className=\"w-48 shrink-0 rounded-md border object-contain\"\n onLoad={handleImgLoad}\n onError={handleImgError}\n />\n )}\n <div className=\"min-w-0 flex-1 pt-1\">\n <p className=\"text-foreground truncate text-sm font-medium\">\n {thumbFileName}\n </p>\n {imgMeta && imgMeta.width > 0 && (\n <p className=\"text-muted-foreground text-xs\">\n {imgMeta.width} &times; {imgMeta.height}px\n </p>\n )}\n {imgMeta?.size && (\n <p className=\"text-muted-foreground text-xs\">{imgMeta.size}</p>\n )}\n {!disabled && (\n <div className=\"mt-2 flex flex-col items-start gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleOpenDialog}\n className=\"gap-1.5\"\n >\n <Camera className=\"size-3.5\" />\n {t(\"media_select_from_video_frame\")}\n </Button>\n {onThumbnailSelected && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={onThumbnailSelected}\n className=\"gap-1.5\"\n >\n <ImageIcon className=\"size-3.5\" />\n {t(\"media_select_new_image\")}\n </Button>\n )}\n </div>\n )}\n </div>\n </div>\n ) : (\n <div className=\"flex items-center gap-4\">\n <div className=\"bg-muted flex h-28 w-48 shrink-0 items-center justify-center rounded-md border\">\n <Camera className=\"text-muted-foreground size-8\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-muted-foreground text-sm\">\n {t(\"media_no_thumbnail\")}\n </p>\n {!disabled && (\n <div className=\"mt-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleOpenDialog}\n className=\"gap-1.5\"\n >\n <Camera className=\"size-3.5\" />\n {t(\"media_select_video_frame\")}\n </Button>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n\n {/* Frame selector dialog */}\n <ResponsiveDialog\n open={frameSelectorOpen}\n onOpenChange={setFrameSelectorOpen}\n title={t(\"media_select_video_frame\")}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n size=\"wide\"\n >\n <div className=\"space-y-4\">\n {/* Video player */}\n <div className=\"relative aspect-video w-full overflow-hidden rounded-lg bg-black\">\n <video\n ref={videoRef}\n src={videoUrl}\n crossOrigin=\"anonymous\"\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={handleTimeUpdate}\n onEnded={() => setIsPlaying(false)}\n preload=\"metadata\"\n muted\n playsInline\n className=\"absolute inset-0 h-full w-full object-contain\"\n />\n {/* Play/pause overlay */}\n <button\n type=\"button\"\n onClick={handlePlayPause}\n disabled={!isMetadataLoaded}\n className=\"absolute inset-0 flex items-center justify-center bg-black/20 opacity-0 transition-opacity hover:opacity-100\"\n >\n {isPlaying ? (\n <Pause className=\"size-12 text-white\" />\n ) : (\n <Play className=\"size-12 text-white\" />\n )}\n </button>\n </div>\n\n {/* Scrub slider + time */}\n <div className=\"flex items-center gap-3\">\n <span className=\"text-muted-foreground w-12 text-xs\">\n {formatTime(currentTime)}\n </span>\n <input\n type=\"range\"\n min={0}\n max={duration || 0}\n step={0.1}\n value={currentTime}\n onChange={handleScrub}\n disabled={!isMetadataLoaded}\n className=\"h-2 flex-1 cursor-pointer appearance-none rounded-full bg-gray-200 accent-black disabled:cursor-not-allowed disabled:opacity-50\"\n />\n <span className=\"text-muted-foreground w-12 text-right text-xs\">\n {formatTime(duration)}\n </span>\n </div>\n\n {/* Capture button */}\n <Button\n onClick={handleCapture}\n disabled={!isMetadataLoaded || capturing}\n className=\"w-full gap-2\"\n >\n {capturing ? (\n <Spinner className=\"size-4\" />\n ) : (\n <Camera className=\"size-4\" />\n )}\n {t(\"media_capture_frame\")}\n </Button>\n </div>\n\n {/* Hidden canvas for frame capture */}\n <canvas ref={canvasRef} className=\"hidden\" />\n </ResponsiveDialog>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport { useQuery, useQueryClient, useMutation } from \"@tanstack/react-query\";\nimport {\n useShareablesApi,\n useRepContext,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n DialogFooter,\n Input,\n Label,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Spinner,\n Switch,\n Textarea,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { ImageIcon, MoreVertical, Trash2 } from \"lucide-react\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport {\n FilePicker,\n FilePickerApiProvider,\n FilePickerProvider,\n type FilePickerContextValue,\n} from \"@fluid-app/file-picker-ui\";\nimport type { FilePickerResult } from \"@fluid-app/file-picker-core\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport { useThumbnailMeta } from \"../../hooks/use-thumbnail-meta\";\nimport { stripTags } from \"../../utils/strip-tags\";\nimport { RichTextEditor } from \"../playlists/RichTextEditor\";\nimport { MediaProductTagging } from \"../media/MediaProductTagging\";\nimport { CtaModal } from \"../media/CtaModal\";\nimport { VideoThumbnailSelector } from \"../media/VideoThumbnailSelector\";\n\nexport interface MediaEditScreenProps {\n mediaId: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n}\n\ntype CtaType = \"link\" | \"cart\" | \"email\" | \"phone\";\n\nfunction deriveUpdateFieldsFromResult(result: FilePickerResult): {\n image_url?: string;\n video_url?: string;\n pdf_url?: string;\n kind: string;\n} {\n const mime = result.metadata.mime_type;\n if (mime.startsWith(\"video/\")) {\n return { video_url: result.file_url, kind: \"video\" };\n }\n if (mime === \"application/pdf\") {\n return { pdf_url: result.file_url, kind: \"pdf\" };\n }\n return { image_url: result.file_url, kind: \"image\" };\n}\n\ninterface EditState {\n title: string;\n description: string;\n active: boolean;\n ctaEnabled: boolean;\n ctaType: CtaType;\n ctaButtonText: string;\n ctaUrl: string;\n seoTitle: string;\n seoDescription: string;\n blockCrawler: boolean;\n}\n\nfunction buildEditState(mediaItem: {\n title: string;\n status?: \"active\" | \"draft\";\n description?: { body?: string | null } | null;\n stripped?: string | null;\n cta_enabled?: boolean;\n cta_action_type?: string | null;\n cta_button_text?: string | null;\n cta_url?: string | null;\n cta?: {\n enabled?: boolean | null;\n type?: string | null;\n button_text?: string | null;\n action_url?: string | null;\n } | null;\n seo?: {\n title?: string | null;\n description?: string | null;\n block_crawler?: boolean | null;\n } | null;\n settings?: {\n cta?: {\n enabled?: boolean;\n type?: string | null;\n button_text?: string | null;\n url?: string | null;\n } | null;\n seo?: {\n title?: string | null;\n description?: string | null;\n block_crawler?: boolean | null;\n } | null;\n } | null;\n}): EditState {\n const ctaEnabled =\n mediaItem.cta?.enabled ??\n mediaItem.settings?.cta?.enabled ??\n mediaItem.cta_enabled ??\n false;\n const rawCtaType =\n mediaItem.cta?.type ??\n mediaItem.settings?.cta?.type ??\n mediaItem.cta_action_type ??\n \"link\";\n const ctaType: CtaType =\n rawCtaType === \"cart\" || rawCtaType === \"email\" || rawCtaType === \"phone\"\n ? rawCtaType\n : \"link\";\n\n return {\n title: mediaItem.title ?? \"\",\n // Prefer HTML body; fall back to legacy `stripped` plain text only when\n // body is null/undefined (i.e. pre-rich-text items that never had a\n // stored HTML version). Nullish coalescing keeps intentionally-empty\n // \"\" from being replaced by stripped content.\n description: mediaItem.description?.body ?? mediaItem.stripped ?? \"\",\n active: mediaItem.status !== \"draft\",\n ctaEnabled,\n ctaType,\n ctaButtonText:\n mediaItem.cta?.button_text ??\n mediaItem.settings?.cta?.button_text ??\n mediaItem.cta_button_text ??\n \"\",\n ctaUrl:\n mediaItem.cta?.action_url ??\n mediaItem.settings?.cta?.url ??\n mediaItem.cta_url ??\n \"\",\n seoTitle: mediaItem.seo?.title ?? mediaItem.settings?.seo?.title ?? \"\",\n seoDescription:\n mediaItem.seo?.description ?? mediaItem.settings?.seo?.description ?? \"\",\n blockCrawler:\n mediaItem.seo?.block_crawler ??\n mediaItem.settings?.seo?.block_crawler ??\n false,\n };\n}\n\nfunction ThumbnailWithMeta({\n src,\n onReplace,\n}: {\n src: string;\n onReplace: () => void;\n}) {\n const { t } = useShareablesTranslation();\n const { meta, fileName, handleLoad, handleError, loadError } =\n useThumbnailMeta(src);\n\n return (\n <div className=\"flex items-start gap-4\">\n {loadError ? (\n <div className=\"bg-muted flex h-28 w-48 shrink-0 items-center justify-center rounded-md border\">\n <ImageIcon className=\"text-muted-foreground size-8\" />\n </div>\n ) : (\n <img\n src={src}\n alt={t(\"media_thumbnail_alt\")}\n className=\"w-48 shrink-0 rounded-md border object-contain\"\n onLoad={handleLoad}\n onError={handleError}\n />\n )}\n <div className=\"min-w-0 flex-1 pt-1\">\n <p className=\"text-foreground truncate text-sm font-medium\">\n {fileName}\n </p>\n {meta && meta.width > 0 && (\n <p className=\"text-muted-foreground text-xs\">\n {meta.width} &times; {meta.height}px\n </p>\n )}\n {meta?.size && (\n <p className=\"text-muted-foreground text-xs\">{meta.size}</p>\n )}\n <div className=\"mt-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={onReplace}\n className=\"gap-1.5\"\n >\n <ImageIcon className=\"size-3.5\" />\n {t(\"media_select_new_image\")}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n\nfunction CurrentMediaPreview({\n mediaItem,\n pendingFileUrl,\n pendingKind,\n}: {\n mediaItem: {\n kind?: string | null;\n content_format?: string | null;\n image_url?: string | null;\n video_url?: string | null;\n pdf_url?: string | null;\n title?: string | null;\n };\n pendingFileUrl?: string | null;\n pendingKind?: string | null;\n}) {\n const { t } = useShareablesTranslation();\n const effectiveKind =\n pendingKind ?? mediaItem.content_format ?? mediaItem.kind;\n const effectiveVideoUrl =\n pendingKind === \"video\" ? pendingFileUrl : mediaItem.video_url;\n const effectiveImageUrl =\n pendingKind === \"image\"\n ? pendingFileUrl\n : pendingKind\n ? null\n : mediaItem.image_url;\n const effectivePdfUrl =\n pendingKind === \"pdf\" ? pendingFileUrl : mediaItem.pdf_url;\n\n // Video: show video player (thumbnail is handled by the selector section below)\n if (effectiveKind === \"video\" && effectiveVideoUrl) {\n return (\n <div className=\"relative aspect-video w-full overflow-hidden rounded-lg border bg-black\">\n <video\n src={effectiveVideoUrl}\n poster={effectiveImageUrl ?? undefined}\n controls\n preload=\"metadata\"\n className=\"absolute inset-0 h-full w-full object-contain\"\n />\n </div>\n );\n }\n\n // PDF: show embedded viewer (thumbnail is handled by the selector section below)\n if (effectiveKind === \"pdf\" && effectivePdfUrl) {\n return (\n <div className=\"relative w-full overflow-hidden rounded-lg border\">\n <iframe\n src={`${effectivePdfUrl}#view=FitH`}\n sandbox=\"allow-same-origin\"\n className=\"h-auto w-full\"\n style={{ minHeight: \"50vh\" }}\n title={mediaItem.title ?? t(\"pdf_document_title\")}\n />\n </div>\n );\n }\n\n // Image: show the image\n if (effectiveImageUrl) {\n return (\n <div className=\"relative aspect-video w-full overflow-hidden rounded-lg border\">\n <img\n src={effectiveImageUrl}\n alt={mediaItem.title ?? t(\"media_alt_fallback\")}\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n </div>\n );\n }\n\n // No media\n return (\n <div className=\"border-border text-muted-foreground flex h-40 w-full flex-col items-center justify-center gap-2 rounded-lg border\">\n <ImageIcon className=\"size-6\" />\n <span className=\"text-sm\">{t(\"media_no_media_available\")}</span>\n </div>\n );\n}\n\nexport function MediaEditScreen({\n mediaId,\n onNavigate: _onNavigate,\n onBack,\n}: MediaEditScreenProps): React.JSX.Element {\n const { t, locale } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const api = useShareablesApi();\n const repContext = useRepContext();\n const queryClient = useQueryClient();\n const {\n navigate,\n showToast,\n filePickerApi,\n readOnly,\n mediaProductsApi,\n searchProducts,\n uploadThumbnail,\n } = useShareablesUI();\n\n // --- Form state ---\n const [editState, setEditState] = useState<EditState>({\n title: \"\",\n description: \"\",\n active: true,\n ctaEnabled: false,\n ctaType: \"link\",\n ctaButtonText: \"\",\n ctaUrl: \"\",\n seoTitle: \"\",\n seoDescription: \"\",\n blockCrawler: false,\n });\n const [originalState, setOriginalState] = useState<EditState | null>(null);\n // Locale captured at the moment the form was last (re)initialised. The\n // mutation targets this — not the live `locale` from useShareablesTranslation\n // — so a save while the user is editing in locale A but the shell has\n // since flipped to locale B still writes A-content to A's translation row.\n const [editingLocale, setEditingLocale] = useState<string | null>(null);\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isActionsOpen, setIsActionsOpen] = useState(false);\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const [isThumbnailPickerOpen, setIsThumbnailPickerOpen] = useState(false);\n const [isCtaModalOpen, setIsCtaModalOpen] = useState(false);\n const [seoExpanded, setSeoExpanded] = useState(false);\n const [pendingMediaFile, setPendingMediaFile] =\n useState<FilePickerResult | null>(null);\n const [pendingThumbnail, setPendingThumbnail] = useState<string | null>(null);\n\n // --- Data fetching ---\n const { data: mediaResponse, isLoading } = useQuery({\n queryKey: shareablesKeys.media.detail(Number(mediaId), repContext, locale),\n queryFn: () => api.media.getMediaById(Number(mediaId), locale),\n });\n\n const mediaItem = mediaResponse?.media;\n\n // Fetch tagged products\n const { data: productsData, refetch: refetchProducts } = useQuery({\n queryKey: shareablesKeys.mediaProducts.list(Number(mediaId), locale),\n queryFn: () =>\n mediaProductsApi?.listMediaProducts(Number(mediaId), {\n language_iso: locale,\n }),\n enabled: !!mediaProductsApi,\n });\n const taggedProducts = productsData?.media_products ?? [];\n\n const isDirty = useMemo(() => {\n if (pendingMediaFile || pendingThumbnail) return true;\n if (!originalState) return false;\n return (\n editState.title !== originalState.title ||\n editState.description !== originalState.description ||\n editState.active !== originalState.active ||\n editState.ctaEnabled !== originalState.ctaEnabled ||\n editState.ctaType !== originalState.ctaType ||\n editState.ctaButtonText !== originalState.ctaButtonText ||\n editState.ctaUrl !== originalState.ctaUrl ||\n editState.seoTitle !== originalState.seoTitle ||\n editState.seoDescription !== originalState.seoDescription ||\n editState.blockCrawler !== originalState.blockCrawler\n );\n }, [editState, originalState, pendingMediaFile, pendingThumbnail]);\n\n // Mirror isDirty into a ref so the init effect can read the latest\n // dirty signal without re-firing on every keystroke (which would happen\n // if `isDirty` were in its dep array).\n const isDirtyRef = useRef(false);\n isDirtyRef.current = isDirty;\n\n // Initialise edit state once data arrives. Bail on subsequent fetches\n // when the user has unsaved edits — otherwise switching locale via the\n // shell LanguageSelector mid-edit would refetch and silently stomp the\n // in-progress changes with the new locale's translation row.\n useEffect(() => {\n if (!mediaItem) return;\n if (isDirtyRef.current) return;\n const initial = buildEditState(mediaItem);\n setEditState(initial);\n setOriginalState(initial);\n setEditingLocale(locale);\n }, [mediaItem, locale]);\n\n // --- Mutations ---\n const { mutate: updateMedia, isPending: isSaving } = useMutation({\n mutationFn: () =>\n api.media.updateMedia(\n Number(mediaId),\n {\n title: editState.title,\n description: editState.description,\n active: editState.active,\n ...(pendingMediaFile\n ? deriveUpdateFieldsFromResult(pendingMediaFile)\n : {}),\n ...(pendingThumbnail ? { thumbnail_url: pendingThumbnail } : {}),\n settings: {\n cta: {\n enabled: editState.ctaEnabled,\n type: editState.ctaType,\n button_text: editState.ctaButtonText || undefined,\n url:\n editState.ctaType === \"link\"\n ? editState.ctaUrl || undefined\n : undefined,\n },\n seo: {\n title: editState.seoTitle || null,\n description: editState.seoDescription || null,\n block_crawler: editState.blockCrawler,\n },\n },\n },\n editingLocale ?? locale,\n ),\n onSuccess: () => {\n // Invalidate the entire media.all branch so every locale's cached\n // detail entry refetches — a write against one locale can affect the\n // default-locale view (or other consumers) in subtle ways.\n void queryClient.invalidateQueries({\n queryKey: shareablesKeys.media.all,\n });\n setOriginalState(editState);\n setPendingMediaFile(null);\n setPendingThumbnail(null);\n showToast({ title: t(\"media_save_success\"), type: \"success\" });\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"media_save_error\", { message: error.message }),\n type: \"error\",\n error,\n });\n },\n });\n\n const { mutate: deleteMedia, isPending: isDeleting } = useMutation({\n mutationFn: () => api.media.deleteMedia(Number(mediaId)),\n onSuccess: () => {\n showToast({ title: t(\"media_delete_success\"), type: \"success\" });\n navigate(\"media\");\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"media_delete_error\", { message: error.message }),\n type: \"error\",\n error,\n });\n setIsDeleteOpen(false);\n },\n });\n\n // --- Handlers ---\n const handleSave = useCallback(() => {\n updateMedia();\n }, [updateMedia]);\n\n const handleDelete = useCallback(() => {\n deleteMedia();\n }, [deleteMedia]);\n\n const handleFilesSelected = useCallback((results: FilePickerResult[]) => {\n const result = results[0];\n if (!result) return;\n setIsPickerOpen(false);\n setPendingMediaFile(result);\n }, []);\n\n // --- File picker context ---\n const filePickerContextValue = useMemo<FilePickerContextValue | null>(() => {\n if (!filePickerApi) return null;\n return {\n toast: {\n success: (msg: string) => showToast({ title: msg, type: \"success\" }),\n error: (msg: string, error?: unknown) =>\n showToast({\n title: error instanceof Error ? `${msg}: ${error.message}` : msg,\n type: \"error\",\n }),\n loading: () => \"\",\n dismiss: () => {},\n },\n };\n }, [filePickerApi, showToast]);\n\n const displayTitle = mediaItem?.title ?? \"\";\n\n // --- Loading / empty states ---\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center py-16\">\n <Spinner className=\"size-8\" />\n </div>\n );\n }\n\n if (!mediaItem) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">{t(\"media_not_found\")}</p>\n </div>\n );\n }\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (onBack ?? (() => navigate(\"media\")))();\n }}\n >\n {t(\"media_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n navigate(`media/${mediaId}`);\n }}\n className=\"block max-w-[40vw] truncate align-bottom md:max-w-[60ch]\"\n >\n {displayTitle || t(\"media_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"common_edit\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ScreenHeaderActions>\n {readOnly || !mediaItem ? null : (\n <div className=\"flex items-center gap-2\">\n {isMobile ? (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={t(\"media_actions_aria\")}\n onClick={() => setIsActionsOpen(true)}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n <MobileActionSheet\n open={isActionsOpen}\n onOpenChange={setIsActionsOpen}\n title={t(\"media_actions_aria\")}\n actions={[\n {\n id: \"delete\",\n label: t(\"common_delete\"),\n variant: \"destructive\",\n onSelect: () => setIsDeleteOpen(true),\n },\n ]}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"size-8 p-0\">\n <MoreVertical className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={() => setIsDeleteOpen(true)}\n className=\"text-destructive focus:text-destructive\"\n >\n <Trash2 className=\"mr-2 size-4\" />\n {t(\"common_delete\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n <Button\n onClick={handleSave}\n disabled={!isDirty || isSaving}\n size=\"sm\"\n >\n {isSaving ? <Spinner className=\"size-4\" /> : t(\"common_save\")}\n </Button>\n </div>\n )}\n </ScreenHeaderActions>\n <div className=\"flex flex-col gap-4 px-4 pt-4 pb-24 md:px-10 md:py-6\">\n <div className=\"mx-auto flex w-full max-w-5xl items-center gap-3\">\n <h1 className=\"text-foreground text-xl font-semibold break-words\">\n {editState.title || displayTitle || t(\"media_heading\")}\n </h1>\n </div>\n\n {/* Single column on mobile, 3:1 grid on desktop */}\n <div className=\"mx-auto grid w-full max-w-5xl grid-cols-1 gap-6 md:grid-cols-[3fr_1fr]\">\n {/* LEFT COLUMN */}\n <div className=\"space-y-6\">\n {/* Card 1: Media */}\n <div className=\"border-border bg-card rounded-lg border p-4 md:p-5\">\n <div className=\"mb-4 flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-foreground text-base font-semibold\">\n {t(\"media_heading\")}\n </h3>\n <div className=\"flex flex-wrap items-center gap-2\">\n {!readOnly && filePickerContextValue && filePickerApi && (\n <Button\n onClick={() => setIsPickerOpen(true)}\n variant=\"outline\"\n size=\"sm\"\n >\n {t(\"media_replace_button\")}\n </Button>\n )}\n {!readOnly && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsCtaModalOpen(true)}\n className={\n editState.ctaEnabled\n ? \"border-primary text-primary\"\n : \"\"\n }\n >\n {t(\"media_cta_button_label\")}\n {editState.ctaEnabled ? t(\"common_on\") : t(\"common_off\")}\n </Button>\n )}\n </div>\n </div>\n\n <CurrentMediaPreview\n mediaItem={mediaItem}\n pendingFileUrl={\n pendingMediaFile ? pendingMediaFile.file_url : null\n }\n pendingKind={\n pendingMediaFile\n ? deriveUpdateFieldsFromResult(pendingMediaFile).kind\n : null\n }\n />\n\n {/* Thumbnail selector — video gets frame capture + upload,\n PDF gets upload only */}\n {(() => {\n const pendingKind = pendingMediaFile\n ? deriveUpdateFieldsFromResult(pendingMediaFile).kind\n : null;\n const rawContentFormat = (\n mediaItem as { content_format?: string | null }\n ).content_format;\n const effectiveKind =\n pendingKind ?? rawContentFormat ?? mediaItem.kind;\n const effectiveVideoUrl =\n pendingKind === \"video\"\n ? pendingMediaFile?.file_url\n : mediaItem.video_url;\n\n if (readOnly) return null;\n\n if (effectiveKind === \"video\" && effectiveVideoUrl) {\n return (\n <div className=\"border-border mt-4 border-t pt-4\">\n <VideoThumbnailSelector\n videoUrl={effectiveVideoUrl}\n thumbnailUrl={pendingThumbnail ?? mediaItem.image_url}\n onThumbnailCaptured={async (blob) => {\n if (!uploadThumbnail) {\n showToast({\n title: t(\"media_thumbnail_capture_unavailable\"),\n type: \"error\",\n });\n return;\n }\n try {\n const url = await uploadThumbnail(\n blob,\n `thumbnail-${mediaId}.jpg`,\n );\n setPendingThumbnail(url);\n } catch (error) {\n showToast({\n title: t(\"media_thumbnail_upload_error\"),\n type: \"error\",\n error,\n });\n }\n }}\n onThumbnailSelected={() =>\n setIsThumbnailPickerOpen(true)\n }\n />\n {pendingThumbnail && (\n <p className=\"text-muted-foreground mt-1.5 text-xs\">\n {t(\"media_pending_save\")}\n </p>\n )}\n </div>\n );\n }\n\n if (effectiveKind === \"pdf\") {\n const currentThumbnail =\n pendingThumbnail ?? mediaItem.image_url;\n return (\n <div className=\"border-border mt-4 border-t pt-4\">\n <div className=\"space-y-3\">\n <label className=\"text-foreground mb-1.5 block text-sm font-medium\">\n {t(\"media_thumbnail_label\")}\n </label>\n {currentThumbnail ? (\n <ThumbnailWithMeta\n src={currentThumbnail}\n onReplace={() => setIsThumbnailPickerOpen(true)}\n />\n ) : (\n <div className=\"flex items-center gap-4\">\n <div className=\"bg-muted flex h-28 w-48 shrink-0 items-center justify-center rounded-md border\">\n <ImageIcon className=\"text-muted-foreground size-8\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-muted-foreground mb-2 text-sm\">\n {t(\"media_no_thumbnail\")}\n </p>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsThumbnailPickerOpen(true)}\n className=\"gap-1.5\"\n >\n <ImageIcon className=\"size-3.5\" />\n {t(\"media_select_new_image\")}\n </Button>\n </div>\n </div>\n )}\n </div>\n {pendingThumbnail && (\n <p className=\"text-muted-foreground mt-1.5 text-xs\">\n {t(\"media_pending_save\")}\n </p>\n )}\n </div>\n );\n }\n\n return null;\n })()}\n\n {/* CTA configured via modal */}\n </div>\n\n {/* Card 2: Media Details */}\n <div className=\"border-border bg-card rounded-lg border p-4 md:p-5\">\n <h3 className=\"text-foreground mb-4 text-base font-semibold\">\n {t(\"media_details_heading\")}\n </h3>\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"media-title\">\n {t(\"common_title_label\")}{\" \"}\n <span className=\"text-destructive\">*</span>\n </Label>\n {readOnly ? (\n <p className=\"text-foreground text-sm\">{displayTitle}</p>\n ) : (\n <Input\n id=\"media-title\"\n value={editState.title}\n onChange={(e) =>\n setEditState((s) => ({ ...s, title: e.target.value }))\n }\n placeholder={t(\"media_title_placeholder\")}\n />\n )}\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <Label>{t(\"common_description_label\")}</Label>\n {readOnly ? (\n // Description is stored as HTML from RichTextEditor, so\n // strip tags before rendering as plain text — otherwise\n // raw <p>/<strong>/etc. would show as visible characters.\n <p className=\"text-foreground/70 text-sm leading-relaxed\">\n {stripTags(editState.description)}\n </p>\n ) : (\n <RichTextEditor\n value={editState.description}\n onChange={(val) =>\n setEditState((s) => ({ ...s, description: val }))\n }\n placeholder={t(\"common_start_writing_placeholder\")}\n />\n )}\n </div>\n </div>\n </div>\n\n {/* Card 3: Tagged Products */}\n <MediaProductTagging\n products={taggedProducts}\n onAddProduct={async (productId) => {\n if (!mediaProductsApi) return;\n try {\n await mediaProductsApi.addMediaProduct(Number(mediaId), {\n id: productId,\n });\n await refetchProducts();\n } catch (error) {\n showToast({\n title: t(\"media_edit_add_product_failed\", {\n message:\n error instanceof Error\n ? error.message\n : t(\"error_unknown\"),\n }),\n type: \"error\",\n error,\n });\n }\n }}\n onRemoveProduct={async (productId) => {\n if (!mediaProductsApi) return;\n try {\n await mediaProductsApi.removeMediaProduct(\n Number(mediaId),\n productId,\n );\n await refetchProducts();\n } catch (error) {\n showToast({\n title: t(\"media_edit_remove_product_failed\", {\n message:\n error instanceof Error\n ? error.message\n : t(\"error_unknown\"),\n }),\n type: \"error\",\n error,\n });\n }\n }}\n onSearchProducts={\n searchProducts ??\n (async () => ({ products: [], nextCursor: null }))\n }\n disabled={readOnly || !mediaProductsApi}\n />\n\n {/* Delete is in the header dropdown menu */}\n </div>\n\n {/* RIGHT COLUMN */}\n <div className=\"space-y-6\">\n {/* SEO And Link Sharing */}\n <div className=\"border-border bg-card rounded-lg border p-4 md:p-5\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"text-foreground text-base font-semibold\">\n {t(\"seo_heading\")}\n </h3>\n {!readOnly && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setSeoExpanded((prev) => !prev)}\n >\n {seoExpanded ? t(\"common_done\") : t(\"common_edit\")}\n </Button>\n )}\n </div>\n\n {/* SEO preview card. Thumbnail reflects pending media replacement\n (new image from file picker, or captured thumbnail) so it\n stays consistent with CurrentMediaPreview before Save. */}\n {(() => {\n const pendingFields = pendingMediaFile\n ? deriveUpdateFieldsFromResult(pendingMediaFile)\n : null;\n const seoThumbnailUrl =\n pendingFields?.image_url ??\n pendingThumbnail ??\n mediaItem?.image_url;\n return (\n <div className=\"bg-muted/40 border-border mb-4 rounded-md border p-3\">\n <div className=\"mb-2 flex items-center gap-2\">\n {seoThumbnailUrl ? (\n <img\n src={seoThumbnailUrl}\n alt={t(\"media_edit_seo_preview_alt\")}\n className=\"size-8 shrink-0 rounded object-cover\"\n />\n ) : (\n <div className=\"bg-muted flex size-8 items-center justify-center rounded\">\n <ImageIcon className=\"text-muted-foreground size-4\" />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-foreground truncate text-xs font-medium\">\n {editState.seoTitle ||\n displayTitle ||\n t(\"seo_preview_title_fallback\")}\n </p>\n <p className=\"text-muted-foreground truncate text-xs\">\n {mediaItem?.preview_link ??\n mediaItem?.canonical_url ??\n t(\"seo_preview_url_fallback\")}\n </p>\n </div>\n </div>\n {editState.seoDescription && (\n <p className=\"text-muted-foreground line-clamp-2 text-xs\">\n {editState.seoDescription}\n </p>\n )}\n </div>\n );\n })()}\n\n {seoExpanded && (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"seo-title\">{t(\"seo_title_label\")}</Label>\n <Input\n id=\"seo-title\"\n value={editState.seoTitle}\n onChange={(e) =>\n setEditState((s) => ({\n ...s,\n seoTitle: e.target.value,\n }))\n }\n placeholder={t(\"seo_title_placeholder\")}\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"seo-description\">\n {t(\"seo_description_label\")}\n </Label>\n <Textarea\n id=\"seo-description\"\n value={editState.seoDescription}\n onChange={(e) =>\n setEditState((s) => ({\n ...s,\n seoDescription: e.target.value,\n }))\n }\n placeholder={t(\"seo_description_placeholder\")}\n rows={3}\n />\n </div>\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"text-foreground text-sm font-medium\">\n {t(\"seo_block_crawlers_label\")}\n </span>\n <span className=\"text-muted-foreground text-xs\">\n {t(\"seo_block_crawlers_description\")}\n </span>\n </div>\n <Switch\n checked={editState.blockCrawler}\n onCheckedChange={(checked) =>\n setEditState((s) => ({ ...s, blockCrawler: checked }))\n }\n aria-label={t(\"seo_block_crawlers_aria\")}\n />\n </div>\n </div>\n )}\n </div>\n\n {/* Status and Countries default — hidden, defaults apply */}\n </div>\n </div>\n\n {/* Delete confirmation */}\n <ResponsiveDialog\n open={isDeleteOpen}\n onOpenChange={setIsDeleteOpen}\n title={t(\"media_delete_dialog_title\")}\n description={t(\"media_delete_dialog_description\", {\n title: mediaItem?.title ?? \"\",\n })}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n role=\"alertdialog\"\n size=\"compact\"\n >\n <DialogFooter>\n <Button\n variant=\"secondary\"\n disabled={isDeleting}\n onClick={() => setIsDeleteOpen(false)}\n >\n {t(\"common_cancel\")}\n </Button>\n <Button\n variant=\"destructive\"\n onClick={handleDelete}\n disabled={isDeleting}\n >\n {isDeleting ? t(\"common_deleting\") : t(\"common_delete\")}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n\n {/* CTA Modal */}\n <CtaModal\n open={isCtaModalOpen}\n onOpenChange={setIsCtaModalOpen}\n ctaEnabled={editState.ctaEnabled}\n ctaType={editState.ctaType}\n ctaButtonText={editState.ctaButtonText}\n ctaUrl={editState.ctaUrl}\n onDone={(ctaState) => {\n setEditState((s) => ({\n ...s,\n ctaEnabled: ctaState.enabled,\n ctaType: ctaState.type,\n ctaButtonText: ctaState.buttonText,\n ctaUrl: ctaState.url,\n }));\n }}\n />\n\n {/* Replace Media file picker */}\n {filePickerContextValue && filePickerApi && (\n <FilePickerApiProvider api={filePickerApi}>\n <FilePickerProvider value={filePickerContextValue}>\n <FilePicker\n open={isPickerOpen}\n onFilesSelected={handleFilesSelected}\n onClose={() => setIsPickerOpen(false)}\n config={{\n maxFiles: 1,\n allowedMethods: [\"dam\", \"upload\", \"url\"],\n }}\n />\n </FilePickerProvider>\n </FilePickerApiProvider>\n )}\n\n {/* Thumbnail image file picker */}\n {filePickerContextValue && filePickerApi && (\n <FilePickerApiProvider api={filePickerApi}>\n <FilePickerProvider value={filePickerContextValue}>\n <FilePicker\n open={isThumbnailPickerOpen}\n onFilesSelected={(results) => {\n const result = results[0];\n if (result) {\n setPendingThumbnail(result.file_url);\n }\n setIsThumbnailPickerOpen(false);\n }}\n onClose={() => setIsThumbnailPickerOpen(false)}\n config={{\n maxFiles: 1,\n allowedMethods: [\"dam\", \"upload\", \"url\"],\n }}\n />\n </FilePickerProvider>\n </FilePickerApiProvider>\n )}\n </div>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useCallback, useMemo } from \"react\";\nimport {\n useShareablesUser,\n useCreateMediaMutation,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Input,\n Label,\n Spinner,\n Switch,\n Textarea,\n} from \"@fluid-app/ui-primitives\";\nimport { ImageIcon, Upload, Video, FileText } from \"lucide-react\";\nimport {\n FilePicker,\n FilePickerApiProvider,\n FilePickerProvider,\n type FilePickerContextValue,\n} from \"@fluid-app/file-picker-ui\";\nimport type { FilePickerResult } from \"@fluid-app/file-picker-core\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport { RichTextEditor } from \"../playlists/RichTextEditor\";\nimport { CtaModal } from \"../media/CtaModal\";\n\nexport interface MediaCreateScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n}\n\ntype CtaType = \"link\" | \"cart\" | \"email\" | \"phone\";\n\nfunction deriveMediaType(mimeType: string): \"image\" | \"video\" | \"pdf\" {\n if (mimeType.startsWith(\"video/\")) return \"video\";\n if (mimeType === \"application/pdf\") return \"pdf\";\n return \"image\";\n}\n\nfunction FilePreview({\n result,\n mediaType,\n}: {\n result: FilePickerResult;\n mediaType: \"image\" | \"video\" | \"pdf\";\n}) {\n const { t } = useShareablesTranslation();\n const thumbnailSrc =\n mediaType === \"image\" ? result.file_url : result.thumbnail_url;\n\n if (thumbnailSrc) {\n const Icon =\n mediaType === \"video\" ? Video : mediaType === \"pdf\" ? FileText : null;\n return (\n <div className=\"relative aspect-video w-full overflow-hidden rounded-lg border\">\n <img\n src={thumbnailSrc}\n alt={result.metadata.file_name}\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n {Icon && (\n <div className=\"absolute bottom-2 left-2 flex items-center gap-1.5 rounded-md bg-black/60 px-2 py-1 text-white\">\n <Icon className=\"size-3.5\" />\n <span className=\"text-xs font-medium\">\n {mediaType === \"video\"\n ? t(\"media_type_video\")\n : t(\"media_type_pdf\")}\n </span>\n </div>\n )}\n </div>\n );\n }\n\n const Icon = mediaType === \"video\" ? Video : FileText;\n return (\n <div className=\"flex items-center gap-3 rounded-lg border p-4\">\n <Icon className=\"text-muted-foreground size-8 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-foreground truncate text-sm font-medium\">\n {result.metadata.file_name}\n </p>\n <p className=\"text-muted-foreground text-xs\">\n {result.metadata.mime_type}\n </p>\n </div>\n </div>\n );\n}\n\nexport function MediaCreateScreen({\n onNavigate,\n onBack,\n}: MediaCreateScreenProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const { navigate, showToast, filePickerApi } = useShareablesUI();\n const user = useShareablesUser();\n\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [active] = useState(true);\n const [selectedResult, setSelectedResult] = useState<FilePickerResult | null>(\n null,\n );\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const [isCtaModalOpen, setIsCtaModalOpen] = useState(false);\n\n // CTA state\n const [ctaEnabled, setCtaEnabled] = useState(false);\n const [ctaType, setCtaType] = useState<CtaType>(\"link\");\n const [ctaButtonText, setCtaButtonText] = useState(\"\");\n const [ctaUrl, setCtaUrl] = useState(\"\");\n\n // SEO state\n const [seoExpanded, setSeoExpanded] = useState(false);\n const [seoTitle, setSeoTitle] = useState(\"\");\n const [seoDescription, setSeoDescription] = useState(\"\");\n const [seoBlockCrawler, setSeoBlockCrawler] = useState(false);\n\n const mediaType = selectedResult\n ? deriveMediaType(selectedResult.metadata.mime_type)\n : null;\n\n const { mutate: createMedia, isPending: isCreating } = useCreateMediaMutation(\n {\n onSuccess: (newMedia: { id: number }) => {\n showToast({ title: t(\"media_create_success\"), type: \"success\" });\n onNavigate?.(\"media\", String(newMedia.id));\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"media_create_error\", { message: error.message }),\n type: \"error\",\n });\n },\n },\n );\n\n const handleSave = useCallback(() => {\n if (!title.trim()) {\n showToast({ title: t(\"media_title_required\"), type: \"warning\" });\n return;\n }\n if (!selectedResult) {\n showToast({ title: t(\"media_file_required\"), type: \"warning\" });\n return;\n }\n\n const mt = deriveMediaType(selectedResult.metadata.mime_type);\n\n createMedia({\n title: title.trim(),\n description: description,\n active,\n media_type: \"share\",\n kind: mt,\n image_url: mt === \"image\" ? selectedResult.file_url : undefined,\n video_url: mt === \"video\" ? selectedResult.file_url : undefined,\n pdf_url: mt === \"pdf\" ? selectedResult.file_url : undefined,\n user_id: user?.id,\n settings: {\n cta: ctaEnabled\n ? {\n enabled: true,\n type: ctaType,\n button_text: ctaButtonText || null,\n url: ctaType === \"link\" ? ctaUrl || null : null,\n }\n : { enabled: false },\n seo: {\n title: seoTitle || null,\n description: seoDescription || null,\n block_crawler: seoBlockCrawler,\n },\n },\n });\n }, [\n t,\n title,\n description,\n active,\n selectedResult,\n user,\n createMedia,\n showToast,\n ctaEnabled,\n ctaType,\n ctaButtonText,\n ctaUrl,\n seoTitle,\n seoDescription,\n seoBlockCrawler,\n ]);\n\n const handleFilesSelected = useCallback(\n (results: FilePickerResult[]) => {\n const result = results[0];\n if (!result) return;\n setSelectedResult(result);\n setIsPickerOpen(false);\n if (!title.trim()) {\n setTitle(result.metadata.file_name.replace(/\\.[^/.]+$/, \"\"));\n }\n },\n [title],\n );\n\n const filePickerContextValue = useMemo<FilePickerContextValue | null>(() => {\n if (!filePickerApi) return null;\n return {\n toast: {\n success: (msg: string) => showToast({ title: msg, type: \"success\" }),\n error: (msg: string, error?: unknown) =>\n showToast({\n title: error instanceof Error ? `${msg}: ${error.message}` : msg,\n type: \"error\",\n }),\n loading: () => \"\",\n dismiss: () => {},\n },\n };\n }, [filePickerApi, showToast]);\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (onBack ?? (() => navigate(\"media\")))();\n }}\n >\n {t(\"media_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"media_create_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ScreenHeaderActions>\n <Button onClick={handleSave} disabled={isCreating} size=\"sm\">\n {isCreating ? <Spinner className=\"size-4\" /> : t(\"common_save\")}\n </Button>\n </ScreenHeaderActions>\n <div className=\"flex flex-col gap-4 px-4 py-4 md:px-10 md:py-6\">\n {/* Title display area */}\n <div className=\"mx-auto flex w-full max-w-5xl items-center gap-3\">\n <h1 className=\"text-foreground text-xl font-semibold\">\n {title.trim() || t(\"media_new_title\")}\n </h1>\n </div>\n\n {/* Two-column grid */}\n <div className=\"mx-auto grid w-full max-w-5xl gap-6 md:grid-cols-[3fr_1fr]\">\n {/* LEFT COLUMN */}\n <div className=\"space-y-6\">\n {/* Card 1: Media */}\n <div className=\"border-border bg-card rounded-lg border p-5\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"text-foreground text-base font-semibold\">\n {t(\"media_heading\")}\n </h3>\n <div className=\"flex items-center gap-2\">\n <Button\n onClick={() => setIsPickerOpen(true)}\n variant=\"outline\"\n size=\"sm\"\n disabled={!filePickerApi}\n >\n {t(\"media_select_button\")}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setIsCtaModalOpen(true)}\n className={ctaEnabled ? \"border-primary text-primary\" : \"\"}\n >\n {t(\"media_cta_button_label\")}\n {ctaEnabled ? t(\"common_on\") : t(\"common_off\")}\n </Button>\n </div>\n </div>\n\n {selectedResult && mediaType ? (\n <div className=\"flex flex-col gap-3\">\n <FilePreview result={selectedResult} mediaType={mediaType} />\n <Button\n onClick={() => setIsPickerOpen(true)}\n variant=\"outline\"\n size=\"sm\"\n className=\"w-fit\"\n >\n {t(\"common_replace\")}\n </Button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => setIsPickerOpen(true)}\n disabled={!filePickerApi}\n className=\"border-border text-muted-foreground hover:bg-muted/30 flex h-40 w-full flex-col items-center justify-center gap-2 rounded-lg border border-dashed transition-colors disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Upload className=\"size-6\" />\n <span className=\"text-sm\">\n {t(\"media_upload_placeholder\")}\n </span>\n </button>\n )}\n\n {/* CTA configured via modal */}\n </div>\n\n {/* Card 2: Media Details */}\n <div className=\"border-border bg-card rounded-lg border p-5\">\n <h3 className=\"text-foreground mb-4 text-base font-semibold\">\n {t(\"media_details_heading\")}\n </h3>\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"media-title\">\n {t(\"common_title_label\")}{\" \"}\n <span className=\"text-destructive\">*</span>\n </Label>\n <Input\n id=\"media-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder={t(\"media_title_placeholder\")}\n required\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <Label>{t(\"common_description_label\")}</Label>\n <RichTextEditor\n value={description}\n onChange={setDescription}\n placeholder={t(\"common_start_writing_placeholder\")}\n />\n </div>\n </div>\n </div>\n </div>\n\n {/* RIGHT COLUMN */}\n <div className=\"space-y-6\">\n {/* SEO And Link Sharing */}\n <div className=\"border-border bg-card rounded-lg border p-5\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"text-foreground text-base font-semibold\">\n {t(\"seo_heading\")}\n </h3>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setSeoExpanded((prev) => !prev)}\n >\n {seoExpanded ? t(\"common_done\") : t(\"common_edit\")}\n </Button>\n </div>\n\n {/* SEO preview card */}\n <div className=\"bg-muted/40 border-border mb-4 rounded-md border p-3\">\n <div className=\"mb-2 flex items-center gap-2\">\n <div className=\"bg-muted flex size-8 items-center justify-center rounded\">\n <ImageIcon className=\"text-muted-foreground size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-foreground truncate text-xs font-medium\">\n {seoTitle || t(\"seo_preview_title_fallback\")}\n </p>\n <p className=\"text-muted-foreground truncate text-xs\">\n {t(\"seo_preview_url_fallback\")}\n </p>\n </div>\n </div>\n {seoDescription && (\n <p className=\"text-muted-foreground line-clamp-2 text-xs\">\n {seoDescription}\n </p>\n )}\n </div>\n\n {seoExpanded && (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"seo-title\">{t(\"seo_title_label\")}</Label>\n <Input\n id=\"seo-title\"\n value={seoTitle}\n onChange={(e) => setSeoTitle(e.target.value)}\n placeholder={t(\"seo_title_placeholder\")}\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"seo-description\">\n {t(\"seo_description_label\")}\n </Label>\n <Textarea\n id=\"seo-description\"\n value={seoDescription}\n onChange={(e) => setSeoDescription(e.target.value)}\n placeholder={t(\"seo_description_placeholder\")}\n rows={3}\n />\n </div>\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex flex-col gap-0.5\">\n <span className=\"text-foreground text-sm font-medium\">\n {t(\"seo_block_crawlers_label\")}\n </span>\n <span className=\"text-muted-foreground text-xs\">\n {t(\"seo_block_crawlers_description\")}\n </span>\n </div>\n <Switch\n checked={seoBlockCrawler}\n onCheckedChange={setSeoBlockCrawler}\n aria-label={t(\"seo_block_crawlers_aria\")}\n />\n </div>\n </div>\n )}\n </div>\n\n {/* Status defaults to active, countries default to all — hidden from create UI */}\n </div>\n </div>\n\n {/* CTA Modal */}\n <CtaModal\n open={isCtaModalOpen}\n onOpenChange={setIsCtaModalOpen}\n ctaEnabled={ctaEnabled}\n ctaType={ctaType}\n ctaButtonText={ctaButtonText}\n ctaUrl={ctaUrl}\n onDone={(ctaState) => {\n setCtaEnabled(ctaState.enabled);\n setCtaType(ctaState.type);\n setCtaButtonText(ctaState.buttonText);\n setCtaUrl(ctaState.url);\n }}\n />\n\n {/* File Picker Dialog */}\n {filePickerContextValue && filePickerApi && (\n <FilePickerApiProvider api={filePickerApi}>\n <FilePickerProvider value={filePickerContextValue}>\n <FilePicker\n open={isPickerOpen}\n onFilesSelected={handleFilesSelected}\n onClose={() => setIsPickerOpen(false)}\n config={{\n maxFiles: 1,\n allowedMethods: [\"dam\", \"upload\", \"url\"],\n }}\n />\n </FilePickerProvider>\n </FilePickerApiProvider>\n )}\n </div>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport {\n Badge,\n Checkbox,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n cn,\n} from \"@fluid-app/ui-primitives\";\nimport { Heart, MoreVertical, Pencil, Trash2 } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { useShareablesUI, useRenderImage } from \"../../context\";\n\nexport interface PlaylistCardProps {\n title: string;\n imageUrl?: string | null;\n href: string;\n itemCount: number;\n isFavorited?: boolean;\n isSelectable?: boolean;\n isSelected?: boolean;\n canEdit?: boolean;\n onSelectionChange?: (selected: boolean) => void;\n onToggleFavorite?: () => void;\n onEdit?: () => void;\n onDelete?: () => void;\n}\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport function PlaylistCard({\n title,\n imageUrl,\n href,\n itemCount,\n isFavorited = false,\n isSelectable = false,\n isSelected = false,\n canEdit = false,\n onSelectionChange,\n onToggleFavorite,\n onEdit,\n onDelete,\n}: PlaylistCardProps): React.JSX.Element {\n const { navigate } = useShareablesUI();\n const { t } = useShareablesTranslation();\n const renderImage = useRenderImage();\n const [imgError, setImgError] = useState(false);\n const [isActionsOpen, setIsActionsOpen] = useState(false);\n const isMobile = useIsMobile();\n\n const hasStack = itemCount > 1;\n const badgeText =\n itemCount === 1\n ? t(\"playlist_item_count_one\", { count: \"1\" })\n : t(\"playlist_item_count_other\", { count: String(itemCount) });\n\n return (\n <button\n type=\"button\"\n onClick={() => navigate(href)}\n className=\"group block w-full cursor-pointer text-left\"\n >\n {/* Stack effect container */}\n <div className=\"relative\">\n {/* Stack layers */}\n {hasStack && (\n <>\n <div\n className=\"bg-muted absolute -top-2 right-2 left-2 z-0 h-full rounded-lg shadow-sm transition-transform duration-300 ease-out\"\n style={{\n transform: isSelected ? \"rotate(0deg)\" : \"rotate(-4deg)\",\n }}\n />\n <div\n className=\"bg-muted absolute -top-1 right-1 left-1 z-0 h-full rounded-lg shadow-sm transition-transform duration-300 ease-out\"\n style={{\n transform: isSelected ? \"rotate(0deg)\" : \"rotate(7deg)\",\n }}\n />\n </>\n )}\n\n {/* Main card */}\n <div className=\"bg-background relative z-10 rounded-lg p-1 shadow-md\">\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: imgError ? DEFAULT_IMAGE : imageUrl || DEFAULT_IMAGE,\n alt: title,\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n onError: () => setImgError(true),\n })}\n\n {/* Selection checkbox */}\n {isSelectable && (\n <div\n className={cn(\n \"absolute top-2 left-2 z-10 transition-opacity\",\n isSelected\n ? \"opacity-100\"\n : \"opacity-0 group-hover:opacity-100\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n }}\n >\n <Checkbox\n checked={isSelected}\n onCheckedChange={() => onSelectionChange?.(!isSelected)}\n />\n </div>\n )}\n\n {/* Context menu */}\n {(onToggleFavorite || canEdit) && (\n <div\n className={cn(\n \"absolute top-2 right-2 z-20 transition-opacity\",\n isMobile\n ? \"opacity-100\"\n : \"opacity-0 group-hover:opacity-100\",\n )}\n >\n {isMobile ? (\n <>\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n setIsActionsOpen(true);\n }}\n className=\"bg-background/90 hover:bg-background flex size-8 items-center justify-center rounded-lg shadow-md backdrop-blur-sm transition-all\"\n aria-label={t(\"common_more_options_aria\")}\n >\n <MoreVertical className=\"text-foreground size-4\" />\n </button>\n <MobileActionSheet\n open={isActionsOpen}\n onOpenChange={setIsActionsOpen}\n title={t(\"common_more_options_aria\")}\n actions={[\n ...(onToggleFavorite\n ? [\n {\n id: \"favorite\",\n label: isFavorited\n ? t(\"common_unfavorite\")\n : t(\"common_favorite\"),\n onSelect: onToggleFavorite,\n },\n ]\n : []),\n ...(canEdit && onEdit\n ? [\n {\n id: \"edit\",\n label: t(\"common_edit\"),\n onSelect: onEdit,\n },\n ]\n : []),\n ...(canEdit && onDelete\n ? [\n {\n id: \"delete\",\n label: t(\"common_delete\"),\n onSelect: onDelete,\n variant: \"destructive\" as const,\n },\n ]\n : []),\n ]}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n onClick={(e) => e.stopPropagation()}\n className=\"bg-background/90 hover:bg-background flex size-8 items-center justify-center rounded-lg shadow-md backdrop-blur-sm transition-all\"\n aria-label={t(\"common_more_options_aria\")}\n >\n <MoreVertical className=\"text-foreground size-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n onClick={(e) => e.stopPropagation()}\n >\n {onToggleFavorite && (\n <DropdownMenuItem onClick={onToggleFavorite}>\n <Heart\n className={cn(\n \"mr-2 h-4 w-4\",\n isFavorited && \"text-destructive fill-current\",\n )}\n />\n {isFavorited\n ? t(\"common_unfavorite\")\n : t(\"common_favorite\")}\n </DropdownMenuItem>\n )}\n {canEdit && onEdit && (\n <DropdownMenuItem onClick={onEdit}>\n <Pencil className=\"mr-2 size-4\" />\n {t(\"common_edit\")}\n </DropdownMenuItem>\n )}\n {canEdit && onDelete && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n variant=\"destructive\"\n onClick={onDelete}\n >\n <Trash2 className=\"mr-2 size-4\" />\n {t(\"common_delete\")}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n )}\n\n {/* Radial gradient behind heart for visibility */}\n {onToggleFavorite && (\n <div className=\"pointer-events-none absolute -bottom-8 -left-8 z-9 size-24 rounded-full bg-[#00000020] blur-xl\" />\n )}\n\n {/* Favorite heart */}\n {onToggleFavorite && (\n <button\n type=\"button\"\n className=\"absolute bottom-3 left-3 z-10 cursor-pointer transition-transform hover:scale-110\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleFavorite();\n }}\n aria-label={\n isFavorited ? t(\"common_unfavorite\") : t(\"common_favorite\")\n }\n >\n <Heart\n className={cn(\n \"h-6 w-6 drop-shadow-lg transition-all\",\n isFavorited\n ? \"fill-destructive text-destructive\"\n : \"text-[#ffffff]\",\n )}\n />\n </button>\n )}\n\n {/* Item count badge */}\n <div className=\"absolute right-2 bottom-2\">\n <Badge\n className=\"bg-foreground text-background shadow-lg backdrop-blur-sm\"\n variant=\"secondary\"\n >\n {badgeText}\n </Badge>\n </div>\n </div>\n\n {/* Title */}\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {title || t(\"common_untitled\")}\n </h3>\n </div>\n </div>\n </div>\n </button>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { X } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport interface BulkSelectionBarProps {\n selectedCount: number;\n totalCount: number;\n onSelectAll: () => void;\n onClearSelection: () => void;\n onBulkFavorite: () => void;\n}\n\nexport function BulkSelectionBar({\n selectedCount,\n totalCount,\n onSelectAll,\n onClearSelection,\n onBulkFavorite,\n}: BulkSelectionBarProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n\n return (\n <div className=\"bg-foreground flex items-center justify-between rounded-lg px-4 py-3\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={onSelectAll}\n className=\"text-background hover:text-background/80 text-sm font-medium\"\n >\n {selectedCount === totalCount\n ? t(\"common_deselect_all\")\n : t(\"common_select_all\")}\n </button>\n <span className=\"text-background/70 text-sm\">\n {t(\"common_selected_count\", { count: String(selectedCount) })}\n </span>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button\n onClick={onBulkFavorite}\n disabled={selectedCount === 0}\n variant=\"secondary\"\n size=\"sm\"\n className=\"bg-background text-foreground hover:bg-background/90 rounded-md px-3 py-1.5 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t(\"common_favorite\")}\n </Button>\n <button\n type=\"button\"\n onClick={onClearSelection}\n className=\"text-background hover:text-background/80\"\n aria-label={t(\"common_cancel_selection_aria\")}\n >\n <X className=\"size-4\" />\n </button>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport {\n useInfiniteQuery,\n useMutation,\n useQueryClient,\n keepPreviousData,\n} from \"@tanstack/react-query\";\nimport { useShareablesApi, shareablesKeys } from \"@fluid-app/shareables-core\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport {\n Badge,\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Button,\n DialogFooter,\n Checkbox,\n cn,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Spinner,\n} from \"@fluid-app/ui-primitives\";\nimport { Heart, MoreVertical, Pencil, Plus, Trash2 } from \"lucide-react\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { useShareablesUI, useCanCuratePlaylists } from \"../../context\";\nimport { PlaylistCard } from \"../playlists/PlaylistCard\";\nimport { BulkSelectionBar } from \"../playlists/BulkSelectionBar\";\nimport {\n OwnerFilterTabs,\n getFilteredEmptyMessage,\n type OwnerFilter,\n} from \"../OwnerFilterTabs\";\nimport { ShareableListLayout } from \"../shared/ShareableListLayout\";\nimport {\n ShareableListRow,\n SHAREABLE_LIST_CLASS,\n} from \"../shared/ShareableListRow\";\nimport { ViewModeToggle, type ViewMode } from \"../shared/ViewModeToggle\";\nimport {\n ShareableFilterBarLayout,\n SHAREABLE_SEARCH_SORT_CLASS,\n} from \"../shared/ShareableFilterBarLayout\";\n\nconst PAGE_SIZE = 12;\n\n// Playlists use a narrower grid than the default shareable grid.\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\";\n\nexport interface PlaylistsListingScreenProps {}\n\nexport function PlaylistsListingScreen(\n _props: PlaylistsListingScreenProps,\n): React.JSX.Element {\n const api = useShareablesApi();\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const { navigate, showToast, user, onToggleFavorite, onDeletePlaylist } =\n useShareablesUI();\n const canCuratePlaylists = useCanCuratePlaylists();\n const queryClient = useQueryClient();\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [sortValue, setSortValue] = useState(\"title\");\n const [ownerFilter, setOwnerFilter] = useState<OwnerFilter>(\"all\");\n const [viewMode, setViewMode] = useState<ViewMode>(\"grid\");\n const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());\n const [pendingDeleteId, setPendingDeleteId] = useState<number | null>(null);\n const [actionPlaylistId, setActionPlaylistId] = useState<number | null>(null);\n\n useEffect(() => {\n setSelectedIds(new Set());\n }, [ownerFilter]);\n\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(searchTerm), 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n const observerTarget = useRef<HTMLDivElement>(null);\n\n const queryKey = [\n ...shareablesKeys.playlists.list(debouncedSearch, sortValue, ownerFilter),\n ];\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey,\n queryFn: async ({ pageParam }) => {\n const response = await api.playlists.getPlaylists({\n \"filter[title]\": debouncedSearch || undefined,\n \"filter[ownership]\": ownerFilter !== \"all\" ? ownerFilter : undefined,\n \"page[cursor]\": pageParam,\n \"page[limit]\": PAGE_SIZE,\n sort: sortValue,\n });\n return {\n playlists: response.playlists,\n nextCursor: response.meta.pagination.next_cursor,\n };\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,\n initialPageParam: undefined as string | undefined,\n placeholderData: keepPreviousData,\n });\n\n const filteredPlaylists = useMemo(\n () => data?.pages.flatMap((page) => page.playlists) ?? [],\n [data?.pages],\n );\n\n // Infinite scroll\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n { threshold: 0, rootMargin: \"200px\" },\n );\n observer.observe(target);\n return () => observer.disconnect();\n }, [fetchNextPage, hasNextPage, isFetchingNextPage]);\n\n // --- Selection ---\n const hasSelection = selectedIds.size > 0;\n\n const handleToggleSelection = useCallback((id: number, selected: boolean) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (selected) next.add(id);\n else next.delete(id);\n return next;\n });\n }, []);\n\n const handleSelectAll = useCallback(() => {\n if (selectedIds.size === filteredPlaylists.length) {\n setSelectedIds(new Set());\n } else {\n setSelectedIds(new Set(filteredPlaylists.map((p) => p.id)));\n }\n }, [selectedIds.size, filteredPlaylists]);\n\n const handleClearSelection = useCallback(() => {\n setSelectedIds(new Set());\n }, []);\n\n // --- Favorites ---\n const favoriteMutation = useMutation({\n mutationFn: (playlistId: number) => {\n if (!onToggleFavorite) {\n return Promise.reject(new Error(\"Favorite not available\"));\n }\n return onToggleFavorite({\n favoriteableId: playlistId,\n favoriteableType: \"Library\",\n });\n },\n onMutate: async (playlistId) => {\n await queryClient.cancelQueries({ queryKey });\n const previousData = queryClient.getQueryData(queryKey);\n\n queryClient.setQueryData<typeof data>(queryKey, (oldData) => {\n if (!oldData) return oldData;\n return {\n ...oldData,\n pages: oldData.pages.map((page) => ({\n ...page,\n playlists: page.playlists.map((p) =>\n p.id === playlistId ? { ...p, is_favorited: !p.is_favorited } : p,\n ),\n })),\n };\n });\n\n return { previousData, capturedKey: queryKey };\n },\n onError: (_err, _playlistId, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(context.capturedKey, context.previousData);\n }\n showToast({ title: t(\"playlist_favorite_error\"), type: \"error\" });\n },\n onSuccess: (result) => {\n showToast({\n title: result.is_favorited\n ? t(\"playlist_favorite_added\")\n : t(\"playlist_favorite_removed\"),\n type: \"success\",\n });\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: shareablesKeys.playlists.all });\n },\n });\n\n const handleFavorite = useCallback(\n (playlistId: number) => {\n if (!onToggleFavorite) return;\n favoriteMutation.mutate(playlistId);\n },\n [onToggleFavorite, favoriteMutation],\n );\n\n const handleBulkFavorite = useCallback(() => {\n for (const id of selectedIds) {\n handleFavorite(id);\n }\n setSelectedIds(new Set());\n }, [selectedIds, handleFavorite]);\n\n // --- Card actions ---\n const handleEdit = useCallback(\n (playlistId: number) => {\n navigate(`playlists/${playlistId}/edit`);\n },\n [navigate],\n );\n\n const { mutate: deletePlaylist, isPending: isDeleting } = useMutation({\n mutationFn: (playlistId: number) => {\n if (!onDeletePlaylist) {\n return Promise.reject(new Error(\"Delete not available\"));\n }\n return onDeletePlaylist(playlistId);\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: shareablesKeys.playlists.all });\n showToast({ title: t(\"playlist_delete_success\"), type: \"success\" });\n setPendingDeleteId(null);\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"playlist_delete_error\"),\n type: \"error\",\n error,\n });\n setPendingDeleteId(null);\n },\n });\n\n const confirmDelete = useCallback(() => {\n if (pendingDeleteId != null) deletePlaylist(pendingDeleteId);\n }, [pendingDeleteId, deletePlaylist]);\n\n const sortOptions = useMemo(\n () => [\n { label: t(\"sort_name_asc\"), value: \"title\" },\n { label: t(\"sort_name_desc\"), value: \"-title\" },\n { label: t(\"sort_date_newest\"), value: \"-created_at\" },\n { label: t(\"sort_date_oldest\"), value: \"created_at\" },\n ],\n [t],\n );\n\n const filterBar = hasSelection ? (\n <BulkSelectionBar\n selectedCount={selectedIds.size}\n totalCount={filteredPlaylists.length}\n onSelectAll={handleSelectAll}\n onClearSelection={handleClearSelection}\n onBulkFavorite={handleBulkFavorite}\n />\n ) : (\n <ShareableFilterBarLayout\n tabs={\n <OwnerFilterTabs\n value={ownerFilter}\n onValueChange={setOwnerFilter}\n myLabel={t(\"playlist_my_playlists_tab\")}\n fullWidthOnMobile\n />\n }\n controls={\n <>\n <div className={SHAREABLE_SEARCH_SORT_CLASS}>\n <SearchSort\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n searchValue={searchTerm}\n onSearchChange={setSearchTerm}\n placeholder={t(\"playlist_search_placeholder\")}\n sortOptions={sortOptions}\n sortValue={sortValue}\n onSortChange={setSortValue}\n />\n </div>\n\n <ViewModeToggle value={viewMode} onChange={setViewMode} />\n </>\n }\n />\n );\n\n const footer = (\n <>\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary size-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n {error && (\n <p className=\"bg-destructive/10 text-destructive rounded-lg px-3 py-2\">\n Error: {error.message}\n </p>\n )}\n </>\n );\n\n const isEmpty =\n filteredPlaylists.length === 0 && !isFetchingNextPage && !hasNextPage;\n\n return (\n <>\n <ScreenHeaderActions>\n {canCuratePlaylists ? (\n <Button onClick={() => navigate(\"playlists/new\")} size=\"sm\">\n <Plus className=\"mr-1 size-4\" />\n {t(\"playlist_create_button\")}\n </Button>\n ) : null}\n </ScreenHeaderActions>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"playlist_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableListLayout\n isLoading={isLoading}\n filters={filterBar}\n isEmpty={isEmpty}\n emptyMessage={getFilteredEmptyMessage({\n searchTerm,\n ownerFilter,\n entityName: \"playlists\",\n defaultMessage: t(\"playlist_empty_default\"),\n t,\n messages: {\n filterSearchNoResults: t(\"common_filter_search_no_results\", {\n entityName: \"playlists\",\n searchTerm,\n }),\n searchNoResults: t(\"common_search_no_results\", {\n entityName: \"playlists\",\n searchTerm,\n }),\n filterNoResults: t(\"common_filter_no_results\", {\n entityName: \"playlists\",\n }),\n },\n })}\n footer={footer}\n sentinelRef={observerTarget}\n loadingFilterShape=\"search-view\"\n >\n {viewMode === \"grid\" ? (\n <div className={GRID_CLASS}>\n {filteredPlaylists.map((playlist) => {\n const firstItem = playlist.items?.[0];\n const imageUrl =\n playlist.image_url ??\n firstItem?.image_url ??\n firstItem?.relateable?.image_url ??\n firstItem?.relateable?.compressed_image_url;\n const itemCount =\n playlist.items_count ?? playlist.items?.length ?? 0;\n const canEdit =\n canCuratePlaylists && playlist.user_id === user?.id;\n\n return (\n <PlaylistCard\n key={playlist.id}\n title={playlist.title || t(\"playlist_untitled\")}\n imageUrl={imageUrl}\n href={`playlists/${playlist.id}`}\n itemCount={itemCount}\n isFavorited={playlist.is_favorited}\n isSelectable\n isSelected={selectedIds.has(playlist.id)}\n canEdit={canEdit}\n onSelectionChange={(selected) =>\n handleToggleSelection(playlist.id, selected)\n }\n onToggleFavorite={\n onToggleFavorite\n ? () => handleFavorite(playlist.id)\n : undefined\n }\n onEdit={() => handleEdit(playlist.id)}\n onDelete={\n onDeletePlaylist\n ? () => setPendingDeleteId(playlist.id)\n : undefined\n }\n />\n );\n })}\n </div>\n ) : (\n <div className={SHAREABLE_LIST_CLASS}>\n {filteredPlaylists.map((playlist) => {\n const firstItem = playlist.items?.[0];\n const imageUrl =\n playlist.image_url ??\n firstItem?.image_url ??\n firstItem?.relateable?.image_url ??\n firstItem?.relateable?.compressed_image_url;\n const itemCount =\n playlist.items_count ?? playlist.items?.length ?? 0;\n const canEdit =\n canCuratePlaylists && playlist.user_id === user?.id;\n const isSelected = selectedIds.has(playlist.id);\n const title = playlist.title || t(\"playlist_untitled\");\n\n return (\n <ShareableListRow\n key={playlist.id}\n imageUrl={imageUrl}\n title={title}\n subtitle={\n <Badge\n variant=\"secondary\"\n className=\"px-1.5 py-0 text-[10px] leading-4\"\n >\n {itemCount === 1\n ? t(\"playlist_item_count_one\", { count: \"1\" })\n : t(\"playlist_item_count_other\", {\n count: String(itemCount),\n })}\n </Badge>\n }\n onClick={() => navigate(`playlists/${playlist.id}`)}\n leading={\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) =>\n handleToggleSelection(playlist.id, checked === true)\n }\n aria-label={t(\"playlist_select_aria\", { title })}\n />\n }\n trailing={\n <>\n {onToggleFavorite && (\n <button\n type=\"button\"\n onClick={() => handleFavorite(playlist.id)}\n aria-label={\n playlist.is_favorited\n ? t(\"common_unfavorite\")\n : t(\"common_favorite\")\n }\n className=\"hover:bg-muted-foreground/10 flex size-8 items-center justify-center rounded-md transition-colors\"\n >\n <Heart\n className={cn(\n \"h-4 w-4 transition-colors\",\n playlist.is_favorited\n ? \"fill-destructive text-destructive\"\n : \"text-muted-foreground\",\n )}\n />\n </button>\n )}\n {canEdit &&\n (isMobile ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={t(\"playlist_actions_aria\")}\n onClick={() => setActionPlaylistId(playlist.id)}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={t(\"playlist_actions_aria\")}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={() => handleEdit(playlist.id)}\n >\n <Pencil className=\"mr-2 size-4\" />\n {t(\"common_edit\")}\n </DropdownMenuItem>\n {onDeletePlaylist && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n variant=\"destructive\"\n onClick={() =>\n setPendingDeleteId(playlist.id)\n }\n >\n <Trash2 className=\"mr-2 size-4\" />\n {t(\"common_delete\")}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n ))}\n </>\n }\n />\n );\n })}\n </div>\n )}\n </ShareableListLayout>\n\n <MobileActionSheet\n open={actionPlaylistId !== null}\n onOpenChange={(open) => {\n if (!open) setActionPlaylistId(null);\n }}\n title={t(\"playlist_actions_aria\")}\n actions={[\n {\n id: \"edit\",\n label: t(\"common_edit\"),\n onSelect: () => {\n if (actionPlaylistId !== null) handleEdit(actionPlaylistId);\n },\n },\n ...(onDeletePlaylist\n ? [\n {\n id: \"delete\",\n label: t(\"common_delete\"),\n variant: \"destructive\" as const,\n onSelect: () => setPendingDeleteId(actionPlaylistId),\n },\n ]\n : []),\n ]}\n />\n\n <ResponsiveDialog\n open={pendingDeleteId !== null}\n onOpenChange={(open) => {\n if (!open && !isDeleting) setPendingDeleteId(null);\n }}\n title={t(\"playlist_delete_confirm_title\")}\n description={t(\"playlist_delete_confirm_description\")}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n role=\"alertdialog\"\n size=\"compact\"\n >\n <DialogFooter>\n <Button\n variant=\"secondary\"\n onClick={() => setPendingDeleteId(null)}\n disabled={isDeleting}\n >\n {t(\"common_cancel\")}\n </Button>\n <Button\n variant=\"destructive\"\n onClick={confirmDelete}\n disabled={isDeleting}\n >\n {isDeleting ? <Spinner className=\"size-4\" /> : t(\"common_delete\")}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n </>\n );\n}\n","export const NAVIGABLE_RELATEABLE_TYPES = new Set<string>([\n \"Product\",\n \"Page\",\n \"Medium\",\n]);\n","\"use client\";\n\nimport type React from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage } from \"../../context\";\nimport { ListMusic, Play, ArrowUpRight } from \"lucide-react\";\nimport type { shareables } from \"@fluid-app/shareables-core\";\nimport { NAVIGABLE_RELATEABLE_TYPES } from \"../../constants\";\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\ninterface PlaylistItemsListProps {\n items: shareables.PlaylistItem[];\n onSelectItem?: (index: number) => void;\n selectedItemIndex?: number;\n /** Called when user clicks to navigate to an item's detail page */\n onNavigateToItem?: (itemId: number, relateableType?: string | null) => void;\n}\n\nfunction getItemType(\n type: string | null | undefined,\n kind: string | null | undefined,\n labels: {\n product: string;\n enrollment: string;\n page: string;\n video: string;\n image: string;\n media: string;\n },\n) {\n if (type === \"Product\") return labels.product;\n if (type === \"EnrollmentPack\") return labels.enrollment;\n if (type === \"Page\") return labels.page;\n if (kind === \"video\") return labels.video;\n if (kind === \"image\") return labels.image;\n return labels.media;\n}\n\nfunction formatVideoLength(seconds?: number) {\n if (seconds == null) return null;\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${String(mins).padStart(2, \"0\")}:${String(secs).padStart(2, \"0\")}`;\n}\n\nfunction getFileType(mediaFormat?: string | null, imageUrl?: string | null) {\n if (mediaFormat) {\n return mediaFormat.toLowerCase();\n }\n if (imageUrl) {\n const urlParts = imageUrl.split(\".\");\n const extension = urlParts[urlParts.length - 1]?.split(\"?\")[0];\n if (extension) {\n return extension.toLowerCase();\n }\n }\n return null;\n}\n\nexport default function PlaylistItemsList({\n items,\n onSelectItem,\n selectedItemIndex = 0,\n onNavigateToItem,\n}: PlaylistItemsListProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const handleNavigateToItem = (\n itemId: number,\n type?: string | null,\n e?: React.MouseEvent,\n ) => {\n e?.stopPropagation();\n onNavigateToItem?.(itemId, type);\n };\n\n const handleCardClick = (index: number) => {\n onSelectItem?.(index);\n };\n\n const renderImage = useRenderImage();\n\n const renderContent = () => {\n if (!items || items.length === 0) {\n return (\n <div className=\"flex flex-1 items-center justify-center px-6 py-12\">\n <div className=\"text-center\">\n <div className=\"mb-2 text-gray-400\">\n <ListMusic className=\"mx-auto size-12\" />\n </div>\n <div className=\"mb-1 text-sm text-gray-500\">\n {t(\"playlist_no_items\")}\n </div>\n <div className=\"text-xs text-gray-400\">\n {t(\"playlist_no_items_description\")}\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {items.map((item, index) => {\n const relateable = item.relateable;\n const imageUrl =\n item.image_url ??\n relateable?.image_url ??\n relateable?.compressed_image_url ??\n DEFAULT_IMAGE;\n const title = item.title ?? relateable?.title ?? t(\"common_untitled\");\n const rawPrice = relateable?.display_price || relateable?.price;\n const price =\n rawPrice && parseFloat(rawPrice.replace(/[^0-9.-]/g, \"\")) > 0\n ? rawPrice\n : null;\n const kind = item.kind ?? relateable?.kind;\n const itemType = getItemType(item.relateable_type, kind, {\n product: t(\"common_product\"),\n enrollment: t(\"common_enrollment\"),\n page: t(\"common_page\"),\n video: t(\"media_type_video\"),\n image: t(\"media_type_image\"),\n media: t(\"media_type_default\"),\n });\n const isVideo = kind === \"video\";\n const isImage = kind === \"image\";\n const isProduct = item.relateable_type === \"Product\";\n const isNavigable = NAVIGABLE_RELATEABLE_TYPES.has(\n item.relateable_type ?? \"\",\n );\n const videoLength = formatVideoLength(\n item.duration ?? relateable?.duration,\n );\n const fileType = getFileType(\n item.media_format ?? relateable?.media_format,\n item.image_url ?? relateable?.image_url,\n );\n const isSelected = index === selectedItemIndex;\n\n return (\n <div\n key={item.id}\n onClick={() => handleCardClick(index)}\n className={`hover:bg-muted/60 flex w-full cursor-pointer items-start gap-3 rounded-lg p-3 text-left transition-all ${\n isSelected ? \"bg-muted/60\" : \"bg-muted\"\n }`}\n >\n {/* Thumbnail */}\n <div className=\"bg-background relative h-[104px] w-[104px] shrink-0 overflow-hidden rounded-lg\">\n {renderImage({\n src: imageUrl,\n alt: title,\n fill: true,\n className: \"object-cover\",\n })}\n {/* Play button overlay for videos */}\n {isVideo && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-background/90 flex size-10 items-center justify-center rounded-full shadow-md\">\n <Play className=\"text-foreground ml-0.5 size-4\" />\n </div>\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col gap-1 py-1\">\n {/* Title - clickable to navigate for supported types */}\n {isNavigable ? (\n <button\n onClick={(e) =>\n handleNavigateToItem(\n relateable?.id ?? item.id,\n item.relateable_type,\n e,\n )\n }\n className=\"group text-foreground flex items-start gap-1.5 text-left text-[15px] leading-[1.4] font-semibold hover:underline\"\n >\n <span className=\"line-clamp-2\">{title}</span>\n <ArrowUpRight className=\"text-foreground mt-0.5 size-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100\" />\n </button>\n ) : (\n <div className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n <span className=\"line-clamp-2\">{title}</span>\n </div>\n )}\n\n {/* Product: Price */}\n {isProduct && price && (\n <div className=\"text-foreground text-[15px] leading-[1.4] font-normal\">\n {price}\n </div>\n )}\n\n {/* Video: Duration */}\n {isVideo && (\n <div className=\"text-muted-foreground text-[13px] leading-[1.4] font-normal\">\n {videoLength || t(\"media_type_video\")}\n </div>\n )}\n\n {/* Image: File type */}\n {isImage && (\n <div className=\"text-muted-foreground text-[13px] leading-[1.4] font-normal\">\n {fileType || t(\"file_type_image_fallback\")}\n </div>\n )}\n\n {/* Badge */}\n <div className=\"mt-1\">\n <span className=\"bg-background text-foreground inline-flex items-center rounded-md px-2.5 py-1 text-[13px] leading-[1.4] font-medium\">\n {itemType}\n </span>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n };\n\n return (\n <div className=\"hide-scrollbar flex h-full flex-col\">\n {/* Header */}\n <div className=\"mb-4 px-4 pt-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n {t(\"playlist_items_in_playlist_count\", {\n count: String(items.length),\n })}\n </h2>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto px-4 pb-4\">{renderContent()}</div>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { NAVIGABLE_RELATEABLE_TYPES } from \"../../constants\";\nimport {\n useShareablesApi,\n useShareLink,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Separator,\n} from \"@fluid-app/ui-primitives\";\nimport { Pencil } from \"lucide-react\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport AssetActions from \"../SharePage/AssetActions\";\nimport TaggedProductsList from \"../SharePage/TaggedProductsList\";\nimport PlaylistItemsList from \"../SharePage/PlaylistItemsList\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesUI, useCanCuratePlaylists } from \"../../context\";\nimport type { ShareablesParentBreadcrumb } from \"../shareables-parent-breadcrumb-types\";\nimport { ShareableDetailLayout } from \"../shared/ShareableDetailLayout\";\n\nconst DEFAULT_IMAGE =\n \"https://ik.imagekit.io/fluid/tr:w-1500,h-1500,cm-pad_resize,bg-FFFFFF/980191006/images/WJHL8V/WeCommerce_Logotype-Black_NcO-MzatB.png\";\n\nexport interface PlaylistDetailScreenProps {\n playlistId: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n parentBreadcrumb?: ShareablesParentBreadcrumb | null;\n}\n\nexport function PlaylistDetailScreen({\n playlistId,\n onNavigate,\n onBack,\n parentBreadcrumb,\n}: PlaylistDetailScreenProps): React.JSX.Element {\n const api = useShareablesApi();\n const { t, locale } = useShareablesTranslation();\n const { navigate, user } = useShareablesUI();\n const canCuratePlaylists = useCanCuratePlaylists();\n const [selectedPlaylistItemIndex, setSelectedPlaylistItemIndex] = useState(0);\n\n // Fetch playlist\n const { data: playlistResponse, isLoading } = useQuery({\n queryKey: shareablesKeys.playlists.detail(Number(playlistId), locale),\n queryFn: () => api.playlists.getPlaylistById(Number(playlistId), locale),\n });\n\n // Share link — relateableType for playlist is \"Library\"\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink({ id: Number(playlistId) }, \"Library\", undefined, locale);\n\n const playlist = playlistResponse?.playlist;\n\n const canEdit = canCuratePlaylists && playlist?.user_id === user?.id;\n const displayTitle = playlist?.title || \"\";\n\n // Tagged products section: Product + EnrollmentPack — mirrors the\n // editor's \"Tagged Products\" bucket so the detail page groups items\n // the same way authors organize them.\n const taggedProducts =\n playlist?.items\n ?.filter(\n (item) =>\n item.relateable_type === \"Product\" ||\n item.relateable_type === \"EnrollmentPack\",\n )\n .map((item) => item.relateable)\n .filter((p): p is NonNullable<typeof p> => !!p) || [];\n\n // Playlist items section: Medium + Page only — the rest belong to the\n // tagged-products bucket above. Filter at the call site instead of in\n // PlaylistItemsList so the component stays type-agnostic.\n const playableItems =\n playlist?.items?.filter(\n (item) =>\n item.relateable_type === \"Medium\" || item.relateable_type === \"Page\",\n ) ?? [];\n\n // Selected item drives the hero image/video — index into `playableItems`\n // since that's what PlaylistItemsList renders. Falls back to the first\n // item when the index is out of range (e.g. after a filter shrinks the\n // list).\n const selectedPlaylistItem =\n playableItems[selectedPlaylistItemIndex] ?? playableItems[0];\n\n // Derive display values\n const displayImage =\n selectedPlaylistItem?.image_url ??\n selectedPlaylistItem?.relateable?.image_url ??\n selectedPlaylistItem?.relateable?.compressed_image_url ??\n DEFAULT_IMAGE;\n const displayDescription =\n playlist?.description ||\n playlist?.search_engine_optimizer?.description ||\n \"\";\n\n // Handle video display for playlist items\n const selectedKind =\n selectedPlaylistItem?.kind ?? selectedPlaylistItem?.relateable?.kind;\n const displayVideo =\n selectedKind === \"video\"\n ? (selectedPlaylistItem?.video_url ??\n selectedPlaylistItem?.relateable?.video_url ??\n undefined)\n : undefined;\n const isVideo = selectedKind === \"video\" && !!displayVideo;\n\n return (\n <>\n <ScreenHeaderActions>\n {canEdit ? (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate(`playlists/${playlistId}/edit`)}\n >\n <Pencil className=\"mr-1 size-3.5\" />\n {t(\"common_edit\")}\n </Button>\n </div>\n ) : null}\n </ScreenHeaderActions>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (\n parentBreadcrumb?.onClick ??\n onBack ??\n (() => navigate(\"playlists\"))\n )();\n }}\n >\n {t(\"playlist_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"block max-w-[60vw] truncate align-bottom font-semibold md:max-w-[60ch]\">\n {displayTitle || t(\"playlist_breadcrumb_fallback\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableDetailLayout\n isLoading={isLoading}\n notFound={!playlist}\n notFoundMessage={t(\"playlist_not_found\")}\n title={displayTitle}\n description={displayDescription}\n image={\n <div className=\"relative h-full overflow-hidden rounded-2xl\">\n <SharePageImageDisplay\n displayImage={displayImage}\n displayTitle={displayTitle}\n displayVideo={displayVideo}\n isVideo={isVideo}\n badgeLabel={t(\"playlist_badge_label\")}\n pdfPrevAriaLabel={t(\"pdf_previous_page_aria\")}\n pdfNextAriaLabel={t(\"pdf_next_page_aria\")}\n pdfDocumentTitle={t(\"pdf_document_title\")}\n />\n </div>\n }\n actions={\n <AssetActions\n downloadUrl={null}\n displayTitle={displayTitle}\n shareLink={shareLinkError ? null : shareLink || null}\n shareLinkLoading={shareLinkLoading}\n isVideo={isVideo}\n relateableId={Number(playlistId)}\n relateableType=\"Library\"\n />\n }\n >\n <Separator className=\"border-foreground my-4\" />\n\n <div className=\"bg-background rounded-lg\">\n {/* Tagged Products Section */}\n <TaggedProductsList\n products={taggedProducts}\n onProductClick={(productId) =>\n onNavigate?.(\"product\", String(productId))\n }\n />\n\n {/* Playlist Items Section */}\n <PlaylistItemsList\n items={playableItems}\n onSelectItem={setSelectedPlaylistItemIndex}\n selectedItemIndex={selectedPlaylistItemIndex}\n onNavigateToItem={(itemId, relateableType) => {\n if (!NAVIGABLE_RELATEABLE_TYPES.has(relateableType ?? \"\")) return;\n if (relateableType === \"Product\") {\n onNavigate?.(\"product\", String(itemId));\n } else if (relateableType === \"Page\") {\n onNavigate?.(\"page\", String(itemId));\n } else if (relateableType === \"Medium\") {\n onNavigate?.(\"media\", String(itemId));\n }\n }}\n />\n </div>\n </ShareableDetailLayout>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport {\n createContext,\n use,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport type PlaylistSearchEngineOptimizer = {\n id?: number;\n title: string;\n description: string;\n image_url: string;\n image_path: string;\n block_crawler: boolean;\n};\n\nexport type PlaylistFormState = {\n id?: number;\n title: string;\n description: string | null;\n image_url: string | null;\n slug: string | null;\n custom_slug: boolean | null;\n category_id: number | null;\n application_theme_template_id: number | null;\n active: boolean;\n company_library: boolean | null;\n indexed_for_seo: boolean;\n countries: string[];\n search_engine_optimizer: PlaylistSearchEngineOptimizer | null;\n};\n\nexport type PlaylistLike = {\n id: number;\n title?: string | null;\n description?: string | null;\n image_url?: string | null;\n slug?: string | null;\n custom_slug?: boolean | null;\n category_id?: number | null;\n application_theme_template_id?: number | null;\n active?: boolean;\n company_library?: boolean | null;\n indexed_for_seo?: boolean;\n countries?: Array<{ iso: string }> | null;\n search_engine_optimizer?: PlaylistSearchEngineOptimizer | null;\n canonical_url?: string | null;\n preview_link?: string | null;\n items?: Array<unknown> | null;\n};\n\ntype ValidationErrors = {\n [K in keyof PlaylistFormState]?: string;\n};\n\ninterface PlaylistFormContextValue {\n form: PlaylistFormState;\n validationErrors: ValidationErrors;\n isDirty: boolean;\n updateField: <K extends keyof PlaylistFormState>(\n field: K,\n value: PlaylistFormState[K],\n ) => void;\n updateSeo: (\n updates: Partial<PlaylistSearchEngineOptimizer> & {\n slug?: string | null;\n },\n ) => void;\n validateForm: () => boolean;\n clearFieldError: (field: keyof PlaylistFormState) => void;\n resetForm: (nextPlaylist?: PlaylistLike) => void;\n}\n\nconst PlaylistFormContext = createContext<PlaylistFormContextValue | undefined>(\n undefined,\n);\n\nconst createDefaultSeo = (title: string): PlaylistSearchEngineOptimizer => ({\n title: title || \"\",\n description: \"\",\n image_url: \"\",\n image_path: \"\",\n block_crawler: false,\n});\n\nconst mapPlaylistToForm = (playlist?: PlaylistLike): PlaylistFormState => ({\n id: playlist?.id,\n title: playlist?.title ?? \"\",\n description: playlist?.description ?? null,\n image_url: playlist?.image_url ?? null,\n slug: playlist?.slug ?? \"\",\n custom_slug: playlist?.custom_slug ?? false,\n category_id: playlist?.category_id ?? null,\n application_theme_template_id:\n playlist?.application_theme_template_id ?? null,\n active: playlist?.active ?? false,\n company_library: playlist?.company_library ?? true,\n indexed_for_seo: playlist?.indexed_for_seo ?? false,\n countries: playlist?.countries?.map((country) => country.iso) ?? [],\n search_engine_optimizer: playlist?.search_engine_optimizer ?? null,\n});\n\nexport function PlaylistFormProvider({\n children,\n playlist,\n}: {\n children: React.ReactNode;\n playlist?: PlaylistLike;\n}): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const initialForm = useMemo(() => mapPlaylistToForm(playlist), [playlist]);\n const [form, setForm] = useState<PlaylistFormState>(initialForm);\n const [validationErrors, setValidationErrors] = useState<ValidationErrors>(\n {},\n );\n\n const isDirty = useMemo(() => {\n return JSON.stringify(form) !== JSON.stringify(initialForm);\n }, [form, initialForm]);\n\n const prevPlaylistRef = useRef<string>(JSON.stringify(playlist));\n\n useEffect(() => {\n const serialized = JSON.stringify(playlist);\n if (serialized === prevPlaylistRef.current) return;\n prevPlaylistRef.current = serialized;\n setForm(mapPlaylistToForm(playlist));\n }, [playlist]);\n\n const updateField = useCallback(\n <K extends keyof PlaylistFormState>(\n field: K,\n value: PlaylistFormState[K],\n ) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n if (validationErrors[field]) {\n setValidationErrors((prev) => {\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }\n },\n [validationErrors],\n );\n\n const updateSeo = useCallback(\n (\n updates: Partial<PlaylistSearchEngineOptimizer> & {\n slug?: string | null;\n },\n ) => {\n setForm((prev) => {\n const currentSeo =\n prev.search_engine_optimizer ?? createDefaultSeo(prev.title);\n const nextSeo = { ...currentSeo };\n\n if (updates.title !== undefined) nextSeo.title = updates.title ?? \"\";\n if (updates.description !== undefined)\n nextSeo.description = updates.description ?? \"\";\n if (updates.image_url !== undefined)\n nextSeo.image_url = updates.image_url ?? \"\";\n if (updates.image_path !== undefined)\n nextSeo.image_path = updates.image_path ?? \"\";\n if (updates.block_crawler !== undefined)\n nextSeo.block_crawler = updates.block_crawler ?? false;\n\n const nextSlug =\n updates.slug !== undefined ? (updates.slug ?? null) : prev.slug;\n\n return {\n ...prev,\n slug: nextSlug,\n search_engine_optimizer: nextSeo,\n };\n });\n },\n [],\n );\n\n const validateForm = useCallback(() => {\n const errors: ValidationErrors = {};\n if (!form.title || !form.title.trim()) {\n errors.title = t(\"playlist_title_required\");\n }\n setValidationErrors(errors);\n return Object.keys(errors).length === 0;\n }, [form.title, t]);\n\n const clearFieldError = useCallback((field: keyof PlaylistFormState) => {\n setValidationErrors((prev) => {\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }, []);\n\n const resetForm = useCallback(\n (nextPlaylist?: PlaylistLike) => {\n setForm(mapPlaylistToForm(nextPlaylist ?? playlist));\n setValidationErrors({});\n },\n [playlist],\n );\n\n const value = useMemo(\n () => ({\n form,\n validationErrors,\n isDirty,\n updateField,\n updateSeo,\n validateForm,\n clearFieldError,\n resetForm,\n }),\n [\n form,\n validationErrors,\n isDirty,\n updateField,\n updateSeo,\n validateForm,\n clearFieldError,\n resetForm,\n ],\n );\n\n return (\n <PlaylistFormContext.Provider value={value}>\n {children}\n </PlaylistFormContext.Provider>\n );\n}\n\nexport function usePlaylistForm() {\n const context = use(PlaylistFormContext);\n if (!context) {\n throw new Error(\"usePlaylistForm must be used within PlaylistFormProvider\");\n }\n return context;\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport {\n createContext,\n use,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport interface PlaylistItemLike {\n id: number;\n order?: number;\n relateable_type?: string | null;\n // Flat fields from BFF (preferred over relateable nested fields)\n title?: string | null;\n image_url?: string | null;\n kind?: string | null;\n relateable?: {\n id?: number;\n title?: string | null;\n image_url?: string | null;\n compressed_image_url?: string | null;\n kind?: string | null;\n video_url?: string | null;\n price?: string | null;\n sku?: string | null;\n description?: { body?: string | null } | null;\n [key: string]: unknown;\n };\n}\n\nexport interface PlaylistItemsContextValue {\n items: PlaylistItemLike[];\n setItems: (items: PlaylistItemLike[]) => void;\n addItem: (item: PlaylistItemLike) => void;\n removeItem: (itemId: number) => void;\n updateItems: (\n items:\n | PlaylistItemLike[]\n | ((prev: PlaylistItemLike[]) => PlaylistItemLike[]),\n ) => void;\n isDirty: boolean;\n}\n\nconst PlaylistItemsContext = createContext<\n PlaylistItemsContextValue | undefined\n>(undefined);\n\nexport function usePlaylistItems() {\n const context = use(PlaylistItemsContext);\n if (!context) {\n throw new Error(\n \"usePlaylistItems must be used within PlaylistItemsProvider\",\n );\n }\n return context;\n}\n\nexport function PlaylistItemsProvider({\n children,\n initialItems = [],\n}: {\n children: React.ReactNode;\n initialItems?: PlaylistItemLike[];\n}): React.JSX.Element {\n const [items, setItems] = useState<PlaylistItemLike[]>(initialItems);\n const [initialSnapshot, setInitialSnapshot] =\n useState<PlaylistItemLike[]>(initialItems);\n\n const prevInitialRef = useRef<string>(JSON.stringify(initialItems));\n\n useEffect(() => {\n const serialized = JSON.stringify(initialItems);\n if (serialized === prevInitialRef.current) return;\n prevInitialRef.current = serialized;\n setItems(initialItems);\n setInitialSnapshot(initialItems);\n }, [initialItems]);\n\n const isDirty = useMemo(\n () => JSON.stringify(items) !== JSON.stringify(initialSnapshot),\n [items, initialSnapshot],\n );\n\n const addItem = useCallback((item: PlaylistItemLike) => {\n setItems((prev) => [...prev, item]);\n }, []);\n\n const removeItem = useCallback((itemId: number) => {\n setItems((prev) => prev.filter((item) => item.id !== itemId));\n }, []);\n\n const updateItems = useCallback(\n (\n newItems:\n | PlaylistItemLike[]\n | ((prev: PlaylistItemLike[]) => PlaylistItemLike[]),\n ) => {\n setItems((prev) =>\n typeof newItems === \"function\" ? newItems(prev) : newItems,\n );\n },\n [],\n );\n\n const value: PlaylistItemsContextValue = useMemo(\n () => ({\n items,\n setItems,\n addItem,\n removeItem,\n updateItems,\n isDirty,\n }),\n [items, addItem, removeItem, updateItems, isDirty],\n );\n\n return (\n <PlaylistItemsContext.Provider value={value}>\n {children}\n </PlaylistItemsContext.Provider>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useMemo, useState } from \"react\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Spinner,\n DialogFooter,\n} from \"@fluid-app/ui-primitives\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { MoreVertical, Trash2 } from \"lucide-react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport {\n ScreenHeaderActions,\n ScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { usePlaylistForm } from \"./PlaylistFormProvider\";\nimport { usePlaylistItems } from \"./PlaylistItemsContext\";\nimport { useShareablesUI } from \"../../../context\";\n\ninterface PlaylistFormHeaderProps {\n playlistId?: string;\n isEditMode: boolean;\n onSubmit: () => Promise<void>;\n isSaving: boolean;\n onBack?: () => void;\n}\n\nexport function PlaylistFormHeader({\n playlistId,\n isEditMode,\n onSubmit,\n isSaving,\n}: PlaylistFormHeaderProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const { form, isDirty: formIsDirty } = usePlaylistForm();\n const { isDirty: itemsIsDirty } = usePlaylistItems();\n const { onDeletePlaylist, showToast, navigate } = useShareablesUI();\n const isMobile = useIsMobile();\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isActionsOpen, setIsActionsOpen] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n\n const isFormValid = useMemo(() => {\n return !!(form.title && form.title.trim());\n }, [form.title]);\n\n const isDirty = formIsDirty || itemsIsDirty;\n const isMutating = isDeleting || isSaving;\n\n const handleDelete = async () => {\n if (!playlistId || !onDeletePlaylist) return;\n setIsDeleting(true);\n try {\n await onDeletePlaylist(Number(playlistId));\n showToast({ title: t(\"playlist_delete_success_full\"), type: \"success\" });\n navigate(\"playlists\");\n } catch (error) {\n showToast({\n title: t(\"playlist_delete_error\"),\n type: \"error\",\n error,\n });\n } finally {\n setIsDeleting(false);\n setIsDeleteOpen(false);\n }\n };\n\n return (\n <>\n <ScreenHeaderActions>\n <div className=\"flex items-center gap-2\">\n {playlistId &&\n onDeletePlaylist &&\n (isMobile ? (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={t(\"playlist_actions_aria\")}\n disabled={isMutating}\n onClick={() => setIsActionsOpen(true)}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n <MobileActionSheet\n open={isActionsOpen}\n onOpenChange={setIsActionsOpen}\n title={t(\"playlist_actions_aria\")}\n actions={[\n {\n id: \"delete\",\n label: t(\"common_delete\"),\n variant: \"destructive\",\n onSelect: () => setIsDeleteOpen(true),\n },\n ]}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"size-8 p-0\"\n aria-label={t(\"playlist_actions_aria\")}\n disabled={isMutating}\n >\n <MoreVertical className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={() => setIsDeleteOpen(true)}\n className=\"text-destructive focus:text-destructive\"\n >\n <Trash2 className=\"mr-2 size-4\" />\n {t(\"common_delete\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ))}\n <Button\n onClick={() => {\n onSubmit().catch(() => {});\n }}\n disabled={isMutating || !isDirty || !isFormValid}\n size=\"sm\"\n >\n {isSaving ? <Spinner className=\"size-4\" /> : t(\"common_save\")}\n </Button>\n </div>\n </ScreenHeaderActions>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n navigate(\"playlists\");\n }}\n >\n {t(\"playlist_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {isEditMode\n ? t(\"playlist_edit_breadcrumb\")\n : t(\"playlist_create_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ResponsiveDialog\n open={isDeleteOpen}\n onOpenChange={(open) => {\n if (!open && !isDeleting) setIsDeleteOpen(false);\n }}\n title={t(\"playlist_delete_confirm_title\")}\n description={t(\"playlist_delete_confirm_description\")}\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n role=\"alertdialog\"\n size=\"compact\"\n >\n <DialogFooter>\n <Button\n variant=\"secondary\"\n disabled={isDeleting}\n onClick={() => setIsDeleteOpen(false)}\n >\n {t(\"common_cancel\")}\n </Button>\n <Button\n variant=\"destructive\"\n onClick={(e) => {\n e.preventDefault();\n void handleDelete();\n }}\n disabled={isDeleting}\n >\n {isDeleting ? <Spinner className=\"size-4\" /> : t(\"common_delete\")}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { Input, Label, cn } from \"@fluid-app/ui-primitives\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { usePlaylistForm } from \"./PlaylistFormProvider\";\nimport { RichTextEditor } from \"../RichTextEditor\";\n\nexport function PlaylistFormDetails(): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const { form, updateField, validationErrors } = usePlaylistForm();\n\n return (\n <div className=\"border-border bg-card w-full rounded-lg border p-4 shadow-xs\">\n <div className=\"space-y-6\">\n <div>\n <h2 className=\"text-md text-foreground font-semibold\">\n {t(\"playlist_form_heading\")}\n </h2>\n <p className=\"text-muted-foreground text-sm\">\n {t(\"playlist_form_description\")}\n </p>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <Label className=\"text-foreground font-medium\">\n {t(\"playlist_name_label\")}\n </Label>\n <Input\n name=\"title\"\n placeholder={t(\"playlist_name_placeholder\")}\n value={form.title}\n onChange={(e) => updateField(\"title\", e.target.value)}\n required\n className={cn(\n validationErrors.title && \"ring-destructive ring-1\",\n )}\n />\n {validationErrors.title && (\n <p className=\"text-destructive text-xs\">\n {validationErrors.title}\n </p>\n )}\n </div>\n\n <div className=\"flex flex-col gap-2\">\n <Label className=\"text-foreground font-medium\">\n {t(\"common_description_label\")}\n </Label>\n <div className=\"border-border rounded-[6px] border shadow-xs\">\n <RichTextEditor\n value={form.description || \"\"}\n onChange={(value) => updateField(\"description\", value)}\n className=\"h-48 border-0\"\n editorClassName=\"h-36 overflow-auto\"\n placeholder={t(\"playlist_description_placeholder\")}\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useSortable } from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\n\ninterface SortableRowProps {\n children: React.ReactNode;\n id: string;\n enableReordering?: boolean;\n}\n\nexport function SortableRow({\n children,\n id,\n enableReordering = true,\n}: SortableRowProps): React.JSX.Element {\n const {\n attributes,\n listeners,\n setNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id, disabled: !enableReordering });\n\n const style = {\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n };\n\n return (\n <tr ref={setNodeRef} style={style} {...attributes}>\n {React.Children.map(children, (child, index) => {\n if (index === 0 && enableReordering) {\n return React.cloneElement(child as React.ReactElement, {\n ...listeners,\n });\n }\n return child;\n })}\n </tr>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n MoreHorizontal,\n Trash2,\n GripVertical,\n Image,\n ShoppingBag,\n FileText,\n Package,\n Video,\n} from \"lucide-react\";\nimport {\n DndContext,\n closestCenter,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from \"@dnd-kit/core\";\nimport {\n arrayMove,\n SortableContext,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport type { PlaylistItemLike } from \"./PlaylistItemsContext\";\nimport { SortableRow } from \"./SortableRow\";\n\nfunction typeBadgeIcon(relateableType: string, kind?: string): LucideIcon {\n const k = kind?.toLowerCase() ?? \"\";\n if (relateableType === \"Medium\") {\n if (k === \"video\" || k.includes(\"video\")) return Video;\n return Image;\n }\n if (relateableType === \"Page\") return FileText;\n if (relateableType === \"Product\") return ShoppingBag;\n if (relateableType === \"EnrollmentPack\") return Package;\n return ShoppingBag;\n}\n\nfunction getTypeDisplay(\n relateableType: string,\n relateableKind: string | undefined,\n t: ReturnType<typeof useShareablesTranslation>[\"t\"],\n) {\n switch (relateableType) {\n case \"Product\":\n return t(\"common_product\");\n case \"EnrollmentPack\":\n return t(\"common_enrollment_pack\");\n case \"Medium\": {\n const kind = relateableKind?.toLowerCase() ?? \"\";\n if (kind === \"video\" || kind.includes(\"video\"))\n return t(\"media_type_video\");\n if (kind === \"image\" || kind.includes(\"image\"))\n return t(\"media_type_image\");\n return relateableKind || t(\"media_type_default\");\n }\n case \"Page\":\n return t(\"common_page\");\n default:\n return relateableType?.replace(/_/g, \" \") || t(\"common_unknown\");\n }\n}\n\nfunction getItemIcon(relateableType: string) {\n switch (relateableType) {\n case \"Medium\":\n return <Image className=\"text-muted-foreground size-4\" />;\n case \"Page\":\n return <FileText className=\"text-muted-foreground size-4\" />;\n case \"Product\":\n return <ShoppingBag className=\"text-muted-foreground size-4\" />;\n case \"EnrollmentPack\":\n return <Package className=\"text-muted-foreground size-4\" />;\n default:\n return <Image className=\"text-muted-foreground size-4\" />;\n }\n}\n\ntype TableRow = {\n id: number | string;\n relateable_type?: string | null;\n relateable_id?: number;\n relateable?: PlaylistItemLike[\"relateable\"];\n order: number;\n title: string;\n image_url?: string | null;\n kind?: string | null;\n};\n\ninterface SortableTableProps {\n items: PlaylistItemLike[];\n setItems?: (items: PlaylistItemLike[]) => void;\n onDeleteItem: (itemId: number) => void;\n isDeletePending: boolean;\n enableReordering?: boolean;\n}\n\nexport function SortableTable({\n items,\n setItems,\n onDeleteItem,\n isDeletePending,\n enableReordering = true,\n}: SortableTableProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n const [actionItemId, setActionItemId] = useState<number | null>(null);\n\n const sensors = useSensors(\n useSensor(PointerSensor),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n }),\n );\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (active.id !== over?.id && setItems) {\n const oldIndex = items.findIndex(\n (item) => item.id?.toString() === active.id,\n );\n const newIndex = items.findIndex(\n (item) => item.id?.toString() === over?.id,\n );\n setItems(arrayMove(items, oldIndex, newIndex));\n }\n };\n\n const tableData: TableRow[] = items.map((item, index) => ({\n id: item.id || `item-${index}`,\n relateable_type: item.relateable_type,\n relateable_id: item.relateable?.id,\n relateable: item.relateable,\n order: index + 1,\n title:\n item.title ??\n item.relateable?.title ??\n `${item.relateable_type} #${item.relateable?.id}`,\n image_url: item.image_url ?? item.relateable?.image_url ?? null,\n kind: item.kind ?? item.relateable?.kind ?? null,\n }));\n\n const tableContent = (\n <div className=\"border-border w-full overflow-x-auto rounded-lg border\">\n <table className=\"w-full min-w-max\">\n <thead className=\"bg-muted\">\n <tr>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n {t(\"playlist_table_order\")}\n </th>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n {t(\"playlist_table_item\")}\n </th>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n {t(\"playlist_table_type\")}\n </th>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n {t(\"playlist_table_actions\")}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-border bg-card divide-y\">\n {tableData.map((row) => (\n <SortableRow\n key={row.id}\n id={row.id.toString()}\n enableReordering={enableReordering}\n >\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"flex items-center gap-2\">\n <div className={enableReordering ? \"cursor-move\" : \"\"}>\n <GripVertical\n className={`h-4 w-4 ${enableReordering ? \"text-muted-foreground\" : \"text-muted-foreground/50\"}`}\n />\n </div>\n <div className=\"text-foreground text-sm font-medium\">\n {row.order}\n </div>\n </div>\n </td>\n <td className=\"max-w-xs px-6 py-4 lg:max-w-md\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"bg-muted flex size-8 shrink-0 items-center justify-center overflow-hidden rounded\">\n {row.image_url ? (\n <img\n src={String(row.image_url)}\n alt={String(row.title)}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n getItemIcon(row.relateable_type || \"\")\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-foreground truncate text-sm font-medium\">\n {row.title || t(\"common_untitled\")}\n </div>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"bg-muted text-foreground inline-flex items-center gap-1 rounded-full px-2 py-1 text-xs\">\n {(() => {\n const Icon = typeBadgeIcon(\n row.relateable_type || \"\",\n row.kind || \"\",\n );\n return (\n <>\n <Icon className=\"size-3 shrink-0\" />\n {getTypeDisplay(\n row.relateable_type || \"\",\n row.kind || \"\",\n t,\n )}\n </>\n );\n })()}\n </div>\n </td>\n <td className=\"px-6 py-4 text-right text-sm font-medium whitespace-nowrap\">\n {isMobile ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"flex size-8 items-center justify-center p-0\"\n disabled={isDeletePending}\n onClick={() => {\n const itemId =\n typeof row.id === \"number\"\n ? row.id\n : parseInt(String(row.id).replace(\"item-\", \"\"));\n setActionItemId(itemId);\n }}\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"flex size-8 items-center justify-center p-0\"\n disabled={isDeletePending}\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={() => {\n const itemId =\n typeof row.id === \"number\"\n ? row.id\n : parseInt(String(row.id).replace(\"item-\", \"\"));\n onDeleteItem(itemId);\n }}\n variant=\"destructive\"\n >\n <Trash2 className=\"mr-2 size-4\" />\n {t(\"playlist_delete_item\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </td>\n </SortableRow>\n ))}\n </tbody>\n </table>\n </div>\n );\n\n const mobileActions = (\n <MobileActionSheet\n open={actionItemId !== null}\n onOpenChange={(open) => {\n if (!open) setActionItemId(null);\n }}\n title={t(\"playlist_table_actions\")}\n actions={[\n {\n id: \"delete\",\n label: t(\"playlist_delete_item\"),\n variant: \"destructive\",\n disabled: isDeletePending,\n onSelect: () => {\n if (actionItemId !== null) onDeleteItem(actionItemId);\n },\n },\n ]}\n />\n );\n\n if (enableReordering) {\n return (\n <>\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext\n items={tableData.map((item) => item.id.toString())}\n strategy={verticalListSortingStrategy}\n >\n {tableContent}\n </SortableContext>\n </DndContext>\n {mobileActions}\n </>\n );\n }\n\n return (\n <>\n {tableContent}\n {mobileActions}\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { Plus } from \"lucide-react\";\nimport {\n FilePicker,\n FilePickerApiProvider,\n FilePickerProvider,\n type FilePickerContextValue,\n} from \"@fluid-app/file-picker-ui\";\nimport type { FilePickerResult } from \"@fluid-app/file-picker-core\";\nimport {\n useAddItemToPlaylistMutation,\n useRemoveItemsFromPlaylistMutation,\n useReorderPlaylistItemsMutation,\n useShareablesApi,\n} from \"@fluid-app/shareables-core\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport type { shareables } from \"@fluid-app/shareables-core\";\nimport { useShareablesUI } from \"../../../context\";\nimport {\n usePlaylistItems,\n type PlaylistItemLike,\n} from \"./PlaylistItemsContext\";\nimport { SortableTable } from \"./SortableTable\";\n\nconst REORDER_DEBOUNCE_MS = 500;\n\n// Single source of truth: each content type maps a polymorphic playlist\n// item's BFF discriminator (`content_type`, used in the picker's prefixed\n// `asset_code`) to the legacy admin shape's `relateable_type` (still the\n// canonical key in PlaylistItemLike). Adding or renaming a type means\n// updating this one table; the picker tabs, the section bucketing, and\n// the prefix-aware result handler all derive from it.\nconst CONTENT_TYPES = [\n { prefix: \"media_\", relateableType: \"Medium\", section: \"primary\" },\n { prefix: \"page_\", relateableType: \"Page\", section: \"primary\" },\n { prefix: \"product_\", relateableType: \"Product\", section: \"tagged\" },\n {\n prefix: \"enrollment_pack_\",\n relateableType: \"EnrollmentPack\",\n section: \"tagged\",\n },\n] as const satisfies ReadonlyArray<{\n prefix: string;\n relateableType: string;\n section: \"primary\" | \"tagged\";\n}>;\n\nconst PRIMARY_CONTENT_TYPES = new Set<string>(\n CONTENT_TYPES.filter((c) => c.section === \"primary\").map(\n (c) => c.relateableType,\n ),\n);\nconst TAGGED_CONTENT_TYPES = new Set<string>(\n CONTENT_TYPES.filter((c) => c.section === \"tagged\").map(\n (c) => c.relateableType,\n ),\n);\nconst PLAYLIST_CONTENT_TYPES = new Set<string>(\n CONTENT_TYPES.map((c) => c.relateableType),\n);\n\nfunction deriveRelateableType(assetCode: string): string | null {\n for (const entry of CONTENT_TYPES) {\n if (assetCode.startsWith(entry.prefix)) return entry.relateableType;\n }\n return null;\n}\n\nfunction computeOrderedItems(items: PlaylistItemLike[]): PlaylistItemLike[] {\n const required: PlaylistItemLike[] = [];\n const optional: PlaylistItemLike[] = [];\n\n items.forEach((item) => {\n const type = item.relateable_type;\n if (type && PRIMARY_CONTENT_TYPES.has(type)) {\n required.push(item);\n } else if (type && TAGGED_CONTENT_TYPES.has(type)) {\n optional.push(item);\n }\n });\n\n const result: PlaylistItemLike[] = [];\n required.forEach((item, index) => {\n if (item.id) {\n result.push({ ...item, order: index + 1 });\n }\n });\n optional.forEach((item, index) => {\n if (item.id) {\n result.push({ ...item, order: required.length + index + 1 });\n }\n });\n return result;\n}\n\ninterface PlaylistItemsSectionProps {\n playlistId?: number;\n}\n\ninterface SectionConfig {\n /** Section UI heading. */\n heading: string;\n /** Subheading copy under the heading. */\n description: string;\n /** Allowed picker methods scoped to this section. */\n allowedMethods: (\"media\" | \"pages\" | \"products\" | \"enrollment_packs\")[];\n /** relateable_type values this section accepts (defensive filter for picker output). */\n allowedRelateableTypes: Set<string>;\n}\n\nfunction useSections(t: ReturnType<typeof useShareablesTranslation>[\"t\"]): {\n primary: SectionConfig;\n tagged: SectionConfig;\n} {\n return useMemo(\n () => ({\n primary: {\n heading: t(\"playlist_items_heading\"),\n description: t(\"playlist_items_description\"),\n allowedMethods: [\"media\", \"pages\"],\n allowedRelateableTypes: PRIMARY_CONTENT_TYPES,\n },\n tagged: {\n heading: t(\"playlist_tagged_products_heading\"),\n description: t(\"playlist_tagged_products_description\"),\n allowedMethods: [\"products\", \"enrollment_packs\"],\n allowedRelateableTypes: TAGGED_CONTENT_TYPES,\n },\n }),\n [t],\n );\n}\n\nexport function PlaylistItemsSection({\n playlistId,\n}: PlaylistItemsSectionProps): React.JSX.Element {\n const { t, locale } = useShareablesTranslation();\n const { showToast, filePickerApi, pickerSources } = useShareablesUI();\n const api = useShareablesApi();\n const SECTIONS = useSections(t);\n const [openSection, setOpenSection] = useState<\"primary\" | \"tagged\" | null>(\n null,\n );\n\n const {\n items: contextItems,\n updateItems,\n addItem,\n removeItem,\n } = usePlaylistItems();\n\n const tableItems = contextItems;\n\n const { primaryItems, taggedItems } = useMemo(() => {\n const primary: PlaylistItemLike[] = [];\n const tagged: PlaylistItemLike[] = [];\n tableItems.forEach((item) => {\n if (\n item.relateable_type &&\n PRIMARY_CONTENT_TYPES.has(item.relateable_type)\n ) {\n primary.push(item);\n } else if (\n item.relateable_type &&\n TAGGED_CONTENT_TYPES.has(item.relateable_type)\n ) {\n tagged.push(item);\n }\n });\n return { primaryItems: primary, taggedItems: tagged };\n }, [tableItems]);\n\n const addItemMutation = useAddItemToPlaylistMutation({\n onError: (error: Error) => {\n showToast({\n title: t(\"playlist_add_item_error\"),\n type: \"error\",\n error,\n });\n },\n });\n\n const removeItemMutation = useRemoveItemsFromPlaylistMutation({\n onSuccess: () => {\n showToast({\n title: t(\"playlist_remove_item_success\"),\n type: \"success\",\n });\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"playlist_remove_item_error\"),\n type: \"error\",\n error,\n });\n },\n });\n\n const reorderItemsMutation = useReorderPlaylistItemsMutation();\n\n const reorderTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Snapshot taken at the start of a debounce cycle so we can revert if\n // the eventual server call fails. Cleared on success/error.\n const reorderRollbackRef = useRef<PlaylistItemLike[] | null>(null);\n\n useEffect(\n () => () => {\n if (reorderTimerRef.current) clearTimeout(reorderTimerRef.current);\n },\n [],\n );\n\n const canPersistReorder = !!api.playlists.reorderPlaylistItems;\n\n // Reorder handler — accepts a single section's reordered list, merges with\n // the other section's items, recomputes global order numbers, persists.\n const mergeAndOrder = useCallback(\n (reordered: PlaylistItemLike[], whichSection: \"primary\" | \"tagged\") => {\n const merged =\n whichSection === \"primary\"\n ? [...reordered, ...taggedItems]\n : [...primaryItems, ...reordered];\n const ordered = computeOrderedItems(merged);\n\n if (\n playlistId &&\n canPersistReorder &&\n reorderTimerRef.current === null &&\n reorderRollbackRef.current === null\n ) {\n reorderRollbackRef.current = tableItems;\n }\n\n updateItems(ordered);\n\n if (!playlistId || !canPersistReorder) return;\n\n if (reorderTimerRef.current) clearTimeout(reorderTimerRef.current);\n reorderTimerRef.current = setTimeout(() => {\n reorderTimerRef.current = null;\n const rollback = reorderRollbackRef.current;\n reorderItemsMutation.mutate(\n {\n playlistId,\n data: {\n items: ordered\n .filter(\n (\n item,\n ): item is PlaylistItemLike & {\n id: number;\n order: number;\n } =>\n Number.isInteger(item.id) && typeof item.order === \"number\",\n )\n .map((item) => ({ id: item.id, order: item.order })),\n },\n },\n {\n onSuccess: () => {\n if (reorderTimerRef.current === null) {\n reorderRollbackRef.current = null;\n } else {\n reorderRollbackRef.current = ordered;\n }\n },\n onError: (error) => {\n if (rollback) updateItems(rollback);\n reorderRollbackRef.current = null;\n showToast({\n title: t(\"playlist_reorder_error\"),\n type: \"error\",\n error,\n });\n },\n },\n );\n }, REORDER_DEBOUNCE_MS);\n },\n [\n primaryItems,\n taggedItems,\n tableItems,\n updateItems,\n playlistId,\n canPersistReorder,\n reorderItemsMutation.mutate,\n showToast,\n t,\n ],\n );\n\n const handleDeleteItem = (itemId: number) => {\n removeItem(itemId);\n if (playlistId) {\n removeItemMutation.mutate({\n playlistId,\n itemIds: [itemId],\n });\n }\n };\n\n // Per-section picker result handler: each result's asset_code prefix\n // determines its relateable_type. Items not matching the section's\n // allowed types are silently skipped (defensive — picker is scoped, but\n // we don't want a misconfigured host to corrupt the playlist).\n const buildResultsHandler = useCallback(\n (section: SectionConfig) => async (results: FilePickerResult[]) => {\n setOpenSection(null);\n if (results.length === 0) {\n showToast({\n title: t(\"playlist_no_items_selected\"),\n type: \"warning\",\n });\n return;\n }\n\n let nextOrder = tableItems.length + 1;\n\n for (const result of results) {\n const relateableType = deriveRelateableType(result.asset_code);\n if (!relateableType) continue;\n if (!section.allowedRelateableTypes.has(relateableType)) continue;\n\n const relateableId = result.asset_id;\n const label =\n result.metadata?.file_name || `${relateableType} #${relateableId}`;\n\n const itemExists = tableItems.some(\n (existing) =>\n existing.relateable_type === relateableType &&\n existing.relateable?.id === relateableId,\n );\n if (itemExists) {\n showToast({\n title: t(\"playlist_item_already_exists\"),\n type: \"error\",\n });\n continue;\n }\n\n const order = nextOrder++;\n\n const newItem: PlaylistItemLike = {\n id: Date.now() + Math.random(),\n order,\n relateable_type: relateableType,\n relateable: {\n id: relateableId,\n title: label,\n image_url: result.thumbnail_url ?? result.file_url ?? null,\n },\n };\n\n if (playlistId) {\n try {\n await addItemMutation.mutateAsync({\n playlistId,\n newItem,\n data: {\n relateable_type: relateableType,\n relateable_id: relateableId,\n order,\n },\n });\n showToast({\n title: t(\"playlist_add_item_success\"),\n type: \"success\",\n });\n } catch {\n // Error already handled by mutation onError\n }\n } else {\n addItem(newItem);\n }\n }\n },\n [tableItems, playlistId, addItemMutation, addItem, showToast, t],\n );\n\n const filePickerContextValue = useMemo<FilePickerContextValue | null>(() => {\n if (!filePickerApi) return null;\n return {\n toast: {\n success: (msg: string) => showToast({ title: msg, type: \"success\" }),\n error: (msg: string, error?: unknown) =>\n showToast({\n title: error instanceof Error ? `${msg}: ${error.message}` : msg,\n type: \"error\",\n }),\n loading: () => \"\",\n dismiss: () => {},\n },\n shareablesClient: {\n media: {\n cacheKey: `locale:${locale}`,\n list: (filters: unknown) =>\n api.media.getMedia({\n ...((filters ?? {}) as shareables.MediaFilters),\n language_iso: locale,\n }),\n },\n // Wire content-browser tabs only when the host supplies a source.\n // Missing entries cause the picker to hide the corresponding tab.\n ...(pickerSources?.pages\n ? {\n pages: {\n ...pickerSources.pages,\n cacheKey: `${pickerSources.pages.cacheKey ?? \"source\"}|locale:${locale}`,\n },\n }\n : {}),\n ...(pickerSources?.products\n ? {\n products: {\n ...pickerSources.products,\n cacheKey: `${pickerSources.products.cacheKey ?? \"source\"}|locale:${locale}`,\n },\n }\n : {}),\n ...(pickerSources?.enrollment_packs\n ? {\n enrollment_packs: {\n ...pickerSources.enrollment_packs,\n cacheKey: `${pickerSources.enrollment_packs.cacheKey ?? \"source\"}|locale:${locale}`,\n },\n }\n : {}),\n },\n };\n }, [filePickerApi, showToast, api, pickerSources, locale]);\n\n // Always render the two sections (and their tables of existing items).\n // The picker tree is only mounted when the host supplies a FilePickerApi —\n // missing-host scenarios (tests, Storybook, hosts without DAM) get a\n // disabled Add Item button instead of an empty editor.\n const pickerAvailable = !!filePickerApi && !!filePickerContextValue;\n\n return (\n <div className=\"space-y-8\">\n <PlaylistItemsSubsection\n section={SECTIONS.primary}\n items={primaryItems}\n onAdd={() => setOpenSection(\"primary\")}\n onReorder={(reordered) => mergeAndOrder(reordered, \"primary\")}\n onDeleteItem={handleDeleteItem}\n isPending={addItemMutation.isPending}\n isDeletePending={removeItemMutation.isPending}\n disableAdd={!pickerAvailable}\n />\n <PlaylistItemsSubsection\n section={SECTIONS.tagged}\n items={taggedItems}\n onAdd={() => setOpenSection(\"tagged\")}\n onReorder={(reordered) => mergeAndOrder(reordered, \"tagged\")}\n onDeleteItem={handleDeleteItem}\n isPending={addItemMutation.isPending}\n isDeletePending={removeItemMutation.isPending}\n disableAdd={!pickerAvailable}\n />\n\n {pickerAvailable && (\n <FilePickerApiProvider api={filePickerApi}>\n <FilePickerProvider value={filePickerContextValue}>\n <FilePicker\n open={openSection === \"primary\"}\n onClose={() => setOpenSection(null)}\n onFilesSelected={(selected) =>\n void buildResultsHandler(SECTIONS.primary)(selected)\n }\n config={{\n maxFiles: 50,\n allowedMethods: SECTIONS.primary.allowedMethods,\n unifiedSelection: true,\n }}\n />\n <FilePicker\n open={openSection === \"tagged\"}\n onClose={() => setOpenSection(null)}\n onFilesSelected={(selected) =>\n void buildResultsHandler(SECTIONS.tagged)(selected)\n }\n config={{\n maxFiles: 50,\n allowedMethods: SECTIONS.tagged.allowedMethods,\n unifiedSelection: true,\n }}\n />\n </FilePickerProvider>\n </FilePickerApiProvider>\n )}\n </div>\n );\n}\n\ninterface SubsectionProps {\n section: SectionConfig;\n items: PlaylistItemLike[];\n onAdd: () => void;\n onReorder: (reordered: PlaylistItemLike[]) => void;\n onDeleteItem: (itemId: number) => void;\n isPending: boolean;\n isDeletePending: boolean;\n /**\n * When true the Add Item button is disabled — used when the host hasn't\n * supplied a FilePickerApi, so existing items remain visible/editable but\n * the picker can't open.\n */\n disableAdd?: boolean;\n}\n\nfunction PlaylistItemsSubsection({\n section,\n items,\n onAdd,\n onReorder,\n onDeleteItem,\n isPending,\n isDeletePending,\n disableAdd = false,\n}: SubsectionProps) {\n const { t } = useShareablesTranslation();\n\n return (\n <div className=\"border-border bg-card rounded-lg border p-4\">\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"min-w-0\">\n <h3 className=\"text-md text-foreground font-semibold\">\n {section.heading}\n </h3>\n <p className=\"text-muted-foreground text-sm\">\n {section.description}\n </p>\n </div>\n <div className=\"flex shrink-0 flex-wrap items-center justify-end gap-2\">\n <Button\n type=\"button\"\n disabled={isPending || disableAdd}\n variant=\"default\"\n size=\"sm\"\n className=\"flex min-w-25 items-center gap-2\"\n onClick={onAdd}\n >\n <Plus className=\"size-4\" />\n {t(\"playlist_add_item_button\")}\n </Button>\n </div>\n </div>\n\n <SortableTable\n items={items}\n setItems={(reordered) => onReorder(reordered)}\n onDeleteItem={onDeleteItem}\n isDeletePending={isDeletePending}\n enableReordering\n />\n </div>\n </div>\n );\n}\n\nexport { PLAYLIST_CONTENT_TYPES };\n","\"use client\";\n\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { usePlaylistForm, type PlaylistLike } from \"./PlaylistFormProvider\";\nimport {\n usePlaylistItems,\n type PlaylistItemLike,\n} from \"./PlaylistItemsContext\";\n\nconst OG_PLACEHOLDER_IMAGE =\n \"https://ik.imagekit.io/fluid/s3/placeholders/image.jpg?w=1200&q=75\";\n\nconst PLAYLIST_PREVIEW_CONTENT_TYPES = new Set<string>([\n \"Medium\",\n \"Page\",\n \"Product\",\n \"EnrollmentPack\",\n]);\n\nfunction getImageFromRelateable(\n item: PlaylistItemLike | undefined,\n): string | null {\n const r = item?.relateable;\n if (!r) return null;\n const url = r.image_url || r.compressed_image_url;\n const t = url?.trim();\n return t || null;\n}\n\nfunction getFirstOrderedContentItem(\n items: PlaylistItemLike[],\n): PlaylistItemLike | undefined {\n const content = items.filter(\n (item) =>\n item.relateable_type &&\n PLAYLIST_PREVIEW_CONTENT_TYPES.has(item.relateable_type),\n );\n if (content.length === 0) return undefined;\n return content.toSorted(\n (a, b) =>\n (a.order ?? Number.POSITIVE_INFINITY) -\n (b.order ?? Number.POSITIVE_INFINITY),\n )[0];\n}\n\nfunction stripHtml(html: string): string {\n if (!html) return \"\";\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n return doc.body.textContent || \"\";\n}\n\nfunction buildPlaylistUrl(options: {\n playlist?: PlaylistLike | null;\n formSlug?: string | null;\n baseUrl?: string;\n preview?: boolean;\n}): string {\n const { playlist, formSlug, baseUrl, preview = false } = options;\n\n if (playlist?.canonical_url) {\n return preview\n ? `${playlist.canonical_url}?preview=true`\n : playlist.canonical_url;\n }\n if (playlist?.preview_link) {\n return preview\n ? playlist.preview_link\n : playlist.preview_link.replace(\"?preview=true\", \"\");\n }\n const slug = formSlug || playlist?.slug;\n if (baseUrl && slug) {\n const cleanBaseUrl = baseUrl.replace(/\\/$/, \"\");\n const url = `${cleanBaseUrl}/playlists/${slug}`;\n return preview ? `${url}?preview=true` : url;\n }\n return \"\";\n}\n\nexport interface PlaylistOpenGraphPreviewProps {\n playlist?: PlaylistLike | null;\n playlistShareBaseUrl?: string;\n}\n\nexport function PlaylistOpenGraphPreview({\n playlist,\n playlistShareBaseUrl,\n}: PlaylistOpenGraphPreviewProps): React.JSX.Element {\n const { t } = useShareablesTranslation();\n const { form } = usePlaylistForm();\n const { items } = usePlaylistItems();\n const seo = form.search_engine_optimizer;\n\n const firstContentItem = useMemo(\n () => getFirstOrderedContentItem(items),\n [items],\n );\n\n const firstItemImageUrl = useMemo(\n () => getImageFromRelateable(firstContentItem),\n [firstContentItem],\n );\n\n const title =\n (seo?.title || form.title || \"\").trim() || t(\"playlist_og_title_fallback\");\n\n const rawDescription =\n (seo?.description || \"\").trim() || stripHtml(form.description || \"\") || \"\";\n const description = rawDescription || t(\"playlist_og_description_fallback\");\n\n const imageUrl =\n firstItemImageUrl ||\n (seo?.image_url || \"\").trim() ||\n (form.image_url || \"\").trim();\n\n const shareUrl = useMemo(\n () =>\n buildPlaylistUrl({\n playlist,\n formSlug: form.slug,\n baseUrl: playlistShareBaseUrl,\n preview: false,\n }),\n [playlist, form.slug, playlistShareBaseUrl],\n );\n\n const hostname = useMemo(() => {\n if (!shareUrl) return \"\";\n try {\n return new URL(shareUrl).hostname;\n } catch {\n return shareUrl.replace(/^https?:\\/\\//, \"\").split(\"/\")[0] ?? \"\";\n }\n }, [shareUrl]);\n\n return (\n <div className=\"border-border bg-card rounded-lg border p-4 shadow-xs\">\n <h3 className=\"text-foreground mb-2 text-sm font-semibold\">\n {t(\"playlist_preview_heading\")}\n </h3>\n <div className=\"border-border bg-card overflow-hidden rounded-lg border\">\n <div className=\"bg-muted aspect-[1200/630] w-full\">\n <img\n key={imageUrl || \"placeholder\"}\n src={imageUrl || OG_PLACEHOLDER_IMAGE}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n </div>\n <div className=\"space-y-1 p-3\">\n <p className=\"text-muted-foreground text-[11px] tracking-wide uppercase\">\n {hostname || t(\"common_site_hostname_fallback\")}\n </p>\n <p className=\"text-foreground line-clamp-2 text-base font-semibold\">\n {title}\n </p>\n <p className=\"text-muted-foreground line-clamp-3 text-sm leading-snug\">\n {description}\n </p>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useEffect, useCallback } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n useShareablesApi,\n useCreatePlaylistMutation,\n useUpdatePlaylistMutation,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesUI } from \"../../context\";\nimport {\n PlaylistFormProvider,\n usePlaylistForm,\n type PlaylistLike,\n} from \"../playlists/form/PlaylistFormProvider\";\nimport {\n PlaylistItemsProvider,\n usePlaylistItems,\n type PlaylistItemLike,\n} from \"../playlists/form/PlaylistItemsContext\";\nimport { PlaylistFormHeader } from \"../playlists/form/PlaylistFormHeader\";\nimport { PlaylistFormDetails } from \"../playlists/form/PlaylistFormDetails\";\nimport { PlaylistItemsSection } from \"../playlists/form/PlaylistItemsSection\";\nimport { PlaylistOpenGraphPreview } from \"../playlists/form/PlaylistOpenGraphPreview\";\n\nexport interface PlaylistCreateScreenProps {\n playlistId?: string;\n onBack?: () => void;\n /** Hide the built-in header (use when the host app provides its own header chrome) */\n hideHeader?: boolean;\n /** Render custom children inside the form providers (e.g. portal shell header wiring) */\n renderHeaderSlot?: (props: {\n onSubmit: () => Promise<void>;\n isSaving: boolean;\n playlistId?: string;\n isEditMode: boolean;\n }) => React.ReactNode;\n}\n\nfunction PlaylistFormContent({\n playlistId,\n playlist,\n isEditMode,\n onBack,\n hideHeader,\n renderHeaderSlot,\n}: {\n playlistId?: string;\n playlist?: PlaylistLike | null;\n isEditMode: boolean;\n onBack?: () => void;\n hideHeader?: boolean;\n renderHeaderSlot?: PlaylistCreateScreenProps[\"renderHeaderSlot\"];\n}) {\n const api = useShareablesApi();\n const { t } = useShareablesTranslation();\n const { showToast, navigate, user } = useShareablesUI();\n const { items: contextItems } = usePlaylistItems();\n const { form, updateField, validateForm } = usePlaylistForm();\n\n const playlistTitle = form.title || \"\";\n const slugValue = form.slug ?? \"\";\n\n // Auto-generate slug from title\n useEffect(() => {\n const isCustomSlug = form.custom_slug || playlist?.custom_slug || false;\n if (playlistTitle && !isCustomSlug) {\n const generatedSlug = playlistTitle\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n if (generatedSlug !== slugValue) {\n updateField(\"slug\", generatedSlug);\n }\n }\n }, [\n playlistTitle,\n form.custom_slug,\n playlist?.custom_slug,\n slugValue,\n updateField,\n ]);\n\n const createMutation = useCreatePlaylistMutation({\n onSuccess: (createdPlaylist) => {\n showToast({ title: t(\"playlist_create_success\"), type: \"success\" });\n const newId = createdPlaylist.playlist.id;\n navigate(`playlists/${newId}/edit`);\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"playlist_create_error\"),\n type: \"error\",\n error,\n });\n },\n });\n\n const updateMutation = useUpdatePlaylistMutation({\n onSuccess: () => {\n showToast({ title: t(\"playlist_update_success\"), type: \"success\" });\n },\n onError: (error: Error) => {\n showToast({\n title: t(\"playlist_update_error\"),\n type: \"error\",\n error,\n });\n },\n });\n\n const itemsForSubmission = contextItems;\n\n const onSaveForm = useCallback(async (): Promise<number> => {\n if (!validateForm()) {\n showToast({\n title: t(\"common_fill_required_fields\"),\n type: \"error\",\n });\n return Promise.reject(new Error(\"Validation failed\"));\n }\n\n const formData = {\n title: form.title,\n description: form.description ?? null,\n image_url: form.image_url ?? null,\n slug: form.slug || null,\n custom_slug: form.custom_slug ?? false,\n category_id: form.category_id ?? null,\n application_theme_template_id: form.application_theme_template_id\n ? Number(form.application_theme_template_id)\n : null,\n active: isEditMode ? (form.active ?? false) : true,\n company_library: true,\n indexed_for_seo: form.indexed_for_seo ?? false,\n available_countries: form.countries ?? [],\n search_engine_optimizer_attributes:\n form.search_engine_optimizer ?? undefined,\n };\n\n if (isEditMode && playlistId) {\n await updateMutation.mutateAsync({\n playlistId: parseInt(playlistId),\n data: {\n playlist: {\n ...formData,\n library_items_attributes: itemsForSubmission?.map((item) => ({\n id: item.id,\n order: item.order,\n })),\n },\n },\n });\n return parseInt(playlistId);\n }\n\n let searchEngineOptimizer = form.search_engine_optimizer;\n if (!searchEngineOptimizer) {\n searchEngineOptimizer = {\n title: form.title || \"\",\n description: \"\",\n image_url: \"\",\n image_path: \"\",\n block_crawler: false,\n };\n }\n\n const createdPlaylist = await createMutation.mutateAsync({\n playlist: {\n ...formData,\n search_engine_optimizer_attributes: searchEngineOptimizer,\n ...(user?.id ? { user_id: user.id } : {}),\n },\n });\n const newPlaylistId = createdPlaylist.playlist.id;\n\n if (itemsForSubmission.length > 0) {\n try {\n const itemsData = itemsForSubmission\n .filter(\n (\n item,\n ): item is PlaylistItemLike & {\n relateable_type: string;\n relateable: { id: number };\n } =>\n !!item.relateable_type && typeof item.relateable?.id === \"number\",\n )\n .map((item, index) => ({\n relateable_type: item.relateable_type,\n relateable_id: item.relateable.id,\n order: index + 1,\n }));\n\n await api.playlists.addItemToPlaylist(newPlaylistId, {\n items: itemsData,\n });\n } catch (itemError) {\n console.error(\"Error adding items to playlist:\", itemError);\n showToast({\n title: t(\"playlist_create_items_partial_error\"),\n type: \"warning\",\n });\n }\n }\n\n return newPlaylistId;\n }, [\n validateForm,\n form,\n isEditMode,\n playlistId,\n itemsForSubmission,\n createMutation,\n updateMutation,\n showToast,\n api,\n user?.id,\n t,\n ]);\n\n const isSaving = createMutation.isPending || updateMutation.isPending;\n const handleSubmit = async () => {\n await onSaveForm();\n };\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (onBack ?? (() => navigate(\"playlists\")))();\n }}\n >\n {t(\"playlist_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n {isEditMode ? (\n <>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n navigate(`playlists/${playlistId}`);\n }}\n className=\"block max-w-[40vw] truncate align-bottom md:max-w-[60ch]\"\n >\n {playlistTitle || t(\"playlist_breadcrumb_fallback\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"common_edit\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </>\n ) : (\n <>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"playlist_create_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </>\n )}\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n {renderHeaderSlot?.({\n onSubmit: handleSubmit,\n isSaving,\n playlistId,\n isEditMode,\n })}\n {!hideHeader && !renderHeaderSlot && (\n <PlaylistFormHeader\n playlistId={playlistId}\n isEditMode={isEditMode}\n onSubmit={handleSubmit}\n isSaving={isSaving}\n onBack={onBack}\n />\n )}\n <div className=\"mx-auto w-full max-w-7xl space-y-6 px-6 pt-6 pb-8 md:px-10 md:py-8\">\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"space-y-6 lg:col-span-2\">\n <PlaylistFormDetails />\n <PlaylistItemsSection\n playlistId={playlistId ? parseInt(playlistId, 10) : undefined}\n />\n </div>\n <div className=\"space-y-6 lg:col-span-1\">\n <PlaylistOpenGraphPreview playlist={playlist} />\n </div>\n </div>\n </div>\n </>\n );\n}\n\nexport function PlaylistCreateScreen({\n playlistId,\n onBack,\n hideHeader,\n renderHeaderSlot,\n}: PlaylistCreateScreenProps): React.JSX.Element {\n const api = useShareablesApi();\n const { locale } = useShareablesTranslation();\n const isEditMode = !!playlistId;\n\n const { data: playlistResponse, isLoading } = useQuery({\n queryKey: playlistId\n ? shareablesKeys.playlists.detail(parseInt(playlistId, 10), locale)\n : [\"playlists\", \"detail\", null, locale],\n queryFn: () =>\n api.playlists.getPlaylistById(parseInt(playlistId!, 10), locale),\n enabled: isEditMode,\n });\n\n const playlist = playlistResponse?.playlist as PlaylistLike | undefined;\n const itemsToUse: PlaylistItemLike[] =\n (playlist?.items as PlaylistItemLike[] | undefined) ?? [];\n\n if (isEditMode && isLoading) {\n return (\n <div className=\"mx-auto max-w-7xl space-y-6 px-6 pt-6 pb-8 md:px-10 md:py-8\">\n <Skeleton className=\"h-8 w-48\" />\n <Skeleton className=\"h-64 w-full\" />\n </div>\n );\n }\n\n return (\n <PlaylistFormProvider playlist={playlist}>\n <PlaylistItemsProvider initialItems={itemsToUse}>\n <PlaylistFormContent\n playlistId={playlistId}\n playlist={playlist}\n isEditMode={isEditMode}\n onBack={onBack}\n hideHeader={hideHeader}\n renderHeaderSlot={renderHeaderSlot}\n />\n </PlaylistItemsProvider>\n </PlaylistFormProvider>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useMemo } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { useShareablesApi } from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Card,\n Badge,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { CirclePlay } from \"lucide-react\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage } from \"../../context\";\nimport { useInfiniteListSentinel } from \"../../hooks/use-infinite-list-sentinel\";\nimport {\n ShareableListLayout,\n SHAREABLE_GRID_CLASS,\n} from \"../shared/ShareableListLayout\";\n\nconst PAGE_SIZE = 24;\n\nfunction formatFileSize(\n bytes: number,\n labels: { b: string; kb: string; mb: string; gb: string },\n): string {\n if (bytes < 1024) return `${bytes} ${labels.b}`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} ${labels.kb}`;\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} ${labels.mb}`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} ${labels.gb}`;\n}\n\nexport interface FilesListingScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n}\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport function FilesListingScreen({\n onNavigate: _onNavigate,\n}: FilesListingScreenProps): React.JSX.Element {\n const api = useShareablesApi();\n const renderImage = useRenderImage();\n const { t } = useShareablesTranslation();\n const isMobile = useIsMobile();\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n\n // Debounce search input\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearch(searchTerm);\n }, 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey: [\"shareables-files\", debouncedSearch],\n queryFn: async ({ pageParam = 1 }) => {\n const response = await api.fileResources.getFileResources({\n search_query: debouncedSearch || undefined,\n pageParam: pageParam.toString(),\n pageSize: PAGE_SIZE.toString(),\n });\n return response;\n },\n getNextPageParam: (lastPage) => {\n const { current_page, total_pages } = lastPage.meta;\n return current_page < total_pages ? current_page + 1 : undefined;\n },\n initialPageParam: 1,\n });\n\n const files = useMemo(\n () => data?.pages.flatMap((page) => page.file_resources) ?? [],\n [data?.pages],\n );\n\n const sentinelRef = useInfiniteListSentinel({\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n error,\n });\n\n const footer = (\n <>\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary size-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n {error && (\n <p className=\"bg-destructive/10 text-destructive rounded-lg px-3 py-2\">\n {t(\"files_load_error\")}\n </p>\n )}\n </>\n );\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"files_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableListLayout\n isLoading={isLoading}\n isEmpty={\n !error && files.length === 0 && !isFetchingNextPage && !hasNextPage\n }\n emptyMessage={\n debouncedSearch\n ? t(\"files_search_no_results\", { search: debouncedSearch })\n : t(\"files_empty_default\")\n }\n filters={\n <div className=\"flex justify-end\">\n <div className=\"w-full max-w-sm\">\n <SearchSort\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n searchValue={searchTerm}\n onSearchChange={setSearchTerm}\n placeholder={t(\"files_search_placeholder\")}\n />\n </div>\n </div>\n }\n footer={footer}\n sentinelRef={sentinelRef}\n >\n <div className={SHAREABLE_GRID_CLASS}>\n {files.map((file) => {\n const isVideo = file.content_type?.startsWith(\"video/\");\n const fileUrl = file.url || \"#\";\n\n return (\n <Card\n key={file.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => window.open(fileUrl, \"_blank\")}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n window.open(fileUrl, \"_blank\");\n }\n }}\n className=\"group hover:bg-muted cursor-pointer gap-0 overflow-hidden rounded-lg border-0 p-0 shadow-none transition-colors\"\n >\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: file.preview_image_url || DEFAULT_IMAGE,\n alt: file.filename || t(\"files_untitled\"),\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n })}\n {isVideo && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-foreground/50 flex size-16 items-center justify-center rounded-full backdrop-blur-sm\">\n <CirclePlay className=\"text-background size-12\" />\n </div>\n </div>\n )}\n {!isVideo && (\n <Badge\n className=\"absolute top-2 right-2 shadow-lg\"\n variant=\"default\"\n >\n {formatFileSize(file.content_size, {\n b: t(\"files_size_b\"),\n kb: t(\"files_size_kb\"),\n mb: t(\"files_size_mb\"),\n gb: t(\"files_size_gb\"),\n })}\n </Badge>\n )}\n </div>\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {file.filename || t(\"files_untitled\")}\n </h3>\n <p className=\"text-muted-foreground mt-1 text-xs\">\n {file.content_type || t(\"files_type_default\")}\n </p>\n </div>\n </Card>\n );\n })}\n </div>\n </ShareableListLayout>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useState, useEffect, useRef, useMemo } from \"react\";\nimport { useInfiniteQuery, keepPreviousData } from \"@tanstack/react-query\";\nimport { useShareablesApi, shareablesKeys } from \"@fluid-app/shareables-core\";\nimport {\n Badge,\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Card,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useRenderImage } from \"../../context\";\nimport {\n ShareableListLayout,\n SHAREABLE_GRID_CLASS,\n} from \"../shared/ShareableListLayout\";\n\nconst PAGE_SIZE = 24;\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\ntype SortValue = \"newest\" | \"title_asc\" | \"title_desc\";\n\nfunction toApiSort(value: SortValue): \"title_asc\" | \"title_desc\" | undefined {\n return value === \"newest\" ? undefined : value;\n}\n\nexport interface PagesListingScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n}\n\nexport function PagesListingScreen({\n onNavigate,\n}: PagesListingScreenProps): React.JSX.Element {\n const api = useShareablesApi();\n const renderImage = useRenderImage();\n const { t, locale } = useShareablesTranslation();\n const isMobile = useIsMobile();\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [sortValue, setSortValue] = useState<SortValue>(\"newest\");\n const observerTarget = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(searchTerm), 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n const queryKey = shareablesKeys.pages.list(\n debouncedSearch,\n sortValue,\n locale,\n );\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey,\n queryFn: async ({ pageParam }) => {\n const response = await api.pages.listPages({\n cursor: pageParam,\n limit: PAGE_SIZE,\n \"filter[title]\": debouncedSearch || undefined,\n sort: toApiSort(sortValue),\n language_iso: locale,\n });\n return {\n pages: response.pages,\n nextCursor: response.meta.pagination?.next_cursor ?? null,\n };\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,\n initialPageParam: undefined as string | undefined,\n placeholderData: keepPreviousData,\n });\n\n const sortOptions = useMemo(\n () => [\n { label: t(\"sort_newest_first\"), value: \"newest\" },\n { label: t(\"sort_title_asc\"), value: \"title_asc\" },\n { label: t(\"sort_title_desc\"), value: \"title_desc\" },\n ],\n [t],\n );\n\n const allPages = useMemo(\n () => data?.pages.flatMap((p) => p.pages) ?? [],\n [data?.pages],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n { threshold: 0, rootMargin: \"200px\" },\n );\n observer.observe(target);\n return () => observer.disconnect();\n }, [fetchNextPage, hasNextPage, isFetchingNextPage]);\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"pages_breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableListLayout\n isLoading={isLoading}\n error={error}\n errorMessage={t(\"pages_load_error\")}\n isEmpty={allPages.length === 0 && !isFetchingNextPage}\n emptyMessage={\n debouncedSearch\n ? t(\"pages_search_no_results\", { search: debouncedSearch })\n : t(\"pages_empty_default\")\n }\n filters={\n <div className=\"flex justify-end\">\n <div className=\"w-full max-w-sm\">\n <SearchSort\n presentation={isMobile ? \"bottom-sheet\" : \"dialog\"}\n searchValue={searchTerm}\n onSearchChange={setSearchTerm}\n placeholder={t(\"pages_search_placeholder\")}\n sortOptions={sortOptions}\n sortValue={sortValue}\n onSortChange={(v) => setSortValue(v as SortValue)}\n />\n </div>\n </div>\n }\n footer={\n isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary size-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )\n }\n sentinelRef={observerTarget}\n >\n <div className={SHAREABLE_GRID_CLASS}>\n {allPages.map((page) => {\n const title = page.title || t(\"pages_untitled\");\n const openDetail = () => onNavigate?.(\"pages\", String(page.id));\n return (\n <Card\n key={page.id}\n role=\"button\"\n tabIndex={0}\n onClick={openDetail}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n openDetail();\n }\n }}\n className=\"group hover:bg-muted cursor-pointer gap-0 overflow-hidden rounded-lg border-0 p-0 shadow-none transition-colors\"\n >\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: page.image_url || DEFAULT_IMAGE,\n alt: title,\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n })}\n <Badge\n className=\"absolute top-2 right-2 shadow-lg\"\n variant={\n page.status === \"published\" ? \"default\" : \"secondary\"\n }\n >\n {page.status === \"published\"\n ? t(\"pages_status_published\")\n : t(\"pages_status_unpublished\")}\n </Badge>\n </div>\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {title}\n </h3>\n {page.description && (\n <p className=\"text-muted-foreground mt-1 line-clamp-2 text-xs\">\n {page.description}\n </p>\n )}\n </div>\n </Card>\n );\n })}\n </div>\n </ShareableListLayout>\n </>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n useShareablesApi,\n useShareLink,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Badge,\n} from \"@fluid-app/ui-primitives\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport AssetActions from \"../SharePage/AssetActions\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport { useShareablesUI } from \"../../context\";\nimport type { ShareablesParentBreadcrumb } from \"../shareables-parent-breadcrumb-types\";\nimport { ShareableDetailLayout } from \"../shared/ShareableDetailLayout\";\n\nexport interface PageDetailScreenProps {\n pageId: string;\n onBack?: () => void;\n parentBreadcrumb?: ShareablesParentBreadcrumb | null;\n}\n\nexport function PageDetailScreen({\n pageId,\n onBack,\n parentBreadcrumb,\n}: PageDetailScreenProps): React.JSX.Element {\n const api = useShareablesApi();\n const { navigate } = useShareablesUI();\n const { t, locale } = useShareablesTranslation();\n\n const { data: pageResponse, isLoading } = useQuery({\n queryKey: shareablesKeys.pages.detail(Number(pageId), locale),\n queryFn: () => api.pages.getPage(Number(pageId), locale),\n });\n\n const page = pageResponse?.page;\n const displayTitle = page?.title || \"\";\n const displayImage = page?.image_url || \"\";\n const description = page?.description ?? \"\";\n\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink(\n {\n id: page?.id,\n share_link: pageResponse?.share_link ?? undefined,\n },\n \"Page\",\n undefined,\n locale,\n );\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (\n parentBreadcrumb?.onClick ??\n onBack ??\n (() => navigate(\"pages\"))\n )();\n }}\n >\n {t(\"pages_breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"block max-w-[60vw] truncate align-bottom font-semibold md:max-w-[60ch]\">\n {displayTitle || t(\"pages_breadcrumb_fallback\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <ShareableDetailLayout\n isLoading={isLoading}\n notFound={!page && !isLoading}\n notFoundMessage={t(\"pages_not_found\")}\n title={displayTitle}\n description={description}\n containerHeightClass=\"md:h-[calc(95vh-140px)]\"\n image={\n <div className=\"relative h-full overflow-hidden\">\n <SharePageImageDisplay\n displayImage={displayImage}\n displayTitle={displayTitle}\n isVideo={false}\n badgeLabel={t(\"pages_badge_label\")}\n rounded\n showBadge={false}\n pdfPrevAriaLabel={t(\"pdf_previous_page_aria\")}\n pdfNextAriaLabel={t(\"pdf_next_page_aria\")}\n pdfDocumentTitle={t(\"pdf_document_title\")}\n />\n {page && (\n <Badge\n variant={page.status === \"published\" ? \"default\" : \"secondary\"}\n className=\"bg-background/80 text-foreground absolute top-3 right-3 z-0 inline-flex h-8 items-center rounded-full px-3 text-xs font-medium shadow-md backdrop-blur-sm\"\n >\n {page.status === \"published\"\n ? t(\"pages_status_published\")\n : t(\"pages_status_unpublished\")}\n </Badge>\n )}\n </div>\n }\n actions={\n page && (\n <AssetActions\n downloadUrl={null}\n displayTitle={displayTitle}\n shareLink={shareLinkError ? null : shareLink || null}\n shareLinkLoading={shareLinkLoading}\n isVideo={false}\n relateableId={page.id}\n relateableType=\"Page\"\n />\n )\n }\n />\n </>\n );\n}\n","import type React from \"react\";\nimport { ProductsScreen } from \"./screens/ProductsScreen\";\nimport { ProductDetailScreen } from \"./screens/ProductDetailScreen\";\nimport { MediaListingScreen } from \"./screens/MediaListingScreen\";\nimport { MediaDetailScreen } from \"./screens/MediaDetailScreen\";\nimport { MediaEditScreen } from \"./screens/MediaEditScreen\";\nimport { MediaCreateScreen } from \"./screens/MediaCreateScreen\";\nimport { PlaylistsListingScreen } from \"./screens/PlaylistsListingScreen\";\nimport { PlaylistDetailScreen } from \"./screens/PlaylistDetailScreen\";\nimport { PlaylistCreateScreen } from \"./screens/PlaylistCreateScreen\";\nimport { FilesListingScreen } from \"./screens/FilesListingScreen\";\nimport { PagesListingScreen } from \"./screens/PagesListingScreen\";\nimport { PageDetailScreen } from \"./screens/PageDetailScreen\";\nimport { useShareablesUI, useCanCuratePlaylists } from \"../context\";\nimport type {\n PortalProductsResponse,\n PortalProductResponse,\n} from \"./portal-product-types\";\nimport type { ShareablesParentBreadcrumb } from \"./shareables-parent-breadcrumb-types\";\n\nexport type { ShareablesParentBreadcrumb } from \"./shareables-parent-breadcrumb-types\";\n\nexport interface ShareablesAppProps {\n /** Current sub-route: \"products\" | \"media\" | \"playlists\" | \"files\" | \"pages\" | null */\n screen?: string | null;\n /** Detail ID when viewing a specific item */\n detailId?: string | null;\n /** Action for detail pages (e.g., \"edit\") */\n action?: string | null;\n /** Company logo URL for image fallbacks */\n companyLogoUrl?: string | null;\n /** Country code for product pricing */\n countryCode?: string;\n /** Portal-tenant products list fetcher */\n fetchProducts?: (\n search: string,\n cursor?: string,\n limit?: number,\n sort?: string,\n ) => Promise<PortalProductsResponse>;\n /** Portal-tenant single product fetcher */\n fetchProduct?: (id: string | number) => Promise<PortalProductResponse>;\n /** Called when navigating to a sub-screen */\n onNavigate?: (screen: string, detailId?: string) => void;\n /** Called when navigating back */\n onBack?: () => void;\n /** Opt-in product-led carousel for media listing screens. */\n showProductSharablesCarousel?: boolean;\n /**\n * Optional in-session breadcrumb source for detail screens. When `label` is\n * set, the screen renders it instead of its default breadcrumb text (for\n * example, the source product title). When `null`, detail screens fall back\n * to their default structural breadcrumbs.\n */\n parentBreadcrumb?: ShareablesParentBreadcrumb | null;\n}\n\nexport function ShareablesApp({\n screen,\n detailId,\n action,\n onNavigate,\n onBack,\n countryCode,\n fetchProducts,\n fetchProduct,\n showProductSharablesCarousel = false,\n parentBreadcrumb,\n}: ShareablesAppProps): React.JSX.Element {\n const { readOnly } = useShareablesUI();\n // Media create/edit is content authoring and stays behind `readOnly`.\n // Playlist create/edit is curation, which every member type gets.\n const canCuratePlaylists = useCanCuratePlaylists();\n let content: React.JSX.Element;\n\n switch (screen) {\n case \"media\":\n if (detailId === \"new\" && !readOnly) {\n content = <MediaCreateScreen onBack={onBack} onNavigate={onNavigate} />;\n } else if (\n detailId &&\n detailId !== \"new\" &&\n action === \"edit\" &&\n !readOnly\n ) {\n content = (\n <MediaEditScreen\n mediaId={detailId}\n onNavigate={onNavigate}\n onBack={onBack}\n />\n );\n } else if (detailId && detailId !== \"new\") {\n content = (\n <MediaDetailScreen\n mediaId={detailId}\n onNavigate={onNavigate}\n onBack={onBack}\n parentBreadcrumb={parentBreadcrumb}\n />\n );\n } else {\n content = (\n <MediaListingScreen\n onNavigate={onNavigate}\n showProductSharablesCarousel={showProductSharablesCarousel}\n />\n );\n }\n break;\n case \"playlists\":\n case \"playlist\":\n if (detailId === \"new\" && canCuratePlaylists) {\n content = <PlaylistCreateScreen onBack={onBack} />;\n } else if (detailId && action === \"edit\" && canCuratePlaylists) {\n content = (\n <PlaylistCreateScreen playlistId={detailId} onBack={onBack} />\n );\n } else if (detailId && detailId !== \"new\") {\n content = (\n <PlaylistDetailScreen\n playlistId={detailId}\n onNavigate={onNavigate}\n onBack={onBack}\n parentBreadcrumb={parentBreadcrumb}\n />\n );\n } else {\n content = <PlaylistsListingScreen />;\n }\n break;\n case \"files\":\n content = <FilesListingScreen onNavigate={onNavigate} />;\n break;\n case \"pages\":\n case \"page\":\n content =\n detailId && detailId !== \"new\" ? (\n <PageDetailScreen\n pageId={detailId}\n onBack={onBack}\n parentBreadcrumb={parentBreadcrumb}\n />\n ) : (\n <PagesListingScreen onNavigate={onNavigate} />\n );\n break;\n case \"products\":\n default:\n content = detailId ? (\n <ProductDetailScreen\n productId={detailId}\n countryCode={countryCode}\n fetchProduct={fetchProduct}\n onNavigate={onNavigate}\n onBack={onBack}\n parentBreadcrumb={parentBreadcrumb}\n />\n ) : (\n <ProductsScreen\n countryCode={countryCode}\n fetchProducts={fetchProducts}\n onNavigate={onNavigate}\n />\n );\n break;\n }\n\n return content;\n}\n","import { useMemo, type ReactNode } from \"react\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useDomainDict } from \"@fluid-app/i18n/use-domain-dict\";\nimport {\n shareablesDomain,\n createShareablesTranslationAdapter,\n} from \"@fluid-app/shareables-core/translation-adapter\";\nimport { ShareablesTranslationProvider } from \"@fluid-app/shareables-core/translation-api-context\";\n\nexport function ShareablesTranslationBridge({\n children,\n}: {\n children: ReactNode;\n}): React.JSX.Element {\n const { locale } = useActiveLocale();\n const dict = useDomainDict(shareablesDomain, locale);\n const api = useMemo(\n () => createShareablesTranslationAdapter(locale, dict),\n [locale, dict],\n );\n return (\n <ShareablesTranslationProvider value={api}>\n {children}\n </ShareablesTranslationProvider>\n );\n}\n","export interface ShareablesRoute {\n screen: string | null;\n detailId: string | null;\n action: string | null;\n}\n\n/**\n * Parse the current shareables sub-route from the full slug.\n *\n * System nav slugs are \"share/products\", \"share/media\", \"share/playlists\".\n * Detail pages append an ID: \"share/products/123\", \"share/media/456\".\n *\n * \"share/products\" -> screen=\"products\", detailId=null\n * \"share/products/123\" -> screen=\"products\", detailId=\"123\"\n * \"share/media/456\" -> screen=\"media\", detailId=\"456\"\n * \"share/playlists\" -> screen=\"playlists\", detailId=null\n * \"share/playlists/789\" -> screen=\"playlists\", detailId=\"789\"\n * \"share/files\" -> screen=\"files\", detailId=null\n * \"share\" -> screen=null (default to products)\n */\nexport function parseShareablesRoute(currentSlug: string): ShareablesRoute {\n const slugWithoutPrefix = currentSlug.replace(/^share\\/?/, \"\");\n if (!slugWithoutPrefix) {\n return { screen: null, detailId: null, action: null };\n }\n\n const parts = slugWithoutPrefix.split(\"/\");\n const screen = parts[0] || null;\n const detailId = parts[1] || null;\n const action = parts[2] || null;\n return { screen, detailId, action };\n}\n\n/**\n * Resolve the product the user navigated through to reach a media detail page.\n *\n * Returns a product id only when the current route is a plain media detail\n * route and the previous in-session route was a plain product detail route.\n * `null` means the caller should skip fetching a source product. Routes with\n * an `action` segment are rejected because edit/create sub-routes are not\n * stable source-detail breadcrumbs.\n */\nexport function getSourceProductIdForMediaDetail(\n currentRoute: ShareablesRoute,\n parentSlug: string | null | undefined,\n): string | null {\n if (\n currentRoute.screen !== \"media\" ||\n !currentRoute.detailId ||\n currentRoute.action\n ) {\n return null;\n }\n\n if (!parentSlug) {\n return null;\n }\n\n const parentRoute = parseShareablesRoute(parentSlug);\n if (\n parentRoute.screen !== \"products\" ||\n !parentRoute.detailId ||\n parentRoute.action\n ) {\n return null;\n }\n\n return parentRoute.detailId;\n}\n","import { useCallback, useMemo, useRef, type ComponentProps } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { PORTAL_MYSITE_KEYS } from \"@fluid-app/mysite-ui/portal/hooks/use-mysite-portal\";\nimport { usePortalProduct } from \"@fluid-app/products-core/hooks/use-portal-products\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { ShareablesCoreProvider } from \"@fluid-app/shareables-core\";\nimport type { media } from \"@fluid-app/shareables-core/media-api\";\nimport { ShareablesUIProvider, ShareablesApp } from \"@fluid-app/shareables-ui\";\nimport type { PickerListParams } from \"@fluid-app/shareables-ui\";\nimport { useFilePickerApi } from \"@fluid-app/file-picker-core\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useNavigationParent } from \"../shell/use-navigation-parent\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\nimport { useStore } from \"../hooks/use-store\";\nimport { ShareablesTranslationBridge } from \"../providers/ShareablesTranslationBridge\";\nimport { useShareablesTranslation } from \"@fluid-app/shareables-core/translation-api-context\";\nimport {\n PortalContentApiProvider,\n usePortalContentContext,\n} from \"../content/PortalContentApiProvider\";\nimport { toggleFavorite } from \"../adapters/content-api-adapter\";\nimport { usePortalTenantContentClient } from \"../providers/PortalTenantContentClientProvider\";\nimport {\n getSourceProductIdForMediaDetail,\n parseShareablesRoute,\n} from \"./shareables-source-context\";\n\ntype ShareablesScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n showProductSharablesCarousel?: boolean;\n};\n\nexport function ShareablesScreen({\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n showProductSharablesCarousel = false,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: ShareablesScreenProps): React.JSX.Element {\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <ShareablesTranslationBridge>\n <PortalContentApiProvider>\n <ShareablesScreenContent\n showProductSharablesCarousel={showProductSharablesCarousel}\n />\n </PortalContentApiProvider>\n </ShareablesTranslationBridge>\n </div>\n );\n}\n\n/** Inner component rendered inside providers so hooks can access context. */\nfunction ShareablesScreenContent({\n showProductSharablesCarousel,\n}: {\n showProductSharablesCarousel: boolean;\n}): React.JSX.Element {\n const { t, locale } = useShareablesTranslation();\n // Keep a ref to the latest `t` so callbacks inside uiConfig can use\n // translations without forcing useMemo to recreate the entire config\n // object every time the locale changes. Recreating uiConfig causes\n // ShareablesUIProvider to get a new value, which unmounts/remounts all\n // children — including in-flight TanStack Query hooks — breaking\n // media loading when the user switches language.\n const tRef = useRef(t);\n tRef.current = t;\n const localeRef = useRef(locale);\n localeRef.current = locale;\n const shareablesCtx = usePortalContentContext();\n const { productsApi: portalProductsApi } = shareablesCtx;\n const filePickerApi = useFilePickerApi();\n const { data: account } = useAccount();\n const { data: store } = useStore();\n const { currentSlug, navigate } = useAppNavigation();\n const parentBreadcrumb = useNavigationParent();\n // Restricted (read-only) when the member type is not rep-eligible. Named\n // `isCustomer` historically; now derived from the rep_eligible permission\n // so company-custom non-rep types are restricted the same as customers.\n const isCustomer = deniesRepSurfaces(account);\n const client = usePortalTenantContentClient();\n const queryClient = useQueryClient();\n\n const fetchProducts = useCallback(\n async (search: string, cursor?: string, limit?: number, sort?: string) => {\n if (search) {\n return portalProductsApi.searchProducts(search, { cursor, limit });\n }\n return portalProductsApi.listProducts({ cursor, limit, sort });\n },\n [portalProductsApi],\n );\n\n const fetchProduct = useCallback(\n async (id: string | number) => portalProductsApi.getProduct(id),\n [portalProductsApi],\n );\n\n const { screen, detailId, action } = parseShareablesRoute(currentSlug);\n const sourceProductId = getSourceProductIdForMediaDetail(\n { screen, detailId, action },\n parentBreadcrumb?.slug,\n );\n\n const { data: sourceProductResponse } = usePortalProduct(\n sourceProductId ?? \"\",\n {\n enabled: !!sourceProductId,\n },\n );\n\n const sourceProductTitle = sourceProductResponse?.product.name;\n const shareablesParentBreadcrumb = useMemo(() => {\n if (!parentBreadcrumb) {\n return null;\n }\n\n return sourceProductTitle\n ? { ...parentBreadcrumb, label: sourceProductTitle }\n : parentBreadcrumb;\n }, [parentBreadcrumb, sourceProductTitle]);\n\n const handleNavigate = useCallback(\n (subScreen: string, id?: string) => {\n const path = id ? `share/${subScreen}/${id}` : `share/${subScreen}`;\n navigate(path);\n },\n [navigate],\n );\n\n const handleBack = useCallback(() => {\n if (detailId && screen) {\n navigate(`share/${screen}`);\n } else {\n navigate(\"share/products\");\n }\n }, [navigate, detailId, screen]);\n\n const coreConfig = useMemo(\n () => ({\n user: account ? { id: account.id } : null,\n repContext: true,\n }),\n [account],\n );\n\n const uiConfig = useMemo(\n () => ({\n user: account\n ? {\n id: account.id,\n company: { logo_url: store?.logo_url ?? null },\n }\n : undefined,\n // TODO(portal-tenant): affiliate_id not available from /api/account\n affiliateId: null,\n basePath: \"\",\n navigate: (path: string) => {\n const cleanPath = path.replace(/^\\//, \"\");\n const prefixed = cleanPath.startsWith(\"share/\")\n ? cleanPath\n : `share/${cleanPath}`;\n navigate(prefixed);\n },\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n error?: unknown;\n }) => {\n fluidToast({ title: opts.title, type: opts.type });\n if (opts.error) console.error(\"[Shareables]\", opts.error);\n },\n filePickerApi,\n onToggleFavorite: async (params: {\n favoriteableId: number;\n favoriteableType: string;\n }) => {\n const result = await toggleFavorite(\n client,\n params.favoriteableId,\n params.favoriteableType,\n );\n void queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n return result;\n },\n onMySiteShare: async (params: {\n shareLink: string;\n relateable_id: number;\n relateable_type: string;\n }) => {\n await toggleFavorite(\n client,\n params.relateable_id,\n params.relateable_type,\n );\n void queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n // Playlist curation is open to every member type, including customers —\n // the API refuses any playlist the member doesn't own, so the worst a\n // customer can reach is their own. Deliberately not gated on\n // `isCustomer` like the content-authoring capabilities below.\n onDeletePlaylist: async (playlistId: number) => {\n await shareablesCtx.playlistsAdapter.deletePlaylist(playlistId);\n },\n mediaProductsApi: isCustomer\n ? undefined\n : {\n ...shareablesCtx.mediaProductsAdapter,\n listMediaProducts: (\n mediumId: number,\n params?: media.MediaProductsListParams,\n ) =>\n shareablesCtx.mediaProductsAdapter.listMediaProducts(mediumId, {\n ...params,\n language_iso: params?.language_iso ?? localeRef.current,\n }),\n },\n searchProducts: isCustomer\n ? undefined\n : async (\n query: string,\n options?: { cursor?: string | null; limit?: number },\n ) => {\n // Empty query → listProducts so the tag modal opens with\n // suggestions instead of a blank state. Non-empty → real search.\n // Both APIs forward cursor/limit so the tag modal can paginate.\n const limit = options?.limit ?? 25;\n const cursor = options?.cursor ?? undefined;\n const result =\n query.trim().length > 0\n ? await portalProductsApi.searchProducts(query, {\n cursor,\n limit,\n })\n : await portalProductsApi.listProducts({ cursor, limit });\n // Filter out products without an id — mapping an id-less record\n // to `id: 0` would let a subsequent add/remove tag operation\n // silently run on id=0 (confusing 404/422 at best, incorrect\n // mutation at worst).\n const mapped = (result.products ?? [])\n .map(\n (p: {\n id?: number;\n title?: string;\n name?: string;\n image_url?: string | null;\n images?: Array<{ url?: string | null }>;\n price?: string | null;\n }) =>\n p.id != null\n ? {\n id: p.id,\n name:\n p.name ?? p.title ?? tRef.current(\"common_untitled\"),\n image_url: p.image_url ?? p.images?.[0]?.url ?? null,\n price: p.price ?? null,\n }\n : null,\n )\n .filter((p): p is NonNullable<typeof p> => p !== null);\n return {\n products: mapped,\n nextCursor: result.meta?.pagination?.next_cursor ?? null,\n };\n },\n // `readOnly` covers content authoring only: creating and editing media,\n // tagging products, uploading thumbnails. Playlist curation is answered\n // by `canCuratePlaylists`, which stays open to everyone.\n readOnly: isCustomer,\n canCuratePlaylists: true,\n // pickerSources powers the playlist editor's content browser tabs.\n // All four content types are wired here: Media via the existing media\n // adapter, Pages via pagesAdapter, Products via portalProductsApi, and\n // Enrollment Packs via enrollmentPacksAdapter. Each tab is also gated\n // on the host supplying its source — see FilePicker's methods filter\n // for the runtime check.\n pickerSources: {\n pages: {\n cacheKey: `locale:${locale}`,\n list: async ({ search, cursor, limit }: PickerListParams) => {\n const response = await shareablesCtx.pagesAdapter.listPages({\n cursor: cursor ?? undefined,\n limit,\n \"filter[title]\":\n search && search.trim().length > 0 ? search : undefined,\n language_iso: localeRef.current,\n });\n return {\n items: response.pages.map((p) => ({\n id: p.id,\n title:\n p.title ??\n tRef.current(\"pages_fallback_name\", { id: String(p.id) }),\n image_url: p.image_url ?? null,\n })),\n nextCursor: response.meta.pagination?.next_cursor ?? null,\n };\n },\n },\n products: {\n cacheKey: `locale:${locale}`,\n list: async ({ search, cursor, limit }: PickerListParams) => {\n const trimmedSearch = search?.trim() ?? \"\";\n const result =\n trimmedSearch.length > 0\n ? await portalProductsApi.searchProducts(trimmedSearch, {\n cursor: cursor ?? undefined,\n limit,\n })\n : await portalProductsApi.listProducts({\n cursor: cursor ?? undefined,\n limit,\n });\n // PortalProductsApi.Product is { id?, name?, images?, price?, ... }\n // — no top-level `title` or `image_url`. The thumbnail comes\n // from the first entry of `images`.\n const mapped = (result.products ?? [])\n .map((p) =>\n p.id != null\n ? {\n id: p.id,\n name: p.name ?? tRef.current(\"common_untitled\"),\n image_url: p.images?.[0]?.url ?? null,\n price: p.price ?? null,\n }\n : null,\n )\n .filter((p): p is NonNullable<typeof p> => p !== null);\n return {\n items: mapped,\n nextCursor: result.meta?.pagination?.next_cursor ?? null,\n };\n },\n },\n enrollment_packs: {\n cacheKey: `locale:${locale}`,\n // /api/enrollment_packs takes cursor/limit only — no title\n // filter — so we hide the picker's search input rather than\n // surface a non-functional one. Flip to true once the\n // backend ships filter[title] support.\n supportsSearch: false,\n list: async ({ cursor, limit }: PickerListParams) => {\n const response =\n await shareablesCtx.enrollmentPacksAdapter.listEnrollmentPacks({\n cursor: cursor ?? undefined,\n limit,\n });\n const items = response.enrollment_packs.map((pack) => ({\n id: pack.id,\n title:\n pack.title ||\n tRef.current(\"enrollment_pack_fallback_name\", {\n id: String(pack.id),\n }),\n image_url: pack.images[0]?.url ?? null,\n }));\n return {\n items,\n nextCursor: response.meta.pagination?.next_cursor ?? null,\n };\n },\n },\n },\n uploadThumbnail: isCustomer\n ? undefined\n : async (blob: Blob, filename: string) => {\n const response = await filePickerApi.createDamAsset({\n file: new File([blob], filename, { type: blob.type }),\n name: filename,\n });\n const url = response.asset?.default_variant_url;\n if (!url) {\n // DAM accepted the upload but didn't return a playable URL.\n // Throwing here lets the caller (MediaEditScreen catch handler)\n // surface a toast and keeps the pending thumbnail state clean —\n // returning \"\" silently would corrupt the save payload with an\n // empty thumbnail URL.\n throw new Error(\"Thumbnail upload succeeded but no URL returned\");\n }\n return url;\n },\n }),\n [\n account,\n store,\n locale,\n navigate,\n filePickerApi,\n isCustomer,\n shareablesCtx.playlistsAdapter,\n shareablesCtx.pagesAdapter,\n shareablesCtx.enrollmentPacksAdapter,\n shareablesCtx.mediaProductsAdapter,\n portalProductsApi,\n client,\n queryClient,\n ],\n );\n\n return (\n <ShareablesCoreProvider config={coreConfig}>\n <ShareablesUIProvider config={uiConfig}>\n <ShareablesApp\n screen={screen}\n detailId={detailId}\n action={action}\n companyLogoUrl={undefined}\n countryCode={undefined}\n fetchProducts={fetchProducts}\n fetchProduct={fetchProduct}\n onNavigate={handleNavigate}\n onBack={handleBack}\n showProductSharablesCarousel={showProductSharablesCarousel}\n parentBreadcrumb={shareablesParentBreadcrumb}\n />\n </ShareablesUIProvider>\n </ShareablesCoreProvider>\n );\n}\n\nexport const shareablesScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShareablesScreen\",\n displayName: \"Shareables Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n {\n key: \"showProductSharablesCarousel\",\n label: \"Show product sharables carousel\",\n type: \"boolean\",\n description:\n \"Display product-led sharable kits above the media list. Disabled by default so existing portals keep their current media page.\",\n defaultValue: false,\n tab: \"styling\",\n group: \"Display\",\n },\n ],\n} as const satisfies WidgetPropertySchema;\n"],"x_google_ignoreList":[4,10,11],"mappings":"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"}