/** * useFileViewer — resolves which registered viewer can handle a given MIME type. * * Other modules register viewers at the `documents_file_viewer` integration * point by including an entry in their `integrations.ts`: * * ```ts * documents_file_viewer: [{ * id: 'my_viewer', * meta: { mimeTypes: ['application/pdf'] }, * component: () => import('./components/MyViewer.vue'), * }] * ``` * * Viewer components receive the props: `url: string`, `entity: EntityObject`, `mime: string`. */ import { useIntegrationStore } from '@KTXC' import type { EntityObject } from '@DocumentsManager/models/entity' const INTEGRATION_POINT = 'documents_file_viewer' function mimeMatchesPattern(mime: string, pattern: string): boolean { if (pattern.endsWith('/*')) { // e.g. 'image/*' matches 'image/png', 'image/jpeg', … return mime.startsWith(pattern.slice(0, -1)) } return mime === pattern } function viewerMatchesMime( mime: string, mimeTypes?: string[], mimePatterns?: string[], ): boolean { if (mimeTypes?.includes(mime)) return true if (mimePatterns) { for (const pattern of mimePatterns) { if (mimeMatchesPattern(mime, pattern)) return true } } return false } export function useFileViewer() { const integrationStore = useIntegrationStore() /** * Returns the highest-priority registered viewer that can handle `mime`, * or `null` if none is found. */ function findViewer(mime: string) { // getItems() already returns items sorted by priority (ascending) const viewers = integrationStore.getItems(INTEGRATION_POINT) for (const viewer of viewers) { if ( viewerMatchesMime( mime, viewer.meta?.mimeTypes as string[] | undefined, viewer.meta?.mimePatterns as string[] | undefined, ) ) { return viewer } } return null } /** * Convenience: returns true if any registered viewer can open this entity. */ function canOpen(entity: EntityObject): boolean { const mime = entity.properties.mime if (!mime) return false return findViewer(mime) !== null } return { findViewer, canOpen } } export type UseFileViewerReturn = ReturnType