@forgeroute/ui 0.3.6 → 0.3.8

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":"forgeroute-ui-pdf.mjs","names":[],"sources":["../../../projects/forgeroute-ui/pdf/src/pdf-viewer/pdf-viewer.component.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n PLATFORM_ID, Component, ChangeDetectionStrategy, DestroyRef, ElementRef, InjectionToken, Injector, afterNextRender, computed, effect,\n inject, input, output, signal, untracked,\n} from '@angular/core';\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\n\n/** Where pdf.js finds its worker script. Provide it once in the app (`{ provide: PDF_VIEWER_WORKER_SRC, useValue: '/assets/pdf.worker.min.mjs' }`). */\nexport const PDF_VIEWER_WORKER_SRC = new InjectionToken<string>('PDF_VIEWER_WORKER_SRC', { providedIn: 'root', factory: () => '' });\n\n/** Colour of an overlay badge. */\nexport type PdfViewerTone = 'blue' | 'green' | 'amber' | 'red' | 'slate';\n\n/** A labelled box drawn over a page. Positions are PDF points with the origin at the page's bottom-left corner. */\nexport interface PdfViewerField {\n id: string;\n /** 1-based page number; a field on a page that does not exist is ignored. */\n page: number;\n /** Bottom-left corner of the box, in PDF points. */\n x: number;\n y: number;\n /** Size in PDF points. Without a width the box fits its label; the default height is 14. */\n width?: number;\n height?: number;\n label: string;\n tone?: PdfViewerTone;\n}\n\nexport interface PdfViewerLoadedEvent {\n pageCount: number;\n /** Every page's size in PDF points at scale 1. */\n pages: { width: number; height: number }[];\n /** The loaded pdf.js document, so the host can read text content. The viewer destroys it when it is replaced or the viewer goes away. */\n document: PDFDocumentProxy;\n}\n\nexport interface PdfViewerRenderedEvent {\n pageCount: number;\n /** CSS px per PDF point of the first page. */\n scale: number;\n /** Displayed (CSS px) width of every page. */\n pageWidths: number[];\n}\n\ntype PdfJsModule = typeof import('pdfjs-dist');\ntype Status = 'idle' | 'loading' | 'ready' | 'error';\n\ninterface PageView {\n number: number;\n cssWidth: number;\n cssHeight: number;\n canvasWidth: number;\n canvasHeight: number;\n fields: FieldView[];\n}\ninterface FieldView {\n id: string; label: string; tone: PdfViewerTone;\n left: number; top: number; width: number | null; maxWidth: number; height: number; fontPx: number;\n}\n\nconst DEFAULT_MAX_WIDTH = 700;\nconst DEFAULT_FIELD_HEIGHT = 14;\n/** Most pixels one page's canvas may have; Safari (iOS especially) refuses or blanks bigger ones. */\nconst MAX_CANVAS_PIXELS = 16_000_000;\n/** Most pixels all canvases together may have; iOS Safari limits total canvas memory. */\nconst MAX_TOTAL_CANVAS_PIXELS = 48_000_000;\nconst WORKER_MISSING = 'PDF worker is not configured.';\n\n/**\n * Renders every page of a PDF into a canvas (crisp on high-density screens), with optional labelled boxes overlaid in PDF coordinates.\n * Needs `pdfjs-dist` (loaded on first use, so it stays out of bundles that never show a PDF).\n *\n * <ui-pdf-viewer [data]=\"buffer()\" [fields]=\"badges()\" ariaLabel=\"Contract preview\" (loaded)=\"onLoaded($event)\" />\n * <ui-pdf-viewer src=\"/files/contract.pdf\" [httpHeaders]=\"{ Authorization: 'Bearer ' + token }\" />\n *\n * - `src` / `data`: a URL, or the bytes (`data` wins). The bytes are copied, so your buffer stays usable.\n * - `workerSrc` or the `PDF_VIEWER_WORKER_SRC` token: the pdf.js worker URL. Without either the viewer shows \"PDF worker is not configured.\"\n * - `maxPageWidth` (default 700): pages are drawn this wide, or the container's width when narrower; never larger.\n * - `pixelRatio`: canvas pixels per CSS pixel; default is the screen's ratio between 1 and 3.\n * - `fields`: badges positioned in PDF points (origin bottom-left); they scale with the page.\n * - `loaded` / `pagesRendered` / `loadFailed`: see the event types. A new `src` / `data` replaces the document; a slow older load never paints.\n * A page that fails to draw after `loaded` also emits `loadFailed`, shows the error state and destroys the document that `loaded` handed to the host.\n * - A page's canvas never exceeds 16,000,000 pixels (Safari limit); the pixel ratio is lowered for such a page, its CSS size is unchanged.\n * - All canvases together never exceed 48,000,000 pixels (iOS Safari limits total canvas memory): a long document is drawn at a lower\n * density, the same for every page and never below 1 pixel per CSS pixel. CSS sizes are unchanged.\n * - `httpHeaders` and `withCredentials` apply to `src` only; they are not used when `data` is given.\n * - `fields[].id` is only a display / test hook; duplicates are tolerated. `httpHeaders` is compared by content, but a stable reference is still preferred.\n * - The worker-missing message is fixed text and ignores `errorText`, because it is a setup problem for the developer.\n * - Needs pdf.js 5 (`pdfjs-dist` >= 5), which needs Safari 17.4+; the legacy pdf.js build is not supported by this component.\n * - The public typings reference `pdfjs-dist` types: apps that type-check the library without `pdfjs-dist` installed need `skipLibCheck` or the\n * package (the same trade-off as `@forgeroute/ui/charts` and chart.js).\n */\n@Component({\n selector: 'ui-pdf-viewer',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pv-root\" [attr.role]=\"ariaLabel() ? 'region' : null\" [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"status() === 'loading'\">\n <div class=\"pv-live\" role=\"status\" aria-live=\"polite\">{{ status() === 'loading' ? loadingText() : '' }}</div>\n @switch (status()) {\n @case ('loading') { <div class=\"pv-state pv-loading\" aria-hidden=\"true\">{{ loadingText() }}</div> }\n @case ('error') { <div class=\"pv-state pv-error\" role=\"alert\">{{ errorMessage() }}</div> }\n @case ('ready') {\n <div class=\"pv-pages\">\n @for (p of views(); track p.number) {\n <div class=\"pv-page\" [style.width.px]=\"p.cssWidth\">\n <canvas class=\"pv-canvas\" role=\"img\" [attr.aria-label]=\"'Page ' + p.number + ' of ' + views().length\"\n [attr.width]=\"p.canvasWidth\" [attr.height]=\"p.canvasHeight\"></canvas>\n @for (f of p.fields; track $index) {\n <div class=\"pv-field pv-tone-{{ f.tone }}\"\n [style.left.%]=\"f.left\" [style.top.%]=\"f.top\" [style.width.%]=\"f.width\" [style.max-width.%]=\"f.maxWidth\"\n [style.height.%]=\"f.height\" [style.font-size.px]=\"f.fontPx\">\n <span class=\"pv-field-label\">{{ f.label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n `,\n styles: [`\n :host { display: block; width: 100%; }\n .pv-root { position: relative; }\n .pv-live { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }\n .pv-state { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 12rem; padding: 0 16px; text-align: center;\n font-size: 0.8125rem; line-height: 1.4; overflow-wrap: anywhere; box-sizing: border-box; }\n .pv-loading { color: #64748b; }\n .pv-error { color: #dc2626; }\n .pv-pages { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 100%; }\n .pv-page { position: relative; margin: 0 auto; max-width: 100%; }\n .pv-canvas { display: block; width: 100%; height: auto; background: #fff; border-radius: 2px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); }\n .pv-field { position: absolute; display: flex; align-items: center; box-sizing: border-box; padding: 0 4px; overflow: hidden;\n pointer-events: none; border: 1px solid; border-radius: 2px; -webkit-user-select: none; user-select: none; }\n .pv-field-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; }\n .pv-tone-blue { background: #dbeafe; border-color: #93c5fd; color: #1d4ed8; }\n .pv-tone-green { background: #dcfce7; border-color: #86efac; color: #15803d; }\n .pv-tone-amber { background: #fef3c7; border-color: #fcd34d; color: #92400e; }\n .pv-tone-red { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n .pv-tone-slate { background: #f1f5f9; border-color: #cbd5e1; color: #334155; }\n `],\n})\nexport class PdfViewerComponent {\n /** PDF URL. Ignored while `data` is set. */\n readonly src = input<string | null>(null);\n /** PDF bytes. Wins over `src`. */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** Request headers for `src`. Changes are detected by content, but pass a stable reference when you can. */\n readonly httpHeaders = input<Record<string, string>>({});\n readonly withCredentials = input(false);\n /** pdf.js worker URL; falls back to the `PDF_VIEWER_WORKER_SRC` token. */\n readonly workerSrc = input('');\n /** Widest a page is drawn, in CSS px. */\n readonly maxPageWidth = input(DEFAULT_MAX_WIDTH);\n /** Canvas pixels per CSS pixel; null = the screen's ratio, limited to 1..3. */\n readonly pixelRatio = input<number | null>(null);\n readonly fields = input<PdfViewerField[]>([]);\n /** Accessible name of the viewer. */\n readonly ariaLabel = input('');\n readonly loadingText = input('Loading document…');\n readonly errorText = input('Failed to load document.');\n\n readonly loaded = output<PdfViewerLoadedEvent>();\n readonly loadFailed = output<unknown>();\n readonly pagesRendered = output<PdfViewerRenderedEvent>();\n\n protected readonly status = signal<Status>('idle');\n private readonly pageSizes = signal<{ width: number; height: number }[]>([]);\n private readonly containerWidth = signal(0);\n private readonly customError = signal('');\n protected readonly errorMessage = computed(() => this.customError() || this.errorText());\n\n /** Changes exactly when some canvas bitmap changes size (and so is cleared). */\n private readonly drawKey = computed(() => this.views().map(v => v.canvasWidth + 'x' + v.canvasHeight).join('|'));\n private readonly effectiveWidth = computed(() => this.widthFor(this.containerWidth()));\n protected readonly views = computed((): PageView[] => {\n const sizes = this.pageSizes();\n const cssWidth = this.effectiveWidth();\n const ratio = this.resolvedRatio();\n const byPage = new Map<number, PdfViewerField[]>();\n for (const f of this.fields()) {\n if (!Number.isInteger(f.page) || f.page < 1 || f.page > sizes.length) continue;\n const list = byPage.get(f.page) ?? [];\n list.push(f);\n byPage.set(f.page, list);\n }\n const base = sizes.map(size => {\n const scale = cssWidth / size.width;\n const cssHeight = size.height * scale;\n // lower the density for a page whose bitmap would pass the canvas limit; the CSS size stays\n const pageRatio = Math.min(ratio, Math.sqrt(MAX_CANVAS_PIXELS / (cssWidth * cssHeight)));\n return { scale, cssHeight, pageRatio };\n });\n // a total budget for long documents: shrink every page's density by the same factor, but never below 1 (or below a cap-lowered ratio)\n let factor = 1;\n const bitmaps = () => base.map(b => {\n const r = Math.max(Math.min(b.pageRatio, 1), b.pageRatio * factor);\n return { w: Math.floor(cssWidth * r), h: Math.floor(b.cssHeight * r) };\n });\n let sized = bitmaps();\n for (let i = 0; i < 6; i++) {\n const total = sized.reduce((sum, b) => sum + b.w * b.h, 0);\n if (total <= MAX_TOTAL_CANVAS_PIXELS) break;\n factor *= Math.sqrt(MAX_TOTAL_CANVAS_PIXELS / total);\n sized = bitmaps();\n }\n return sizes.map((size, i) => {\n const { scale, cssHeight } = base[i];\n const fields = (byPage.get(i + 1) ?? []).map((f): FieldView => {\n const h = f.height ?? DEFAULT_FIELD_HEIGHT;\n const left = (f.x / size.width) * 100;\n return {\n id: f.id, label: f.label, tone: f.tone ?? 'blue',\n left, top: ((size.height - f.y - h) / size.height) * 100,\n width: f.width == null ? null : (f.width / size.width) * 100,\n maxWidth: Math.max(0, 100 - left),\n height: (h / size.height) * 100,\n fontPx: Math.max(9, h * scale * 0.7),\n };\n });\n return { number: i + 1, cssWidth, cssHeight, canvasWidth: sized[i].w, canvasHeight: sized[i].h, fields };\n });\n });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n private readonly tokenWorkerSrc = inject(PDF_VIEWER_WORKER_SRC);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly headersKey = computed(() => JSON.stringify(this.httpHeaders()));\n\n /** Bumped by every new load, replacement and destroy; an async step whose token no longer matches stops. */\n private loadToken = 0;\n /** Bumped by every (re)draw so only the newest draw paints. */\n private drawToken = 0;\n private doc: PDFDocumentProxy | null = null;\n private loadingTask: { destroy?: () => unknown } | null = null;\n private pageProxies: PDFPageProxy[] = [];\n private renderTasks: { cancel?: () => void }[] = [];\n private observer: ResizeObserver | null = null;\n private frame = 0;\n private lastDrawKey = '';\n\n constructor() {\n effect(() => {\n this.src(); this.data(); this.headersKey(); this.withCredentials(); this.workerSrc();\n if (!this.isBrowser) return; // nothing to load or draw on the server\n untracked(() => void this.startLoad());\n });\n // Whatever resizes the canvases (maxPageWidth, pixelRatio) clears them, so a changed bitmap size needs a redraw. Draws the code\n // already scheduled record their key in scheduleDraw(), so this only fires for changes nothing else handled.\n effect(() => {\n const key = this.drawKey();\n untracked(() => { if (this.status() === 'ready' && key !== this.lastDrawKey) this.scheduleDraw(); });\n });\n if (this.isBrowser && typeof ResizeObserver !== 'undefined') {\n this.observer = new ResizeObserver(() => this.onResize());\n this.observer.observe(this.host.nativeElement);\n }\n inject(DestroyRef).onDestroy(() => {\n this.loadToken++;\n this.drawToken++;\n this.observer?.disconnect();\n if (this.frame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(this.frame);\n this.teardown();\n });\n }\n\n /** Test seam: loads pdf.js on first use so apps that never show a PDF do not bundle or fetch it. */\n protected loadPdfJs(): Promise<PdfJsModule> {\n return import('pdfjs-dist');\n }\n\n private resolvedRatio(): number {\n const explicit = this.pixelRatio();\n if (explicit != null && explicit > 0) return explicit;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n return Math.min(Math.max(dpr, 1), 3);\n }\n\n private widthFor(container: number): number {\n const max = this.maxPageWidth() > 0 ? this.maxPageWidth() : DEFAULT_MAX_WIDTH;\n return container > 0 ? Math.min(max, container) : max;\n }\n\n private async startLoad(): Promise<void> {\n const token = ++this.loadToken;\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set('');\n const data = this.data();\n const src = this.src();\n if (data == null && !src) { this.status.set('idle'); return; }\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n this.status.set('loading');\n try {\n const pdfjs = await this.loadPdfJs();\n if (token !== this.loadToken) return;\n const worker = this.workerSrc() || this.tokenWorkerSrc;\n if (worker) pdfjs.GlobalWorkerOptions.workerSrc = worker;\n else if (!pdfjs.GlobalWorkerOptions.workerSrc) { this.fail(new Error(WORKER_MISSING), WORKER_MISSING); return; }\n\n const params: Record<string, unknown> = data != null\n ? { data: data instanceof Uint8Array ? data.slice() : new Uint8Array(data.slice(0)) } // pdf.js detaches what it is given\n : { url: src };\n if (data == null) { // request options only make sense for a URL\n const headers = this.httpHeaders();\n if (Object.keys(headers).length > 0) params['httpHeaders'] = headers;\n if (this.withCredentials()) params['withCredentials'] = true;\n }\n const task = pdfjs.getDocument(params as unknown as Parameters<PdfJsModule['getDocument']>[0]);\n this.loadingTask = task;\n const doc = await task.promise;\n if (token !== this.loadToken) { this.destroyDoc(doc); return; }\n this.doc = doc;\n\n const proxies: PDFPageProxy[] = [];\n const sizes: { width: number; height: number }[] = [];\n for (let i = 1; i <= doc.numPages; i++) {\n const page = await doc.getPage(i);\n if (token !== this.loadToken) return;\n const vp = page.getViewport({ scale: 1 });\n if (!(Number.isFinite(vp.width) && Number.isFinite(vp.height) && vp.width > 0 && vp.height > 0)) throw new Error('invalid page size');\n proxies.push(page);\n sizes.push({ width: vp.width, height: vp.height });\n }\n this.pageProxies = proxies;\n this.containerWidth.set(this.host.nativeElement.clientWidth); // the container may have changed while the file loaded\n this.pageSizes.set(sizes);\n this.status.set('ready');\n this.loaded.emit({ pageCount: sizes.length, pages: sizes.map(s => ({ ...s })), document: doc });\n this.scheduleDraw();\n } catch (err) {\n if (token !== this.loadToken) return;\n this.fail(err);\n }\n }\n\n private fail(err: unknown, message = ''): void {\n this.loadToken++; // nothing still in flight may paint over the error\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set(message);\n this.status.set('error');\n this.loadFailed.emit(err);\n }\n\n private scheduleDraw(): void {\n const id = ++this.drawToken;\n this.lastDrawKey = this.drawKey();\n this.cancelRenders(); // a still-running paint must not land in canvases about to be resized\n afterNextRender(() => void this.draw(id), { injector: this.injector });\n }\n\n private async draw(id: number): Promise<void> {\n const token = this.loadToken;\n const stale = () => id !== this.drawToken || token !== this.loadToken;\n this.cancelRenders();\n const views = this.views();\n const canvases = Array.from(this.host.nativeElement.querySelectorAll<HTMLCanvasElement>('canvas.pv-canvas'));\n const sizes = this.pageSizes();\n const scales: number[] = [];\n try {\n for (let i = 0; i < views.length; i++) {\n if (stale()) return;\n const canvas = canvases[i];\n const page = this.pageProxies[i];\n if (!canvas || !page || !canvas.getContext('2d')) throw new Error('canvas unavailable');\n const cssScale = views[i].cssWidth / sizes[i].width;\n scales.push(cssScale);\n const viewport = page.getViewport({ scale: (views[i].canvasWidth / sizes[i].width) });\n const task = page.render({ canvas, viewport });\n this.renderTasks.push(task);\n await task.promise;\n }\n } catch (err) {\n if (stale() || (err as { name?: string } | null)?.name === 'RenderingCancelledException') return;\n this.fail(err);\n return;\n }\n if (stale()) return;\n this.pagesRendered.emit({ pageCount: views.length, scale: scales[0] ?? 1, pageWidths: views.map(v => v.cssWidth) });\n }\n\n private onResize(): void {\n if (this.frame || typeof requestAnimationFrame !== 'function') return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n const before = this.effectiveWidth();\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n if (this.effectiveWidth() !== before && this.status() === 'ready') this.scheduleDraw();\n });\n }\n\n private cancelRenders(): void {\n for (const t of this.renderTasks) { try { t.cancel?.(); } catch { /* already finished */ } }\n this.renderTasks = [];\n }\n\n private teardown(): void {\n this.cancelRenders();\n const task = this.loadingTask;\n this.loadingTask = null;\n if (task) { try { Promise.resolve(task.destroy?.()).catch(() => undefined); } catch { /* ignore */ } }\n const doc = this.doc;\n this.doc = null;\n this.pageProxies = [];\n if (doc) this.destroyDoc(doc);\n }\n\n private destroyDoc(doc: PDFDocumentProxy): void {\n try { Promise.resolve(doc.destroy()).catch(() => undefined); } catch { /* ignore */ }\n }\n}\n"],"mappings":"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"}
@@ -2143,7 +2143,7 @@ var ModalComponent = class ModalComponent {
2143
2143
  }
2144
2144
  `,
2145
2145
  isInline: true,
2146
- styles: [".m-backdrop{position:fixed;inset:0;background:#02081773;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:200;animation:m-fade .18s ease}.m-modal:focus{outline:none}.m-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:16px;border:1px solid rgba(0,0,0,.07);z-index:201;width:calc(100% - 32px);box-shadow:0 4px 6px #0000000a,0 16px 32px #0000001f,0 48px 96px #0000001a;max-height:calc(100vh - 48px);display:flex;flex-direction:column;animation:m-scale .22s cubic-bezier(.16,1,.3,1)}.m-header{display:flex;align-items:flex-start;justify-content:space-between;padding:20px 24px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0;border-radius:16px 16px 0 0;flex-shrink:0}.m-header-text{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}.m-title{font-size:1.125rem;font-weight:700;color:#0f172a;margin:0;letter-spacing:-.02em;line-height:1.3}.m-description{font-size:.8125rem;color:#64748b;margin:0;line-height:1.4}.m-close{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:none;background:none;cursor:pointer;color:#94a3b8;flex-shrink:0;margin-left:12px;transition:background .13s,color .13s}.m-close:hover{background:#f1f5f9;color:#1e293b}.m-close:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.m-body{padding:20px 24px;overflow-y:auto;flex:1;min-height:0;font-size:.9rem;color:#475569;line-height:1.65}.m-footer{padding:14px 24px 18px;background:#f8fafc;border-top:1px solid #e2e8f0;border-radius:0 0 16px 16px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:8px}.m-footer:not(:has(*)){display:none}.m-footer-left{display:flex;align-items:center;gap:8px}.m-footer-right{display:flex;align-items:center;gap:8px;margin-left:auto}@media(max-width:480px){.m-modal{top:auto;bottom:var(--ui-kb, 0px);left:0;right:0;transform:none;width:100vw;max-width:none!important;max-height:min(85dvh,var(--ui-vvh, 100dvh));border-radius:20px 20px 0 0;animation:m-up .24s cubic-bezier(.16,1,.3,1)}.m-footer{border-radius:0;flex-direction:column-reverse;align-items:stretch;padding-bottom:max(18px,env(safe-area-inset-bottom))}}@media(pointer:coarse),(max-width:480px){.m-close{position:relative}.m-close:after{content:\"\";position:absolute;inset:-7px}}@keyframes m-fade{0%{opacity:0}to{opacity:1}}@keyframes m-scale{0%{opacity:0;transform:translate(-50%,-48%) scale(.96)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}@keyframes m-up{0%{transform:translateY(100%)}to{transform:translateY(0)}}\n"],
2146
+ styles: [".m-backdrop{position:fixed;inset:0;background:#02081773;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:200;animation:m-fade .18s ease}.m-modal:focus{outline:none}.m-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:16px;border:1px solid rgba(0,0,0,.07);z-index:201;width:calc(100% - 32px);box-shadow:0 4px 6px #0000000a,0 16px 32px #0000001f,0 48px 96px #0000001a;max-height:calc(100vh - 48px);max-height:calc(100dvh - 48px);display:flex;flex-direction:column;animation:m-scale .22s cubic-bezier(.16,1,.3,1)}.m-header{display:flex;align-items:flex-start;justify-content:space-between;padding:20px 24px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0;border-radius:16px 16px 0 0;flex-shrink:0}.m-header-text{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}.m-title{font-size:1.125rem;font-weight:700;color:#0f172a;margin:0;letter-spacing:-.02em;line-height:1.3}.m-description{font-size:.8125rem;color:#64748b;margin:0;line-height:1.4}.m-close{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:none;background:none;cursor:pointer;color:#94a3b8;flex-shrink:0;margin-left:12px;transition:background .13s,color .13s}.m-close:hover{background:#f1f5f9;color:#1e293b}.m-close:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.m-body{padding:20px 24px;overflow-y:auto;flex:1;min-height:0;font-size:.9rem;color:#475569;line-height:1.65}.m-footer{padding:14px 24px 18px;background:#f8fafc;border-top:1px solid #e2e8f0;border-radius:0 0 16px 16px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:8px}.m-footer:not(:has(*)){display:none}.m-footer-left{display:flex;align-items:center;gap:8px}.m-footer-right{display:flex;align-items:center;gap:8px;margin-left:auto}@media(max-width:480px){.m-modal{top:auto;bottom:var(--ui-kb, 0px);left:0;right:0;transform:none;width:100vw;max-width:none!important;max-height:min(85dvh,var(--ui-vvh, 100dvh));border-radius:20px 20px 0 0;animation:m-up .24s cubic-bezier(.16,1,.3,1)}.m-footer{border-radius:0;flex-direction:column-reverse;align-items:stretch;padding-bottom:max(18px,env(safe-area-inset-bottom))}}@media(pointer:coarse),(max-width:480px){.m-close{position:relative}.m-close:after{content:\"\";position:absolute;inset:-7px}}@keyframes m-fade{0%{opacity:0}to{opacity:1}}@keyframes m-scale{0%{opacity:0;transform:translate(-50%,-48%) scale(.96)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}@keyframes m-up{0%{transform:translateY(100%)}to{transform:translateY(0)}}\n"],
2147
2147
  changeDetection: i0.ChangeDetectionStrategy.OnPush
2148
2148
  });
2149
2149
  }
@@ -2187,7 +2187,7 @@ i0.ɵɵngDeclareClassMetadata({
2187
2187
  </div>
2188
2188
  }
2189
2189
  `,
2190
- styles: [".m-backdrop{position:fixed;inset:0;background:#02081773;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:200;animation:m-fade .18s ease}.m-modal:focus{outline:none}.m-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:16px;border:1px solid rgba(0,0,0,.07);z-index:201;width:calc(100% - 32px);box-shadow:0 4px 6px #0000000a,0 16px 32px #0000001f,0 48px 96px #0000001a;max-height:calc(100vh - 48px);display:flex;flex-direction:column;animation:m-scale .22s cubic-bezier(.16,1,.3,1)}.m-header{display:flex;align-items:flex-start;justify-content:space-between;padding:20px 24px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0;border-radius:16px 16px 0 0;flex-shrink:0}.m-header-text{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}.m-title{font-size:1.125rem;font-weight:700;color:#0f172a;margin:0;letter-spacing:-.02em;line-height:1.3}.m-description{font-size:.8125rem;color:#64748b;margin:0;line-height:1.4}.m-close{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:none;background:none;cursor:pointer;color:#94a3b8;flex-shrink:0;margin-left:12px;transition:background .13s,color .13s}.m-close:hover{background:#f1f5f9;color:#1e293b}.m-close:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.m-body{padding:20px 24px;overflow-y:auto;flex:1;min-height:0;font-size:.9rem;color:#475569;line-height:1.65}.m-footer{padding:14px 24px 18px;background:#f8fafc;border-top:1px solid #e2e8f0;border-radius:0 0 16px 16px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:8px}.m-footer:not(:has(*)){display:none}.m-footer-left{display:flex;align-items:center;gap:8px}.m-footer-right{display:flex;align-items:center;gap:8px;margin-left:auto}@media(max-width:480px){.m-modal{top:auto;bottom:var(--ui-kb, 0px);left:0;right:0;transform:none;width:100vw;max-width:none!important;max-height:min(85dvh,var(--ui-vvh, 100dvh));border-radius:20px 20px 0 0;animation:m-up .24s cubic-bezier(.16,1,.3,1)}.m-footer{border-radius:0;flex-direction:column-reverse;align-items:stretch;padding-bottom:max(18px,env(safe-area-inset-bottom))}}@media(pointer:coarse),(max-width:480px){.m-close{position:relative}.m-close:after{content:\"\";position:absolute;inset:-7px}}@keyframes m-fade{0%{opacity:0}to{opacity:1}}@keyframes m-scale{0%{opacity:0;transform:translate(-50%,-48%) scale(.96)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}@keyframes m-up{0%{transform:translateY(100%)}to{transform:translateY(0)}}\n"]
2190
+ styles: [".m-backdrop{position:fixed;inset:0;background:#02081773;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:200;animation:m-fade .18s ease}.m-modal:focus{outline:none}.m-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;border-radius:16px;border:1px solid rgba(0,0,0,.07);z-index:201;width:calc(100% - 32px);box-shadow:0 4px 6px #0000000a,0 16px 32px #0000001f,0 48px 96px #0000001a;max-height:calc(100vh - 48px);max-height:calc(100dvh - 48px);display:flex;flex-direction:column;animation:m-scale .22s cubic-bezier(.16,1,.3,1)}.m-header{display:flex;align-items:flex-start;justify-content:space-between;padding:20px 24px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0;border-radius:16px 16px 0 0;flex-shrink:0}.m-header-text{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}.m-title{font-size:1.125rem;font-weight:700;color:#0f172a;margin:0;letter-spacing:-.02em;line-height:1.3}.m-description{font-size:.8125rem;color:#64748b;margin:0;line-height:1.4}.m-close{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:none;background:none;cursor:pointer;color:#94a3b8;flex-shrink:0;margin-left:12px;transition:background .13s,color .13s}.m-close:hover{background:#f1f5f9;color:#1e293b}.m-close:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.m-body{padding:20px 24px;overflow-y:auto;flex:1;min-height:0;font-size:.9rem;color:#475569;line-height:1.65}.m-footer{padding:14px 24px 18px;background:#f8fafc;border-top:1px solid #e2e8f0;border-radius:0 0 16px 16px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:8px}.m-footer:not(:has(*)){display:none}.m-footer-left{display:flex;align-items:center;gap:8px}.m-footer-right{display:flex;align-items:center;gap:8px;margin-left:auto}@media(max-width:480px){.m-modal{top:auto;bottom:var(--ui-kb, 0px);left:0;right:0;transform:none;width:100vw;max-width:none!important;max-height:min(85dvh,var(--ui-vvh, 100dvh));border-radius:20px 20px 0 0;animation:m-up .24s cubic-bezier(.16,1,.3,1)}.m-footer{border-radius:0;flex-direction:column-reverse;align-items:stretch;padding-bottom:max(18px,env(safe-area-inset-bottom))}}@media(pointer:coarse),(max-width:480px){.m-close{position:relative}.m-close:after{content:\"\";position:absolute;inset:-7px}}@keyframes m-fade{0%{opacity:0}to{opacity:1}}@keyframes m-scale{0%{opacity:0;transform:translate(-50%,-48%) scale(.96)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}@keyframes m-up{0%{transform:translateY(100%)}to{transform:translateY(0)}}\n"]
2191
2191
  }]
2192
2192
  }],
2193
2193
  ctorParameters: () => [],
@@ -10684,6 +10684,137 @@ i0.ɵɵngDeclareClassMetadata({
10684
10684
  }]
10685
10685
  }
10686
10686
  });
10687
+ var AudioPlayerComponent = class AudioPlayerComponent {
10688
+ constructor() {
10689
+ this.src = input("", ...ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []);
10690
+ this.ariaLabel = input("", ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
10691
+ this.preload = input("metadata", ...ngDevMode ? [{ debugName: "preload" }] : /* istanbul ignore next */ []);
10692
+ this.playbackError = output();
10693
+ this.failed = signal(false, ...ngDevMode ? [{ debugName: "failed" }] : /* istanbul ignore next */ []);
10694
+ effect(() => {
10695
+ this.src();
10696
+ this.failed.set(false);
10697
+ });
10698
+ }
10699
+ onError() {
10700
+ if (!this.src()) return;
10701
+ this.failed.set(true);
10702
+ this.playbackError.emit();
10703
+ }
10704
+ static {
10705
+ this.ɵfac = i0.ɵɵngDeclareFactory({
10706
+ minVersion: "12.0.0",
10707
+ version: "22.2.2",
10708
+ ngImport: i0,
10709
+ type: AudioPlayerComponent,
10710
+ deps: [],
10711
+ target: i0.ɵɵFactoryTarget.Component
10712
+ });
10713
+ }
10714
+ static {
10715
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
10716
+ minVersion: "17.0.0",
10717
+ version: "22.2.2",
10718
+ type: AudioPlayerComponent,
10719
+ isStandalone: true,
10720
+ selector: "ui-audio-player",
10721
+ inputs: {
10722
+ src: {
10723
+ classPropertyName: "src",
10724
+ publicName: "src",
10725
+ isSignal: true,
10726
+ isRequired: false,
10727
+ transformFunction: null
10728
+ },
10729
+ ariaLabel: {
10730
+ classPropertyName: "ariaLabel",
10731
+ publicName: "ariaLabel",
10732
+ isSignal: true,
10733
+ isRequired: false,
10734
+ transformFunction: null
10735
+ },
10736
+ preload: {
10737
+ classPropertyName: "preload",
10738
+ publicName: "preload",
10739
+ isSignal: true,
10740
+ isRequired: false,
10741
+ transformFunction: null
10742
+ }
10743
+ },
10744
+ outputs: { playbackError: "playbackError" },
10745
+ ngImport: i0,
10746
+ template: `
10747
+ @if (failed()) {
10748
+ <p class="ap-error" role="alert">Audio could not be played.</p>
10749
+ } @else {
10750
+ <audio class="ap-audio" controls [attr.preload]="preload()" [src]="src() || null" [attr.aria-label]="ariaLabel() || null"
10751
+ (error)="onError()">
10752
+ Your browser does not support the audio element.
10753
+ </audio>
10754
+ }
10755
+ `,
10756
+ isInline: true,
10757
+ styles: [":host{display:block}.ap-audio{display:block;width:100%;height:40px;border-radius:8px;outline:none;accent-color:#3b82f6}.ap-audio:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.ap-error{margin:0;font-size:.8125rem;line-height:1.4;color:#dc2626}\n"],
10758
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
10759
+ });
10760
+ }
10761
+ };
10762
+ i0.ɵɵngDeclareClassMetadata({
10763
+ minVersion: "12.0.0",
10764
+ version: "22.2.2",
10765
+ ngImport: i0,
10766
+ type: AudioPlayerComponent,
10767
+ decorators: [{
10768
+ type: Component,
10769
+ args: [{
10770
+ selector: "ui-audio-player",
10771
+ standalone: true,
10772
+ changeDetection: ChangeDetectionStrategy.OnPush,
10773
+ template: `
10774
+ @if (failed()) {
10775
+ <p class="ap-error" role="alert">Audio could not be played.</p>
10776
+ } @else {
10777
+ <audio class="ap-audio" controls [attr.preload]="preload()" [src]="src() || null" [attr.aria-label]="ariaLabel() || null"
10778
+ (error)="onError()">
10779
+ Your browser does not support the audio element.
10780
+ </audio>
10781
+ }
10782
+ `,
10783
+ styles: [":host{display:block}.ap-audio{display:block;width:100%;height:40px;border-radius:8px;outline:none;accent-color:#3b82f6}.ap-audio:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.ap-error{margin:0;font-size:.8125rem;line-height:1.4;color:#dc2626}\n"]
10784
+ }]
10785
+ }],
10786
+ ctorParameters: () => [],
10787
+ propDecorators: {
10788
+ src: [{
10789
+ type: i0.Input,
10790
+ args: [{
10791
+ isSignal: true,
10792
+ alias: "src",
10793
+ required: false
10794
+ }]
10795
+ }],
10796
+ ariaLabel: [{
10797
+ type: i0.Input,
10798
+ args: [{
10799
+ isSignal: true,
10800
+ alias: "ariaLabel",
10801
+ required: false
10802
+ }]
10803
+ }],
10804
+ preload: [{
10805
+ type: i0.Input,
10806
+ args: [{
10807
+ isSignal: true,
10808
+ alias: "preload",
10809
+ required: false
10810
+ }]
10811
+ }],
10812
+ playbackError: [{
10813
+ type: i0.Output,
10814
+ args: ["playbackError"]
10815
+ }]
10816
+ }
10817
+ });
10687
10818
  const KEY_STEP = 2;
10688
10819
  const KEY_STEP_BIG = 10;
10689
10820
  const clamp = (n) => Math.min(100, Math.max(0, Math.round(n)));
@@ -28038,6 +28169,6 @@ i0.ɵɵngDeclareClassMetadata({
28038
28169
  }]
28039
28170
  }
28040
28171
  });
28041
- export { AlertComponent, AppShellComponent, AvatarComponent, BADGE_VARIANT_STYLES, BadgeComponent, BulkMessagePanelComponent, ButtonComponent, CheckboxComponent, ChipComponent, ChipGroupComponent, ColumnFilterComponent, ComboboxComponent, ConfirmOutletComponent, ConfirmService, ContactPickerComponent, DEFAULT_DOCUMENT_SUBJECT, DOCUMENT_PURPOSE_OPTIONS, DROPDOWN_GROUP, DataTableComponent, DateInputComponent, DateRangePickerComponent, DatetimeInputComponent, DetailCardComponent, DiscountSelectorComponent, DividerComponent, DocumentSendOptionsComponent, DropdownComponent, EmptyStateComponent, ErrorComponent, ErrorStateComponent, FORGEROUTE_CONFIG, FileInputComponent, FormRowComponent, FormSectionComponent, HintComponent, ImagePreviewComponent, ImageRepositionComponent, ImportExportComponent, KpiCardComponent, ListItemComponent, MAX_CC, ModalComponent, MoreMenuComponent, MultiSelectComponent, PaginationComponent, PanelInfoListComponent, PillTabsComponent, ProgressBarComponent, QrCodeComponent, QuickFilterTabsComponent, RICH_TEXT_OPTIONS, RadioComponent, RadioGroupComponent, RadioMatrixComponent, RichTextEditorComponent, SEMANTIC_BADGE_MAP, SearchInputComponent, SelectComponent, SendMessagePanelComponent, SidePanelComponent, SkeletonComponent, TableToolbarComponent, TemplatePickerComponent, TextInputComponent, TextareaInputComponent, TimeInputComponent, ToastComponent, ToastService, ToggleBadgeComponent, TooltipDirective, US_STATES, UiFieldValidity, VideoPickerComponent, appShellGroupsStorageKey, autoVariant, builtInActionIcon, createFieldReveal, decodeFilterValues, encodeFilterValue, encodeFilterValues, errorContextInterceptor, formatIsoDate, formatIsoDateTime, formatTime, getEligibleIds, getEligibleOptions, getErrorMessage, humanReadableMessage, isoKey, issueToErrors, maxLengthIssue, messagePreviewHtml, parseCcList, parseIso, parseTime, pluralize, requiredIssue, resolvePreset, suggestDocumentSubject };
28172
+ export { AlertComponent, AppShellComponent, AudioPlayerComponent, AvatarComponent, BADGE_VARIANT_STYLES, BadgeComponent, BulkMessagePanelComponent, ButtonComponent, CheckboxComponent, ChipComponent, ChipGroupComponent, ColumnFilterComponent, ComboboxComponent, ConfirmOutletComponent, ConfirmService, ContactPickerComponent, DEFAULT_DOCUMENT_SUBJECT, DOCUMENT_PURPOSE_OPTIONS, DROPDOWN_GROUP, DataTableComponent, DateInputComponent, DateRangePickerComponent, DatetimeInputComponent, DetailCardComponent, DiscountSelectorComponent, DividerComponent, DocumentSendOptionsComponent, DropdownComponent, EmptyStateComponent, ErrorComponent, ErrorStateComponent, FORGEROUTE_CONFIG, FileInputComponent, FormRowComponent, FormSectionComponent, HintComponent, ImagePreviewComponent, ImageRepositionComponent, ImportExportComponent, KpiCardComponent, ListItemComponent, MAX_CC, ModalComponent, MoreMenuComponent, MultiSelectComponent, PaginationComponent, PanelInfoListComponent, PillTabsComponent, ProgressBarComponent, QrCodeComponent, QuickFilterTabsComponent, RICH_TEXT_OPTIONS, RadioComponent, RadioGroupComponent, RadioMatrixComponent, RichTextEditorComponent, SEMANTIC_BADGE_MAP, SearchInputComponent, SelectComponent, SendMessagePanelComponent, SidePanelComponent, SkeletonComponent, TableToolbarComponent, TemplatePickerComponent, TextInputComponent, TextareaInputComponent, TimeInputComponent, ToastComponent, ToastService, ToggleBadgeComponent, TooltipDirective, US_STATES, UiFieldValidity, VideoPickerComponent, appShellGroupsStorageKey, autoVariant, builtInActionIcon, createFieldReveal, decodeFilterValues, encodeFilterValue, encodeFilterValues, errorContextInterceptor, formatIsoDate, formatIsoDateTime, formatTime, getEligibleIds, getEligibleOptions, getErrorMessage, humanReadableMessage, isoKey, issueToErrors, maxLengthIssue, messagePreviewHtml, parseCcList, parseIso, parseTime, pluralize, requiredIssue, resolvePreset, suggestDocumentSubject };
28042
28173
 
28043
28174
  //# sourceMappingURL=forgeroute-ui.mjs.map