import React from "react"; import { AppEventBus, type EXPORT_IMAGE_TYPES, Emitter, type EditorInterface, type StylesPanelMode } from "@excalidraw/common"; import { LinearElementEditor, Scene, Store, type ElementUpdate, StoreDelta, type ApplyToOptions } from "@excalidraw/element"; import type { LocalPoint, Radians } from "@excalidraw/math"; import type { ExcalidrawElement, ExcalidrawTextElement, NonDeleted, ExcalidrawImageElement, FileId, NonDeletedExcalidrawElement, ExcalidrawTextContainer, ExcalidrawFrameLikeElement, ExcalidrawEmbeddableElement, Ordered, SceneElementsMap } from "@excalidraw/element/types"; import type { Mutable } from "@excalidraw/common/utility-types"; import { ActionManager } from "../actions/manager"; import { History } from "../history"; import { Fonts } from "../fonts"; import { type WritableAtom } from "../editor-jotai"; import { Renderer } from "../scene/Renderer"; import { type SetViewportOptions } from "../viewport"; import { LaserTrails } from "../laserTrails"; import { isOverScrollBars } from "../scene/scrollbars"; import { LassoTrail } from "../lasso"; import { EraserTrail } from "../eraser"; import { AppArrowText } from "./App.arrowText"; import { AppBucketFill } from "./App.bucketFill"; import { AppCursor } from "./App.cursor"; import { AppDrawShape } from "./App.drawshape"; import { AppFlowchart } from "./App.flowchart"; import { AppViewport } from "./App.viewport"; import { CursorHints } from "./CursorHint"; import { type OnStateChange } from "./AppStateObserver"; import type { ExportedElements } from "../data"; import type { AppClassProperties, AppProps, AppState, ExcalidrawImperativeAPI, BinaryFiles, SceneData, FrameNameBoundsCache, SidebarName, SidebarTabName, ToolType, OnUserFollowedPayload, ExcalidrawImperativeAPIEventMap, NullableGridSize, UIConfig, ImageStatus } from "../types"; import type { RoughCanvas } from "roughjs/bin/canvas"; import type { ActionResult } from "../actions/types"; declare const editorLifecycleEventBehavior: { readonly "editor:mount": { readonly cardinality: "once"; readonly replay: "last"; }; readonly "editor:initialize": { readonly cardinality: "once"; readonly replay: "last"; }; readonly "editor:unmount": { readonly cardinality: "once"; readonly replay: "last"; }; }; export declare const ExcalidrawContainerContext: React.Context<{ container: HTMLDivElement | null; id: string | null; }>; export declare const ExcalidrawAPIContext: React.Context; export declare const ExcalidrawAPISetContext: React.Context<((api: ExcalidrawImperativeAPI | null) => void) | null>; export declare const useApp: () => AppClassProperties; export declare const useAppProps: () => AppProps; export declare const useEditorInterface: () => Readonly<{ formFactor: "phone" | "tablet" | "desktop"; desktopUIMode: "compact" | "full"; userAgent: Readonly<{ isMobileDevice: boolean; platform: "ios" | "android" | "other" | "unknown"; }>; isTouchScreen: boolean; canFitSidebar: boolean; isLandscape: boolean; }>; export declare const useStylesPanelMode: () => StylesPanelMode; export declare const useExcalidrawContainer: () => { container: HTMLDivElement | null; id: string | null; }; export declare const useExcalidrawElements: () => readonly NonDeletedExcalidrawElement[]; export declare const useExcalidrawAppState: () => AppState; export declare const useExcalidrawSetAppState: () => (state: AppState | ((prevState: Readonly, props: Readonly) => AppState | Pick | null) | Pick | null, callback?: (() => void) | undefined) => void; export declare const useExcalidrawActionManager: () => ActionManager; /** * Requires wrapping your component in */ export declare const useExcalidrawAPI: () => ExcalidrawImperativeAPI | null; declare class App extends React.Component { canvas: AppClassProperties["canvas"]; interactiveCanvas: AppClassProperties["interactiveCanvas"]; sessionExportThemeOverride: AppState["theme"] | undefined; rc: RoughCanvas; unmounted: boolean; actionManager: ActionManager; editorInterface: EditorInterface; private stylesPanelMode; private excalidrawContainerRef; scene: Scene; fonts: Fonts; renderer: Renderer; visibleElements: readonly NonDeletedExcalidrawElement[]; /** whether the last render had any renderable elements (excludes e.g. the * in-progress `newElement` and the edited text element) */ private hasRenderableElements; private resizeObserver; id: string; private store; private history; excalidrawContainerValue: { container: HTMLDivElement | null; id: string; }; files: BinaryFiles; imageCache: AppClassProperties["imageCache"]; imageLoadingProgress: Map; imageLoadingProgressEmitter: Emitter<[]>; imageStatus: Map; imageStatusEmitter: Emitter<[]>; imagePlaceholderUpdateEmitter: Emitter<[]>; private iFrameRefs; /** * Indicates whether the embeddable's url has been validated for rendering. * If value not set, indicates that the validation is pending. * Initially or on url change the flag is not reset so that we can guarantee * the validation came from a trusted source (the editor). **/ private embedsValidationStatus; /** embeds that have been inserted to DOM (as a perf optim, we don't want to * insert to DOM before user initially scrolls to them) */ private initializedEmbeds; private elementsPendingErasure; private _initialized; private readonly editorLifecycleEvents; onEvent: AppEventBus["on"]; private appStateObserver; onStateChange: OnStateChange; bucketFill: AppBucketFill; flowchart: AppFlowchart; cursor: AppCursor; private gifPlayback; private zDragZoom; arrowText: AppArrowText; viewport: AppViewport; get lastViewportPosition(): { x: number; y: number; }; bindModeHandler: ReturnType | null; private textWysiwygSubmitHandler; hitLinkElement?: NonDeletedExcalidrawElement; lastPointerDownEvent: React.PointerEvent | null; lastPointerUpEvent: React.PointerEvent | PointerEvent | null; lastPointerUpIsDoubleClick: boolean; lastPointerMoveEvent: PointerEvent | null; /** current frame pointer cords */ lastPointerMoveCoords: { x: number; y: number; } | null; private lastCompletedCanvasClicks; private lastKeyboardSmartZoomImageId; private smartZoomKeyDownAt; private smartZoomKeyHitElement; /** previous frame pointer coords */ previousPointerMoveCoords: { x: number; y: number; } | null; drawShape: AppDrawShape; laserTrails: LaserTrails; eraserTrail: EraserTrail; lassoTrail: LassoTrail; cursorHints: CursorHints; onChangeEmitter: Emitter<[elements: readonly ExcalidrawElement[], appState: AppState, files: BinaryFiles]>; onPointerDownEmitter: Emitter<[activeTool: { lastActiveTool: import("../types").ActiveTool | null; locked: boolean; fromSelection: boolean; } & import("../types").ActiveTool, pointerDownState: Readonly<{ origin: Readonly<{ x: number; y: number; }>; originInGrid: Readonly<{ x: number; y: number; }>; scrollbars: ReturnType; lastCoords: { x: number; y: number; }; originalElements: Map>; resize: { handleType: import("@excalidraw/element").MaybeTransformHandleType; isResizing: boolean; offset: { x: number; y: number; }; arrowDirection: "origin" | "end"; center: { x: number; y: number; }; }; hit: { element: NonDeleted | null; allHitElements: NonDeleted[]; wasAddedToSelection: boolean; hasBeenDuplicated: boolean; hasHitCommonBoundingBoxOfSelectedElements: boolean; }; withCmdOrCtrl: boolean; drag: { hasOccurred: boolean; offset: { x: number; y: number; } | null; origin: { x: number; y: number; }; blockDragging: boolean; }; eventListeners: { onMove: null | ReturnType; onUp: null | ((event: PointerEvent) => void); onKeyDown: null | ((event: KeyboardEvent) => void); onKeyUp: null | ((event: KeyboardEvent) => void); }; boxSelection: { hasOccurred: boolean; }; }>, event: React.PointerEvent]>; onPointerUpEmitter: Emitter<[activeTool: { lastActiveTool: import("../types").ActiveTool | null; locked: boolean; fromSelection: boolean; } & import("../types").ActiveTool, pointerDownState: Readonly<{ origin: Readonly<{ x: number; y: number; }>; originInGrid: Readonly<{ x: number; y: number; }>; scrollbars: ReturnType; lastCoords: { x: number; y: number; }; originalElements: Map>; resize: { handleType: import("@excalidraw/element").MaybeTransformHandleType; isResizing: boolean; offset: { x: number; y: number; }; arrowDirection: "origin" | "end"; center: { x: number; y: number; }; }; hit: { element: NonDeleted | null; allHitElements: NonDeleted[]; wasAddedToSelection: boolean; hasBeenDuplicated: boolean; hasHitCommonBoundingBoxOfSelectedElements: boolean; }; withCmdOrCtrl: boolean; drag: { hasOccurred: boolean; offset: { x: number; y: number; } | null; origin: { x: number; y: number; }; blockDragging: boolean; }; eventListeners: { onMove: null | ReturnType; onUp: null | ((event: PointerEvent) => void); onKeyDown: null | ((event: KeyboardEvent) => void); onKeyUp: null | ((event: KeyboardEvent) => void); }; boxSelection: { hasOccurred: boolean; }; }>, event: PointerEvent]>; onUserFollowEmitter: Emitter<[payload: OnUserFollowedPayload]>; onScrollChangeEmitter: Emitter<[scrollX: number, scrollY: number, zoom: Readonly<{ value: import("../types").NormalizedZoomValue; }>]>; missingPointerEventCleanupEmitter: Emitter<[event: PointerEvent | null]>; onRemoveEventListenersEmitter: Emitter<[]>; api: ExcalidrawImperativeAPI; private createExcalidrawAPI; constructor(props: AppProps); /** * Whether the editor accepts user input (pointer, keyboard, wheel, touch, * clipboard, drag&drop). When `false`, the editor is fully inert for the * user, but remains controllable through the imperative API. * * All user-input entry points must consult this getter (directly or by * not being attached/rendered at all). */ isInteractionEnabled(props?: Pick): boolean; /** * Whether element links render their link icon and are clickable. * True when fully interactive, or when `interaction: { enabled: { links: * true } }` * (in which case clicking anywhere on a linked element opens the link, * same as in view mode). */ isLinksEnabled(props?: Pick): boolean; /** * Whether canvas navigation — panning & zooming, view-mode style — is * enabled. True when fully interactive, or when `interaction: { enabled: * { navigation: true } }`. Respects `appState.scrollConstraints`. */ isNavigationEnabled(props?: Pick): boolean; /** * Whether embeddable & iframe elements are interactive (hover & click to * activate, view-mode style). True when fully interactive, or when * allowed via `interaction.enabled.embeds` / `.interactiveContent`. */ isEmbedsEnabled(props?: Pick): boolean; /** * Whether the browser's own zoom (ctrl/cmd + wheel, pinch, keyboard * shortcuts) stays available over the non-interactive editor. * Prevented by default. */ isBrowserZoomEnabled(props?: Pick): boolean; /** * Whether the tool can be activated & driven by user input. False when * disabled via `UIOptions.tools`, or when the editor is non-interactive * and the tool isn't kept user-driven via `interaction.enabled.tools`. * * (Once UI tool availability is split from input availability — e.g. * `props.ui.tools` vs `interaction.disabled.tools` — the UI axis moves * out into its own predicate.) * * We purposely widen the `tool` type so this helper can be called with * any tool without having to type check it. */ isToolSupported: (tool: T, props?: Pick) => boolean; /** * Whether the active tool is locked in place — via the tool lock * (padlock / Q) or by being host-forced (`props.activeTool`). A locked * tool doesn't revert to the selection tool after use, and elements drawn * with it aren't selected. Forcing deliberately does not mutate * `activeTool.locked`, which is the user's persisted padlock preference. */ isToolLocked(): boolean; /** * Whether the active tool captures the primary pointer instead of the * view-mode drag-to-pan — the laser and host-implemented custom tools do; * while non-interactive, any tool allowed via * `interaction.enabled.tools` does. (Editing tools capture the pointer * trivially since view mode implies they're not active; this predicate only * matters where view-mode gates apply.) */ isActiveToolPointerCapturing(): boolean; /** Whether Excalidraw's full default UI is rendered. */ isDefaultUIEnabled(props?: Pick): boolean; /** Whether an individual default UI control is rendered. */ isUIControlEnabled(control: keyof UIConfig["enabled"], props?: Pick): boolean; updateEditorAtom: (atom: WritableAtom, ...args: Args) => Result; private onWindowMessage; private handleSkipBindMode; private resetDelayedBindMode; private previousHoveredBindableElement; private handleDelayedBindModeChange; private cacheEmbeddableRef; /** * Returns gridSize taking into account `gridModeEnabled`. * If disabled, returns null. */ getEffectiveGridSize: () => NullableGridSize; private getTextCreationGridPoint; private getHTMLIFrameElement; private handleIframeLikeElementHover; /** @returns true if iframe-like element click handled */ private handleIframeLikeCenterClick; private isDoubleClick; private isIframeLikeElementCenter; private updateEmbedValidationStatus; private updateEmbeddables; private renderEmbeddables; private getFrameNameDOMId; frameNameBoundsCache: FrameNameBoundsCache; private resetEditingFrame; private renderFrameNames; private toggleOverscrollBehavior; render(): import("react/jsx-runtime").JSX.Element; focusContainer: AppClassProperties["focusContainer"]; getSceneElementsIncludingDeleted: () => readonly import("@excalidraw/element/types").OrderedExcalidrawElement[]; getSceneElementsMapIncludingDeleted: () => SceneElementsMap; getSceneElements: () => readonly Ordered[]; onInsertElements: (elements: readonly ExcalidrawElement[]) => void; onExportImage: (type: keyof typeof EXPORT_IMAGE_TYPES, elements: ExportedElements, opts: { exportingFrame: NonDeleted | null; }) => Promise; openEyeDropper: ({ type, swapPreviewOnAlt, }: { type: "stroke" | "background"; swapPreviewOnAlt?: boolean; }) => void; dismissLinearEditor: () => void; syncActionResult: (actionResult: ActionResult) => void; scheduleCapture: () => void; scheduleUpdate: () => void; private onBlur; private onUnload; private disableEvent; private preventBrowserZoomWheel; private handleNavigationModeKeyDown; /** * PageUp/PageDown scroll the canvas by a page — vertically, or * horizontally with shift. Respects `appState.scrollConstraints` * (via `viewport.translate`). */ private maybeHandlePageScrollKeyDown; private preventBrowserZoomKeyDown; /** Ends active input sessions before switching to a view-mode/non-interactive * mode. */ private terminateActiveInteraction; private handleInteractionStateChange; /** whether the two values reference the same tool (incl. custom subtype) */ private isSameForcedTool; /** * Keeps `state.activeTool` synced to the host-controlled * `props.activeTool`. `setActiveTool` refuses non-matching activations * while forced (user input, API); this backstop covers the writers that * bypass the funnel (`actionFinalize`/`actionDeselect`, `restore()` on * scene load, ...) and re-applies the tool once it becomes activatable * (e.g. `interaction` config changes). */ private handleForcedToolChange; private resetHistory; private resetStore; /** * Resets scene & history. * ! Do not use to clear scene user action ! */ private resetScene; private initializeScene; private getFormFactor; refreshEditorInterface: () => void; private reconcileStylesPanelMode; /** TO BE USED LATER */ private setDesktopUIMode; private clearImageShapeCache; componentDidMount(): Promise; componentWillUnmount(): void; private onResize; /** generally invoked only if fullscreen was invoked programmatically */ private onFullscreenChange; private removeEventListeners; private addEventListeners; componentDidUpdate(prevProps: AppProps, prevState: AppState): void; private renderInteractiveSceneCallback; private onScroll; private onCut; private onCopy; private static resetTapTwice; private onTouchStart; private onTouchEnd; private insertClipboardContent; private containsImagePaste; private shouldInsertImageInViewMode; pasteFromClipboard: (event: ClipboardEvent) => Promise; addElementsFromPasteOrLibrary: (opts: { elements: readonly ExcalidrawElement[]; files: BinaryFiles | null; position: { clientX: number; clientY: number; } | "cursor" | "center"; retainSeed?: boolean; fit?: SetViewportOptions["fit"]; preserveFrameChildrenOrder?: boolean; }) => void; addElementsFromPaste: (opts: { elements: readonly ExcalidrawElement[]; files: BinaryFiles | null; position: { clientX: number; clientY: number; } | "cursor" | "center"; retainSeed?: boolean; fitToContent?: boolean; preserveFrameChildrenOrder?: boolean; }) => void; private addElementsFromMixedContentPaste; private addTextFromPaste; setAppState: React.Component["setState"]; removePointer: (event: React.PointerEvent | PointerEvent) => void; toggleLock: (source?: "keyboard" | "ui") => void; updateFrameRendering: (opts: Partial | ((prevState: AppState["frameRendering"]) => Partial)) => void; togglePenMode: (force: boolean | null) => void; revealIfHidden: (elements: NonDeletedExcalidrawElement[]) => void; scrollToViewport: (target: { scrollX: number; scrollY: number; zoom: number; }, _opts?: { animate?: boolean; duration?: number; }) => void; scrollToContent: (target?: SetViewportOptions["target"], opts?: { animate?: boolean; duration?: number; fitToViewport?: boolean; }) => void; /** emits a follow/unfollow intent to the host (which owns the * `userToFollow` state) via both the `onUserFollow` prop and the * imperative API emitter */ emitUserFollowIntent: (payload: OnUserFollowedPayload) => void; /** emits an UNFOLLOW intent if currently following someone — use on * user-initiated viewport changes which should break follow mode */ requestUnfollow: () => void; setToast: (toast: AppState["toast"]) => void; restoreFileFromShare: () => Promise; /** * adds supplied files to existing files in the appState. * NOTE if file already exists in editor state, the file data is not updated * */ addFiles: ExcalidrawImperativeAPI["addFiles"]; addImageElementsToScene: ExcalidrawImperativeAPI["addImageElementsToScene"]; addImagePlaceholder: ExcalidrawImperativeAPI["addImagePlaceholder"]; setDownloadProgress: ExcalidrawImperativeAPI["setDownloadProgress"]; setDownloadError: ExcalidrawImperativeAPI["setDownloadError"]; setUploadProgress: ExcalidrawImperativeAPI["setUploadProgress"]; private updateImageStatus; private areImageStatusesEqual; private clearImageLoadingProgress; private clearImageShapeCacheForFileId; private addMissingFiles; updateScene: (sceneData: { elements?: SceneData["elements"]; appState?: Pick | null; collaborators?: SceneData["collaborators"]; /** * Controls which updates should be captured by the `Store`. Captured updates are emmitted and listened to by other components, such as `History` for undo / redo purposes. * * - `CaptureUpdateAction.IMMEDIATELY`: Updates are immediately undoable. Use for most local updates. * - `CaptureUpdateAction.NEVER`: Updates never make it to undo/redo stack. Use for remote updates or scene initialization. * - `CaptureUpdateAction.EVENTUALLY`: Updates will be eventually be captured as part of a future increment. * * Check [API docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props/excalidraw-api#captureUpdate) for more details. * * @default CaptureUpdateAction.EVENTUALLY */ captureUpdate?: SceneData["captureUpdate"]; }) => void; applyDeltas: (deltas: StoreDelta[], options?: ApplyToOptions) => [SceneElementsMap, AppState, boolean]; mutateElement: >(element: TElement, updates: ElementUpdate, informMutation?: boolean) => TElement; triggerRender: ( /** force always re-renders canvas even if no change */ force?: boolean) => void; /** * @returns whether the menu was toggled on or off */ toggleSidebar: ({ name, tab, force, }: { name: SidebarName | null; tab?: SidebarTabName; force?: boolean; }) => boolean; private updateCurrentCursorPosition; private isPlainSmartZoomKey; private updateSmartZoomCursor; private resetSmartZoomKey; private handleSmartZoomKeyDown; private handleSmartZoomKeyUp; private handleSmartZoomPointerDown; private onKeyDown; private onKeyUp; setActiveTool: (tool: ({ type: ToolType; } | { type: "custom"; customType: string; }) & { locked?: boolean; fromSelection?: boolean; }, opts?: { keepSelection?: boolean; /** * When `true`, re-activating an already-active toggle tool (see * `TOGGLE_TOOLS`) switches back to the previously active tool. * Activation is idempotent by default; toggle tools always record the * previously active tool regardless (so ESC and the next `toggle` * activation can switch back to it). */ toggle?: boolean; }) => void; setOpenDialog: (dialogType: AppState["openDialog"]) => void; /** * returns whether user is making a gesture with >= 2 fingers (points) * on o touch screen (not on a trackpad). Currently only relates to Darwin * (iOS/iPadOS,MacOS), but may work on other devices in the future if * GestureEvent is standardized. */ private isTouchScreenMultiTouchGesture; getName: () => string; private onGestureStart; private onGestureChange; private onGestureEnd; private handleTextWysiwyg; private deselectElements; private getSelectedTextElement; private getSelectedTextEditingContainerAtPosition; getTextElementAtPosition(x: number, y: number): NonDeleted | null; private isHittingTextAutoResizeHandle; private handleTextAutoResizeHandlePointerDown; getElementAtPosition(x: number, y: number, opts?: ({ includeBoundTextElement?: boolean; includeLockedElements?: boolean; } | { allHitElements: NonDeleted[]; }) & { preferSelected?: boolean; }): NonDeleted | null; private getElementsAtPosition; getElementHitThreshold(element: ExcalidrawElement): number; private hitElement; getTextBindableContainerAtPosition(x: number, y: number): (Readonly<{ id: string; x: number; y: number; strokeColor: string; backgroundColor: string; fillStyle: import("@excalidraw/element/types").FillStyle; strokeWidth: number; strokeStyle: import("@excalidraw/element/types").StrokeStyle; roundness: null | { type: import("@excalidraw/element/types").RoundnessType; value?: number; }; roughness: number; opacity: number; width: number; height: number; angle: Radians; seed: number; version: number; versionNonce: number; index: import("@excalidraw/element/types").FractionalIndex | null; isDeleted: boolean; groupIds: readonly import("@excalidraw/element/types").GroupId[]; frameId: string | null; boundElements: readonly import("@excalidraw/element/types").BoundElement[] | null; updated: number; link: string | null; locked: boolean; customData?: Record; }> & { type: "rectangle"; } & { isDeleted: false; }) | (Readonly<{ id: string; x: number; y: number; strokeColor: string; backgroundColor: string; fillStyle: import("@excalidraw/element/types").FillStyle; strokeWidth: number; strokeStyle: import("@excalidraw/element/types").StrokeStyle; roundness: null | { type: import("@excalidraw/element/types").RoundnessType; value?: number; }; roughness: number; opacity: number; width: number; height: number; angle: Radians; seed: number; version: number; versionNonce: number; index: import("@excalidraw/element/types").FractionalIndex | null; isDeleted: boolean; groupIds: readonly import("@excalidraw/element/types").GroupId[]; frameId: string | null; boundElements: readonly import("@excalidraw/element/types").BoundElement[] | null; updated: number; link: string | null; locked: boolean; customData?: Record; }> & { type: "diamond"; } & { isDeleted: false; }) | (Readonly<{ id: string; x: number; y: number; strokeColor: string; backgroundColor: string; fillStyle: import("@excalidraw/element/types").FillStyle; strokeWidth: number; strokeStyle: import("@excalidraw/element/types").StrokeStyle; roundness: null | { type: import("@excalidraw/element/types").RoundnessType; value?: number; }; roughness: number; opacity: number; width: number; height: number; angle: Radians; seed: number; version: number; versionNonce: number; index: import("@excalidraw/element/types").FractionalIndex | null; isDeleted: boolean; groupIds: readonly import("@excalidraw/element/types").GroupId[]; frameId: string | null; boundElements: readonly import("@excalidraw/element/types").BoundElement[] | null; updated: number; link: string | null; locked: boolean; customData?: Record; }> & { type: "ellipse"; } & { isDeleted: false; }) | (Readonly<{ id: string; x: number; y: number; strokeColor: string; backgroundColor: string; fillStyle: import("@excalidraw/element/types").FillStyle; strokeWidth: number; strokeStyle: import("@excalidraw/element/types").StrokeStyle; roundness: null | { type: import("@excalidraw/element/types").RoundnessType; value?: number; }; roughness: number; opacity: number; width: number; height: number; angle: Radians; seed: number; version: number; versionNonce: number; index: import("@excalidraw/element/types").FractionalIndex | null; isDeleted: boolean; groupIds: readonly import("@excalidraw/element/types").GroupId[]; frameId: string | null; boundElements: readonly import("@excalidraw/element/types").BoundElement[] | null; updated: number; link: string | null; locked: boolean; customData?: Record; }> & Readonly<{ type: "line" | "arrow"; points: readonly LocalPoint[]; startBinding: import("@excalidraw/element/types").FixedPointBinding | null; endBinding: import("@excalidraw/element/types").FixedPointBinding | null; startArrowhead: import("@excalidraw/element/types").Arrowhead | null; endArrowhead: import("@excalidraw/element/types").Arrowhead | null; }> & Readonly<{ type: "arrow"; elbowed: boolean; }> & { isDeleted: false; }) | null; /** * Whether a text element's content is still being authored. * * Creating a text reverts the tool to selection during pointerdown, so the * pointerup that follows looks like an ordinary canvas click and would * capture the still-empty element as a history entry of its own. Undo would * then rewind only the typing, restoring an invisible, zero-content element * (and, for an endpoint label, leaving the arrow bound to it) rather than * removing it. The editor's own submit captures the finished text instead, * so the whole create-and-type lands in a single entry. */ private isEditingTextContent; private startTextEditing; private startImageCropping; private finishImageCropping; private shouldHandleBrowserCanvasDoubleClick; private handleCanvasDoubleClick; private handleCanvasClick; private getImageStatusActionAtPosition; private isPointInsideImageStatusOverlay; private getElementLinkAtPosition; private getElementLinkAtPositionForContextMenu; private handleElementLinkClick; private openElementLink; /** * Applies (or clears) the element-link hover affordances — pointer cursor * and tooltip — based on the current `hitLinkElement`. Returns whether a * link is being hovered. */ private applyElementLinkHoverAffordance; /** * On touchscreens (where no hover precedes the tap) re-derives * `hitLinkElement`, then opens the hit element link, if any. * Returns whether a link click was handled. */ private maybeHandleElementLinkClick; /** * Restricted pointer handling for the non-interactive editor with links * and/or embeds allowed (`interaction.enabled.links` / `.embeds` / * `.interactiveContent`) — runs only the element-link & embed concerns * (shared with the full pointer handlers above) so they behave like in * view mode without the rest of the canvas pointer machinery. */ private handleInteractiveContentPointerMove; private handleInteractiveContentPointerUp; /** * finds candidate frame under cursor (when dragging frame children/elements * inside frames) */ getTopLayerFrameAtSceneCoords: ( /** * should be already grid aligned (basically should be what the call site * sets the element's coords to, if applicable) */ sceneCoords: { x: number; y: number; }, opts?: { /** to exclude selected elements when dragging, etc. */ excludeElementIds?: AppState["selectedElementIds"]; currentFrameId?: ExcalidrawElement["frameId"]; }) => NonDeleted | null; private updateFrameToHighlight; private maybeUpdateFrameToHighlightOnPointerMove; insertNewElements: (elements: readonly ExcalidrawElement[]) => void; insertNewElement: (element: ExcalidrawElement) => void; private updateZDragZoom; private handleCanvasPointerMove; private handleEraser; private handleTouchMove; handleHoverSelectedLinearElement(linearElementEditor: LinearElementEditor, scenePointerX: number, scenePointerY: number): void; private handleCanvasPointerDown; private handleCanvasPointerUp; private maybeOpenContextMenuAfterPointerDownOnTouchDevices; private resetContextMenuTimer; /** * pointerup may not fire in certian cases (user tabs away...), so in order * to properly cleanup pointerdown state, we need to fire any hanging * pointerup handlers manually */ private maybeCleanupAfterMissingPointerUp; handleCanvasPanUsingWheelOrSpaceDrag: (event: React.PointerEvent | MouseEvent) => boolean; private updateGestureOnPointerDown; /** * Tracks the pointer within the ongoing multi-touch gesture and applies * the two-finger pinch zoom/pan, if any. */ private updateMultiTouchGesture; private initialPointerDownState; private handleDraggingScrollBar; private clearSelectionIfNotUsingSelection; /** * @returns whether the pointer event has been completely handled */ private handleSelectionOnPointerDown; private isASelectedElement; private isHittingCommonBoundingBoxOfSelectedElements; private handleTextOnPointerDown; private handleFreeDrawElementOnPointerDown; insertIframeElement: ({ sceneX, sceneY, width, height, }: { sceneX: number; sceneY: number; width: number; height: number; }) => NonDeleted; insertEmbeddableElement: ({ sceneX, sceneY, link, }: { sceneX: number; sceneY: number; link: string; }) => NonDeleted | undefined; private newImagePlaceholder; private handleLinearElementOnPointerDown; private getCurrentItemRoundness; private getCurrentItemStrokeWidth; private createGenericElementOnPointerDown; private createFrameElementOnPointerDown; private maybeCacheReferenceSnapPoints; private maybeCacheVisibleGaps; private onKeyDownFromPointerDownHandler; private onKeyUpFromPointerDownHandler; private onPointerMoveFromPointerDownHandler; private handlePointerMoveOverScrollbars; private onPointerUpFromPointerDownHandler; private restoreReadyToEraseElements; private eraseElements; private initializeImage; /** * use during async image initialization, * when the placeholder image could have been modified in the meantime, * and when you don't want to loose those modifications */ private getLatestInitializedImageElement; private onImageToolbarButtonClick; private getImageNaturalDimensions; /** updates image cache, refreshing updated elements and/or setting status to error for images that fail during element creation */ private handleImageCacheUpdate; private updateImageCache; ensureGifPlaybackLoop: () => void; loadDeferredGif: (fileId: FileId) => Promise; getGifPlaybackFrameIndex: (element: ExcalidrawImageElement) => number | undefined; setGifPlaybackFrameIndex: (element: ExcalidrawImageElement, frameIndex: number) => void; /** adds new images to imageCache and re-renders if needed */ private addNewImagesToImageCache; /** generally you should use `addNewImagesToImageCache()` directly if you need * to render new images. This is just a failsafe */ private scheduleImageRefresh; private clearSelection; private handleInteractiveCanvasRef; private insertImages; private handleAppOnDrop; loadFileToCanvas: (file: File, fileHandle: FileSystemFileHandle | null) => Promise; private handleCanvasContextMenu; private maybeDragNewGenericElement; private maybeHandleCrop; private maybeHandleResize; private getImageContextMenuItems; private getImageActionContextMenuItems; private getElementLinkContextMenuItems; private getContextMenuItems; private handleWheel; getTextWysiwygSnappedToCenterPosition(x: number, y: number, appState: AppState, container?: ExcalidrawTextContainer | null): { viewportX: number; viewportY: number; elementCenterX: number; elementCenterY: number; } | undefined; private savePointer; private resetShouldCacheIgnoreZoomDebounced; private updateDOMRect; refresh: () => void; private getCanvasOffsets; watchState: () => void; private updateLanguage; } declare global { interface Window { h: { scene: Scene; elements: readonly ExcalidrawElement[]; state: AppState; setState: React.Component["setState"]; watchState: (prev: any, next: any) => void | undefined; app: InstanceType; history: History; store: Store; }; } } export declare const createTestHook: () => void; export default App;