import * as _angular_core from '@angular/core';
import { OnDestroy, InjectionToken, ElementRef } from '@angular/core';

/**
 * Shape descriptor accepted by SdPreviewImage's `items` signal input.
 *
 * - `string` — CDN/HTTP URL; component will fetch as blob and create a File.
 * - `File` — already-selected file from <input type="file">. Must be `image/*`.
 * - Detailed object — when the caller wants to override name/caption/alt.
 */
type PreviewItem = string | File | {
    url?: string;
    file?: File;
    name?: string;
    caption?: string;
    alt?: string;
    mime?: string;
};
/**
 * Internal normalized record. One per slot in the gallery.
 * `blobUrl` is always set after a successful load so the <img> binding can
 * be uniform; `url` retains the original CDN URL for download fallback.
 */
interface NormalizedImage {
    id: string;
    url?: string;
    blobUrl: string;
    name: string;
    size: number;
    caption?: string;
    alt?: string;
    loading: boolean;
    error: boolean;
}
/**
 * Stage = the visual state of the main image area inside the viewer shell.
 * Driven by an internal `_stage` signal in the component.
 */
type PreviewStage = 'ready' | 'loading' | 'error' | 'empty';
/**
 * Where the thumbnail navigator sits relative to the stage. `none` removes
 * thumbnails entirely (single-image lightbox); `dots` swaps the strip for
 * compact dot indicators (≤ 8 images).
 */
type ThumbnailPosition = 'bottom' | 'right' | 'left' | 'top' | 'dots' | 'none';
/**
 * Color scheme for the preview shell. `'dark'` (default) keeps the original
 * dark-theme tokens (black surfaces, white text). `'light'` flips the token
 * set to white surfaces / dark text — picks up `--sd-primary`, `--sd-error`,
 * `--sd-success` from the consumer theme when present.
 *
 * Exposed via `[attr.data-theme]` on the host so SCSS can swap CSS custom
 * properties via the `:host([data-theme="light"])` selector — no runtime
 * style switching is required.
 */
type PreviewTheme = 'dark' | 'light';

declare class SdPreviewImage implements OnDestroy {
    #private;
    static readonly MIN_ZOOM = 0.25;
    static readonly MAX_ZOOM = 4;
    static readonly ZOOM_STEP = 0.1;
    static readonly SWIPE_THRESHOLD = 40;
    readonly autoIdInput: _angular_core.InputSignal<string | null | undefined>;
    readonly autoId: _angular_core.Signal<string | undefined>;
    readonly autoIdPrev: _angular_core.Signal<string | undefined>;
    readonly autoIdNext: _angular_core.Signal<string | undefined>;
    readonly autoIdZoomIn: _angular_core.Signal<string | undefined>;
    readonly autoIdZoomOut: _angular_core.Signal<string | undefined>;
    readonly autoIdFit: _angular_core.Signal<string | undefined>;
    readonly autoIdRotate: _angular_core.Signal<string | undefined>;
    readonly autoIdDownload: _angular_core.Signal<string | undefined>;
    readonly autoIdFullscreen: _angular_core.Signal<string | undefined>;
    readonly autoIdRetry: _angular_core.Signal<string | undefined>;
    readonly autoIdThumb: (i: number) => string | undefined;
    readonly autoIdDot: (i: number) => string | undefined;
    readonly items: _angular_core.InputSignal<PreviewItem[]>;
    readonly title: _angular_core.InputSignal<string | undefined>;
    readonly thumbnailPosition: _angular_core.InputSignal<ThumbnailPosition>;
    readonly showToolbar: _angular_core.InputSignal<boolean>;
    readonly downloadable: _angular_core.InputSignal<boolean>;
    readonly zoomable: _angular_core.InputSignal<boolean>;
    readonly loop: _angular_core.InputSignal<boolean>;
    readonly startIndex: _angular_core.InputSignal<number>;
    readonly theme: _angular_core.InputSignal<PreviewTheme>;
    readonly close: _angular_core.OutputEmitterRef<void>;
    readonly activeIndexChange: _angular_core.OutputEmitterRef<number>;
    readonly download: _angular_core.OutputEmitterRef<{
        index: number;
        item: NormalizedImage;
    }>;
    readonly imageError: _angular_core.OutputEmitterRef<{
        index: number;
        reason: string;
    }>;
    readonly activeIndex: _angular_core.Signal<number>;
    readonly images: _angular_core.Signal<NormalizedImage[]>;
    readonly zoom: _angular_core.Signal<number>;
    readonly rotation: _angular_core.Signal<number>;
    readonly pan: _angular_core.Signal<{
        x: number;
        y: number;
    }>;
    readonly stage: _angular_core.Signal<PreviewStage>;
    readonly isFullscreen: _angular_core.Signal<boolean>;
    readonly activeImage: _angular_core.Signal<NormalizedImage | undefined>;
    readonly hasPrev: _angular_core.Signal<boolean>;
    readonly hasNext: _angular_core.Signal<boolean>;
    readonly zoomPercent: _angular_core.Signal<number>;
    readonly imageTransform: _angular_core.Signal<string>;
    readonly shellClass: _angular_core.Signal<{
        [x: string]: boolean;
        'sd-preview-shell': boolean;
        'sd-preview-shell--fullscreen': boolean;
    }>;
    constructor();
    ngOnDestroy(): void;
    onClickThumbnailImage(index: number): void;
    updateCurrentImage(direction: 1 | -1): void;
    zoomIn(): void;
    zoomOut(): void;
    fitToScreen(): void;
    rotate(direction: 'left' | 'right'): void;
    downloadCurrent(): void;
    toggleFullscreen(): void;
    /** Retry just the active image (Artboard G action). */
    retryActive(): Promise<void>;
    /** User clicked the X button — emit close intent for the consumer to react. */
    requestClose(): void;
    onKeyDown(event: KeyboardEvent): void;
    onWheel(event: WheelEvent): void;
    onPointerDown(event: PointerEvent): void;
    onPointerMove(event: PointerEvent): void;
    onPointerUp(event: PointerEvent): void;
    onPointerCancel(event: PointerEvent): void;
    onImageDblClick(): void;
    onImageError(): void;
    trackByImage(_i: number, item: NormalizedImage): string;
    formatBytes(bytes: number): string;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdPreviewImage, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdPreviewImage, "sd-preview-image", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "thumbnailPosition": { "alias": "thumbnailPosition"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "downloadable": { "alias": "downloadable"; "required": false; "isSignal": true; }; "zoomable": { "alias": "zoomable"; "required": false; "isSignal": true; }; "loop": { "alias": "loop"; "required": false; "isSignal": true; }; "startIndex": { "alias": "startIndex"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; }, { "close": "close"; "activeIndexChange": "activeIndexChange"; "download": "download"; "imageError": "imageError"; }, never, never, true, never>;
}

/**
 * Source descriptor accepted by SdPreviewPdf's `source` signal input.
 *
 * Mirrors the input shapes that `pdfjsLib.getDocument()` understands while
 * adding consumer-friendly variants (raw `File`/`Blob`). The component handles
 * the bridging internally — caller never touches pdfjs types.
 */
type PdfSource = string | File | Blob | ArrayBuffer | Uint8Array | {
    url: string;
    httpHeaders?: Record<string, string>;
    withCredentials?: boolean;
} | {
    data: ArrayBuffer | Uint8Array;
};
/**
 * Zoom expressed either as a raw scale number (1 = 100%) OR a symbolic mode
 * that auto-fits the stage. The auto modes recompute on container resize +
 * page change.
 */
type PdfZoomMode = number | 'page-fit' | 'page-width' | 'page-actual';
/**
 * Which tab the sidebar shows. `'none'` collapses the sidebar entirely.
 * NOTE: outline + search tabs are deferred — selecting them shows a placeholder.
 */
type PdfSidebarMode = 'thumbnails' | 'outline' | 'search' | 'none';
/**
 * Page layout mode. `'continuous'` is deferred — the input is kept on the
 * public API so consumer code compiles, but the component falls back to
 * single-page behavior with a warning.
 */
type PdfScrollMode = 'page' | 'continuous';
/**
 * Document-level metadata extracted from `pdfDoc.getMetadata()`.
 */
interface PdfMeta {
    title?: string;
    author?: string;
    subject?: string;
    numPages: number;
}
/** Visual state of the stage. */
type PdfStage = 'empty' | 'loading' | 'ready' | 'error';
/** Classifier for `loadError` output — drives which Artboard H copy renders. */
type PdfErrorReason = 'invalid' | 'password' | 'network' | 'unknown';
/** Fired once after `getDocument()` resolves successfully. */
interface PdfLoadEvent {
    totalPages: number;
    meta: PdfMeta;
}
/** Fired whenever a load attempt rejects. */
interface PdfErrorEvent {
    reason: PdfErrorReason;
    message?: string;
}
/**
 * One hit returned by `SdPreviewPdf.search()`. `before` / `term` / `after`
 * are sliced from the original page text so the result list can render a
 * snippet with the matched substring wrapped in `<mark>` while preserving the
 * surrounding context (~30 chars each side).
 *
 * `textItemIndices` is reserved for the in-page highlight pass — it carries
 * the indices of the pdfjs text items the match spans across so the renderer
 * can wrap the right spans without re-running the search per page.
 */
interface PdfSearchResult {
    page: number;
    before: string;
    term: string;
    after: string;
    textItemIndices?: number[];
}

/**
 * Lightweight projection of the internal search state for the
 * `searchChange` output + the result-list rendering. `activeIndex` is `-1`
 * when no result is currently focused (e.g. immediately after a `clearSearch`).
 */
interface PdfSearchState {
    term: string;
    caseSensitive: boolean;
    wholeWord: boolean;
    results: PdfSearchResult[];
    activeIndex: number;
}

/**
 * Minimal pdfjs surface the component depends on. We wrap pdfjs in a DI token
 * so unit tests can supply a hand-rolled mock without going through
 * jasmine.createSpyObj on the real module (which is harder with ESM).
 */
interface SdPdfJsLib {
    getDocument: (spec: unknown) => {
        promise: Promise<unknown>;
        onProgress?: (p: {
            loaded: number;
            total: number;
        }) => void;
    };
    GlobalWorkerOptions: {
        workerSrc: string;
    };
}
declare const SD_PDFJS_LIB: InjectionToken<SdPdfJsLib>;
declare class SdPreviewPdf implements OnDestroy {
    #private;
    static readonly MIN_ZOOM = 0.25;
    static readonly MAX_ZOOM = 4;
    static readonly ZOOM_STEP = 0.1;
    protected readonly pageCanvasRef: _angular_core.Signal<ElementRef<HTMLCanvasElement> | undefined>;
    protected readonly stageEl: _angular_core.Signal<ElementRef<HTMLElement> | undefined>;
    protected readonly pageInputRef: _angular_core.Signal<ElementRef<HTMLInputElement> | undefined>;
    protected readonly searchInputRef: _angular_core.Signal<ElementRef<HTMLInputElement> | undefined>;
    protected readonly thumbCanvases: _angular_core.Signal<readonly ElementRef<HTMLCanvasElement>[]>;
    readonly autoIdInput: _angular_core.InputSignal<string | null | undefined>;
    readonly autoId: _angular_core.Signal<string | undefined>;
    readonly autoIdFirst: _angular_core.Signal<string | undefined>;
    readonly autoIdPrev: _angular_core.Signal<string | undefined>;
    readonly autoIdNext: _angular_core.Signal<string | undefined>;
    readonly autoIdLast: _angular_core.Signal<string | undefined>;
    readonly autoIdPageInput: _angular_core.Signal<string | undefined>;
    readonly autoIdZoomIn: _angular_core.Signal<string | undefined>;
    readonly autoIdZoomOut: _angular_core.Signal<string | undefined>;
    readonly autoIdZoomReadout: _angular_core.Signal<string | undefined>;
    readonly autoIdFitPage: _angular_core.Signal<string | undefined>;
    readonly autoIdFitWidth: _angular_core.Signal<string | undefined>;
    readonly autoIdRotate: _angular_core.Signal<string | undefined>;
    readonly autoIdScrollMode: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchToggle: _angular_core.Signal<string | undefined>;
    readonly autoIdPrint: _angular_core.Signal<string | undefined>;
    readonly autoIdDownload: _angular_core.Signal<string | undefined>;
    readonly autoIdFullscreen: _angular_core.Signal<string | undefined>;
    readonly autoIdSidebarToggle: _angular_core.Signal<string | undefined>;
    readonly autoIdTabThumbnails: _angular_core.Signal<string | undefined>;
    readonly autoIdTabOutline: _angular_core.Signal<string | undefined>;
    readonly autoIdTabSearch: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchInput: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchNext: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchPrev: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchCase: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchWhole: _angular_core.Signal<string | undefined>;
    readonly autoIdSearchClose: _angular_core.Signal<string | undefined>;
    readonly autoIdRetry: _angular_core.Signal<string | undefined>;
    readonly autoIdThumb: (i: number) => string | undefined;
    readonly autoIdResult: (i: number) => string | undefined;
    readonly source: _angular_core.InputSignal<PdfSource | null>;
    readonly title: _angular_core.InputSignal<string | undefined>;
    readonly startPage: _angular_core.InputSignal<number>;
    readonly initialZoom: _angular_core.InputSignal<PdfZoomMode>;
    readonly sidebar: _angular_core.InputSignal<PdfSidebarMode>;
    readonly sidebarOpen: _angular_core.InputSignal<boolean>;
    readonly scrollMode: _angular_core.InputSignal<PdfScrollMode>;
    readonly showToolbar: _angular_core.InputSignal<boolean>;
    readonly downloadable: _angular_core.InputSignal<boolean>;
    readonly password: _angular_core.InputSignal<string | undefined>;
    readonly httpHeaders: _angular_core.InputSignal<Record<string, string> | undefined>;
    readonly theme: _angular_core.InputSignal<PreviewTheme>;
    readonly close: _angular_core.OutputEmitterRef<void>;
    readonly loaded: _angular_core.OutputEmitterRef<PdfLoadEvent>;
    readonly pageChange: _angular_core.OutputEmitterRef<number>;
    readonly zoomChange: _angular_core.OutputEmitterRef<number>;
    readonly download: _angular_core.OutputEmitterRef<{
        filename: string;
    }>;
    readonly loadError: _angular_core.OutputEmitterRef<PdfErrorEvent>;
    readonly searchChange: _angular_core.OutputEmitterRef<{
        term: string;
        total: number;
        current: number;
    }>;
    readonly stage: _angular_core.Signal<PdfStage>;
    readonly activePage: _angular_core.Signal<number>;
    readonly zoom: _angular_core.Signal<number>;
    readonly zoomMode: _angular_core.Signal<PdfZoomMode>;
    readonly rotation: _angular_core.Signal<number>;
    readonly sidebarMode: _angular_core.Signal<PdfSidebarMode>;
    readonly scrollModeCurrent: _angular_core.Signal<PdfScrollMode>;
    readonly isFullscreen: _angular_core.Signal<boolean>;
    readonly meta: _angular_core.Signal<PdfMeta | null>;
    readonly filename: _angular_core.Signal<string>;
    readonly fileSize: _angular_core.Signal<number>;
    readonly loadError$: _angular_core.Signal<PdfErrorEvent | null>;
    readonly thumbCache: _angular_core.Signal<Record<number, string>>;
    readonly numPages: _angular_core.Signal<number>;
    readonly canPrev: _angular_core.Signal<boolean>;
    readonly canNext: _angular_core.Signal<boolean>;
    readonly zoomPercent: _angular_core.Signal<number>;
    readonly loadPercent: _angular_core.Signal<number>;
    readonly isSidebarOpen: _angular_core.Signal<boolean>;
    readonly pageList: _angular_core.Signal<number[]>;
    readonly pageNumbers: _angular_core.Signal<number[]>;
    readonly searchBarOpen: _angular_core.Signal<boolean>;
    readonly searchTerm: _angular_core.Signal<string>;
    readonly searchResults: _angular_core.Signal<PdfSearchResult[]>;
    readonly searchActiveIndex: _angular_core.Signal<number>;
    readonly searchCaseSensitive: _angular_core.Signal<boolean>;
    readonly searchWholeWord: _angular_core.Signal<boolean>;
    readonly searchTotal: _angular_core.Signal<number>;
    readonly searchCurrent: _angular_core.Signal<number>;
    readonly searchActiveResult: _angular_core.Signal<PdfSearchResult | null>;
    readonly searchState: _angular_core.Signal<PdfSearchState>;
    constructor();
    ngOnDestroy(): void;
    goToPage(page: number): void;
    nextPage(): void;
    prevPage(): void;
    firstPage(): void;
    lastPage(): void;
    zoomIn(): void;
    zoomOut(): void;
    setZoom(mode: PdfZoomMode): void;
    rotate(direction: 'left' | 'right'): void;
    toggleSidebar(): void;
    setSidebarMode(mode: PdfSidebarMode): void;
    setScrollMode(mode: PdfScrollMode): void;
    downloadFile(): void;
    toggleFullscreen(): void;
    /** Programmatic equivalent of clicking the X — emits the close output. */
    requestClose(): void;
    /** Retry the active load attempt (called from the error state retry button). */
    retryLoad(): void;
    printFile(): void;
    /**
     * Run a full-document search for `term`. Returns the result count.
     *
     * Options:
     * - `caseSensitive`: default off; matches independent of capitalisation.
     * - `wholeWord`: default off; requires Unicode word-boundary on both sides
     *   so `cat` does NOT match `category` (uses `/\b...\b/u` so Vietnamese
     *   diacritic-bearing letters still participate as word characters).
     *
     * Implementation: iterate every page once (cached), build a flat plain-text
     * string per page (joining textItem.str), and slice ~30 chars of context
     * around each match. We deliberately use plain JS regex rather than
     * pdf.js's `pdfFindController` — it's <40 lines, fully testable, and
     * sidesteps the find-controller's dependency on a real text layer (which
     * we don't render).
     */
    search(term: string, options?: {
        caseSensitive?: boolean;
        wholeWord?: boolean;
    }): Promise<number>;
    searchNext(): void;
    searchPrev(): void;
    clearSearch(): void;
    /** Open the search bar and focus its input. Idempotent. */
    openSearch(): void;
    /** Close the search bar AND clear results (mirrors Acrobat behaviour). */
    closeSearch(): void;
    toggleSearchCaseSensitive(): void;
    toggleSearchWholeWord(): void;
    /** Live binding from the search bar's `(input)` event. */
    onSearchInput(value: string): void;
    onKeyDown(event: KeyboardEvent): void;
    onPageInputEnter(event: Event): void;
    onPageInputBlur(event: Event): void;
    onWheel(event: WheelEvent): void;
    trackByPage(_i: number, page: number): number;
    /**
     * Render a single page into a sidebar thumbnail canvas at ~140px width.
     * Cached via `#thumbsRendering` so rapid scroll bounces don't double-render.
     *
     * Visible for testing — tests can stub `getPage()` on the fake doc to
     * assert this method calls `render(...)` with the right page argument.
     */
    renderThumbnailForPage(pageNum: number): Promise<void>;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdPreviewPdf, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdPreviewPdf, "sd-preview-pdf", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "source": { "alias": "source"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "startPage": { "alias": "startPage"; "required": false; "isSignal": true; }; "initialZoom": { "alias": "initialZoom"; "required": false; "isSignal": true; }; "sidebar": { "alias": "sidebar"; "required": false; "isSignal": true; }; "sidebarOpen": { "alias": "sidebarOpen"; "required": false; "isSignal": true; }; "scrollMode": { "alias": "scrollMode"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "downloadable": { "alias": "downloadable"; "required": false; "isSignal": true; }; "password": { "alias": "password"; "required": false; "isSignal": true; }; "httpHeaders": { "alias": "httpHeaders"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; }, { "close": "close"; "loaded": "loaded"; "pageChange": "pageChange"; "zoomChange": "zoomChange"; "download": "download"; "loadError": "loadError"; "searchChange": "searchChange"; }, never, never, true, never>;
}

export { SD_PDFJS_LIB, SdPreviewImage, SdPreviewPdf };
export type { NormalizedImage, PdfErrorEvent, PdfErrorReason, PdfLoadEvent, PdfMeta, PdfScrollMode, PdfSearchResult, PdfSearchState, PdfSidebarMode, PdfSource, PdfStage, PdfZoomMode, PreviewItem, PreviewStage, PreviewTheme, SdPdfJsLib, ThumbnailPosition };
