import { type Ref, type ComputedRef } from '#imports'; import type { ShallowRef } from 'vue'; import type { StorageProvider } from './storage.js'; import type { GlobalUiDialog, SidebarRegion } from '#blokkli/editor/types/ui'; import { type BlokkliClipboardPayload } from '#blokkli/editor/types/clipboard'; import type { Viewport } from '../../../global/constants/index.js'; import type { AdapterContext } from '#blokkli/editor/adapter'; import type { ThemeColorName } from './../../../global/types/theme.js'; import type { ElementProvider } from './element.js'; import { type BlokkliEventBus } from '../events/index.js'; import type { Coord, Rectangle, Size } from '../types/geometry.js'; import type { TextProvider } from './texts.js'; export type UiProvider = { /** * Get the Nuxt root element (#nuxt-root). * * Cached after first access for performance. * * @returns The root HTML element * @throws Error if element cannot be found */ rootElement: () => HTMLElement; /** * Get the artboard element (.bk-main-canvas). * * The artboard is the scrollable container for the edited content. * Cached after first access for performance. * * @returns The artboard HTML element * @throws Error if element cannot be found */ artboardElement: () => HTMLElement; /** * The blökkli provider root element. * * This is the element that contains the entire editor UI. */ providerElement: HTMLElement; /** * Whether the viewport is mobile (< 1024px width). */ isMobile: ComputedRef; /** * Whether the viewport is desktop (>= 1024px width). */ isDesktop: ComputedRef; /** * Whether animations are currently running. * * When true, adds 'bk-is-animating' class to document root. * Used to disable certain interactions during animations. */ isAnimating: Ref; /** * Whether the analyzer is currently active. * * When true, adds 'bk-is-analyzing' class to document root. */ isAnalyzing: Ref; /** * Whether proxy mode is active. * * Proxy mode shows a structured view of blocks instead of their components. */ isProxyMode: Ref; /** * Whether any dialog is currently open. */ hasDialogOpen: ComputedRef; /** * Whether a nested editor overlay is currently open. */ hasNestedEditorOpen: ComputedRef; /** * Set the currently open nested editor. */ setNestedEditor: (key: string | null) => void; /** * The currently open dialog, or null if none is open. */ currentDialog: Readonly>; /** * Require confirmation before closing the current dialog. * * Sets the confirmClose flag on the current dialog, which will * require double clicking the dialog background overlay to close it. */ requireDialogCloseConfirm: () => void; /** * Open a global dialog. * * Closes any previously open dialog and opens the new one. * * @param dialog - The dialog configuration to open */ openDialog: (dialog: GlobalUiDialog) => void; /** * Close a dialog. * * @param id - Optional dialog ID. If provided, only closes if it matches the current dialog. */ closeDialog: (id?: string) => void; /** * Whether any tooltip is currently open. */ hasTooltipOpen: ComputedRef; /** * ID of the currently open tooltip, or empty string if none is open. */ openTooltip: Ref; /** * The current selection color. * * Returns the most recently set color, or null if none is set. * Used to colorize the selection UI (selection rect, drop target indicators, etc.). */ selectionColor: ComputedRef; /** * Set a selection color. * * Multiple features can set colors with different IDs. * The most recently set color is used. * * @param id - Unique identifier for this color source * @param color - The theme color to use */ setSelectionColor: (id: string, color: ThemeColorName) => void; /** * Remove a selection color by ID. * * If this was the active color, the previous color will become active. * * @param id - The color source ID to remove */ removeSelectionColor: (id: string) => void; /** * Whether the transform overlay is open. * * The transform overlay is shown during drag operations, resizing, etc. */ hasTransformOverlayOpen: Ref; /** * Whether a transform operation is active. * * True when transformLabel is not empty. */ isTransforming: ComputedRef; /** * Set the active transform operation. * * @param label - Label describing the transform (e.g., "Moving 3 blocks"), or null/undefined to clear */ setTransform: (label?: string | null | undefined) => void; /** * The current transform operation label. */ transformLabel: ComputedRef; /** * Whether animations are enabled. * * Can be disabled in settings for performance or accessibility. * Defaults to true. */ useAnimations: ComputedRef; /** * Whether low performance mode is enabled. * * When enabled, reduces visual effects and animations for better performance * on slower devices. */ lowPerformanceMode: ComputedRef; /** * The visible viewport rectangle. * * Represents the portion of the window where the editor is visible, * excluding any overlays or sidebars. */ visibleViewport: ComputedRef; /** * The visible viewport rectangle with padding applied. * * Used for positioning elements that should be inset from the viewport edges. */ visibleViewportPadded: ComputedRef; /** * Register a rectangle that blocks part of the viewport. * * Blocking rectangles are used for persistent UI elements (toolbars, sidebars) * that should affect element positioning and visibility calculations. * * @param key - Unique identifier for this blocking rectangle * @param rect - The rectangle, or undefined to remove the blocking rectangle */ setViewportBlockingRectangle: (key: string, rect?: Rectangle) => void; /** * All viewport blocking rectangles with padding applied. * * Each rectangle is expanded by blockingPaddingX and blockingPaddingY * to create a buffer zone around blocking UI elements. */ viewportBlockingRects: ComputedRef; /** * The current viewport type. * * 'mobile' for viewports < 1024px width, 'desktop' otherwise. * Used to adjust UI layout and behavior. */ appViewport: ComputedRef; /** * ID of the currently open context menu, or empty string if none is open. */ openContextMenu: Ref; /** * The browser window viewport size. * * Updated on window resize with 400ms debounce. */ viewport: ComputedRef; /** * The artboard element size. * * Tracked via ResizeObserver for accurate, efficient updates. */ artboardSize: ComputedRef; /** * The artboard zoom/scale factor. * * 1.0 = 100%, 0.5 = 50%, 2.0 = 200%, etc. * Used by artboard zoom feature. */ artboardScale: Ref; /** * The artboard scroll/pan offset in pixels. * * Represents how far the artboard has been scrolled or panned. */ artboardOffset: Ref; /** * Top-left coordinate of the selection rectangle. * * Updated during drag operations and multi-select. */ selectionTopLeft: Ref; /** * The interface language code. * * Respects forceDefaultLanguage config setting. * Falls back to context language otherwise. */ interfaceLanguage: ComputedRef; /** * The full locale string for date/number formatting. * * Maps language codes to locale strings (e.g., 'de' -> 'de-CH'). */ locale: ComputedRef; /** * Format a date using the current locale. * * @param date - Date object or ISO string * @param options - Intl.DateTimeFormat options. Defaults to numeric date + time. * @returns Localized date string * * @example * ```ts * formatDate(new Date()) // "16.11.2025, 14:30" * formatDate(isoString, { dateStyle: 'long' }) // "16. November 2025" * ``` */ formatDate: (date: string | Date, options?: Intl.DateTimeFormatOptions) => string; /** * Format a number using the current locale. * * @param value - The number to format * @param options - Intl.NumberFormat options * @returns Localized number string * * @example * ```ts * formatNumber(12345) // "12'345" (de-CH) or "12,345" (en) * formatNumber(0.5, { style: 'percent' }) // "50%" * ``` */ formatNumber: (value: number, options?: Intl.NumberFormatOptions) => string; /** * Format a price in USD using the current locale. * * @param value - The price value * @returns Localized currency string * * @example * ```ts * formatPrice(12.5) // "$12.50" (en) or "12.50 $" (de-CH) * formatPrice(0.0042) // "$0.0042" * ``` */ formatPrice: (value: number) => string; /** * Get absolute rectangle for an element or rectangle. * * Converts viewport-relative coordinates to artboard-absolute coordinates * by accounting for artboard scale and offset. Ensures minimum size of 24x24. * * @param v - HTML element or rectangle * @param scale - Override artboard scale (uses current scale if not provided) * @param offset - Override artboard offset (uses current offset if not provided) * @returns Absolute rectangle in artboard coordinates */ getAbsoluteElementRect: (v: HTMLElement | Rectangle, scale?: number, offset?: Coord) => Rectangle; /** * Convert viewport-relative coordinates to artboard-relative coordinates. * * @param coords - The coordinates to convert. * @param scale - Override artboard scale (uses current scale if not provided) * @param offset - Override artboard offset (uses current offset if not provided) * @returns Coordinates in artboard coordinate space */ toArtboardCoords: (coords: Coord, scale?: number, offset?: Coord) => Coord; /** * Convert artboard-absolute rectangle to viewport-relative coordinates. * * Applies artboard scale and offset to convert from artboard space * to viewport/screen space. * * @param rect - Rectangle in artboard coordinates * @param scale - Override artboard scale (uses current scale if not provided) * @param offset - Override artboard offset (uses current offset if not provided) * @returns Rectangle in viewport coordinates */ getViewportRelativeRect: (rect: Rectangle, scale?: number, offset?: Coord) => Rectangle; /** * Set the height of a banner by ID. * * Banners are persistent notification bars at the top of the interface. * Height is used to adjust viewport calculations. * * @param id - Unique banner identifier * @param height - Banner height in pixels */ setBannerHeight: (id: string, height: number) => void; /** * Remove a banner by ID. * * Sets the banner height to 0. * * @param id - The banner identifier to remove */ removeBanner: (id: string) => void; /** * Register an active sidebar. * * Adds the sidebar ID to the active list for the specified region. * When sidebars are active, the interface adjusts layout accordingly. * * @param region - Which sidebar region ('left' or 'right') * @param id - Unique sidebar identifier */ setActiveSidebar: (region: SidebarRegion, id: string) => void; /** * Unregister an active sidebar. * * Removes the sidebar ID from the active list for the specified region. * * @param region - Which sidebar region ('left' or 'right') * @param id - The sidebar identifier to remove */ removeActiveSidebar: (region: SidebarRegion, id: string) => void; /** * Whether any sidebar is active on the left. * * When true, adds 'bk-has-sidebar-left' class to document root. */ hasSidebarLeft: ComputedRef; /** * Whether any sidebar is active on the right. * * When true, adds 'bk-has-sidebar-right' class to document root. */ hasSidebarRight: ComputedRef; /** * Whether any sidebar is active in the right-bottom region. * * When true, adds 'bk-has-sidebar-right-bottom' class to document root. */ hasSidebarRightBottom: ComputedRef; /** * Reference to the main layout element. * * The container element for the primary editor interface. */ mainLayoutElement: Readonly>; /** * Whether the canvas is currently focused. */ canvasFocused: Readonly>; setCanvasFocused: (isFocused: boolean) => void; /** * Whether the approval toolbar is currently visible. * * Set by the text field approval toolbar on mount/unmount. */ isApproving: Readonly>; setIsApproving: (value: boolean) => void; /** * Whether the actions toolbar position is locked. * * When true, the toolbar stays in place (e.g. during option changes * or after clicking "Select parent"). Unlocked after a mouseleave * timeout on the Actions component. */ actionsToolbarLocked: Ref; /** * Whether the user is currently changing block options. * * When true, canvas renderers fade out (selection opacity, hover reset, * analyze disable). This is separate from actionsToolbarLocked which * only locks toolbar position. */ isChangingOptions: Ref; /** * Register a handler that persists pending, not-yet-saved changes. * * Some features buffer changes locally while the user is interacting (e.g. * the options form defers persisting option changes while the user tries * them out). Such a feature registers a flush handler here so the rest of * the editor can trigger and await that persistence via * {@link flushPendingChanges}. * * Each handler is responsible for being safe to call when nothing is pending * (resolve immediately) and for de-duping concurrent calls. * * @returns A function that unregisters the handler. */ registerFlushHandler: (handler: () => Promise) => () => void; /** * Persist any pending changes from all registered flush handlers and resolve * once they are done. * * Action buttons should await this before they run, so that an action which * depends on persisted state (e.g. opening an edit form built from server * state, or publishing) sees the latest values. Resolves immediately when no * handlers are registered. */ flushPendingChanges: () => Promise; /** * ID of the currently active (focused) highlight, or empty string. * * Shared between the analyze sidebar and the highlights canvas renderer * so both can show a visual "active" state for the same highlight. */ activeHighlightId: Ref; /** * Whether the item actions are open. */ itemActionsOpen: Ref; copyTextToClipboard: (text: string | number) => void; /** * Write a typed blökkli clipboard payload to the system clipboard. * * Wraps the payload in a marker envelope so paste detection never * confuses arbitrary user-pasted JSON with a blökkli payload. The * `BlokkliClipboardPayload` discriminated union is augmentable by * features (`#blokkli/editor/types/clipboard`), so each call site is * type-checked against the registered clipboard types. * * Throws on `navigator.clipboard.writeText` failure so callers can * surface a domain-specific error message. */ setClipboardData: (payload: BlokkliClipboardPayload) => Promise; }; export default function (eventBus: BlokkliEventBus, providerElement: HTMLElement, storage: StorageProvider, context: ComputedRef, element: ElementProvider, mainLayoutElement: Readonly>, visibleViewportElement: Readonly>, $t: TextProvider): UiProvider;