import React, { MouseEvent, ReactNode } from "react";
import { EditorConfiguration, WorkspaceDefinition, SidebarDefinition, ResolvedSidebar } from "../../config/types";
import type { FieldAction, WebSocketMessage } from "./EditorShell";
import type { EditorSocketDiagnostics } from "./socketDiagnostics";
import type { QuotaInfo } from "./hooks/useQuota";
import { MenuItem } from "../../config/types";
import { Command, CommandData } from "../commands/commands";
import { ComponentDetails } from "../services/componentDesignerService";
import type { EditorSlotContext } from "../views/editorSlotContext";
import type { ComponentFrameInsertColor } from "../componentFrameColors";
import { EditOperation, EditSession, FieldDescriptor, HistoryEntry, InsertOption, LanguageVersions, User, ValidationResult, Comment, WorkboxItem, ReviewWithAssignments, AddComponentOperation, PublishItemOperation, SuggestedEdit, UserInfo, UserPreferences, ParheliaSettings } from "../../types";
import { ConfirmationProps } from "../ConfirmationDialog";
import { Component, FieldButton, ItemDescriptor, Page, Timings, FullItem, Version, Field } from "../pageModel";
import { ItemsRepository } from "./itemsRepository";
import type { FieldModificationStore } from "./fieldModificationStore";
import type { ErrorToastPayload } from "./errorToast";
import { MediaSelectorMode } from "../media-selector/MediaSelector";
import type { ComponentCommand, ComponentCommandProviderContext } from "../commands/componentCommands";
import type { QueryGeometryCommandPayload } from "../bridge/protocol";
export type DragObject = {
    type: "template" | "component" | "link-component" | "items";
    typeId: string;
    templateId?: string;
    name?: string;
    thumbnailUrl?: string;
    components?: ItemDescriptor[];
    items?: ItemDescriptor[];
};
export type InsertingState = {
    position?: {
        x: number;
        y: number;
    };
    positionElement?: Element;
    positionAnchor?: "left" | "right" | "top" | "bottom";
    placeholderKey?: string;
    index?: number;
    operationId?: string;
    color?: ComponentFrameInsertColor;
};
export type AddComponentOptions = {
    showIndicator?: boolean;
    color?: ComponentFrameInsertColor;
};
export type ClipboardState = {
    items: ItemDescriptor[];
    operation: "copy" | "cut";
} | null;
export type ContextMenuSource = "placeholder-insert";
export type SelectionRange = {
    itemId: string;
    fieldId: string;
    elementKey?: string;
    isRichText?: boolean;
    language?: string;
    version?: number;
    startOffset: number;
    endOffset: number;
    text: string;
    /** Optional parent-viewport rectangle for cross-origin iframe selections */
    clientRect?: {
        x: number;
        y: number;
        width: number;
        height: number;
        top: number;
        right: number;
        bottom: number;
        left: number;
    };
    /** Optional reference to the element containing the selection (for input/textarea fields) */
    element?: HTMLInputElement | HTMLTextAreaElement;
    /** Plain text context before the selection, captured at selection time */
    contextBefore?: string;
    /** Plain text context after the selection, captured at selection time */
    contextAfter?: string;
    /** Rich text formatting state captured from the live host selection */
    formatting?: {
        bold?: boolean;
        italic?: boolean;
        underline?: boolean;
        strikethrough?: boolean;
        subscript?: boolean;
        superscript?: boolean;
        link?: boolean;
        /** Attributes of the anchor the selection is inside, when link is true */
        linkAttributes?: {
            href?: string;
            target?: string;
            title?: string;
            rel?: string;
            dataInternal?: string;
            dataItemId?: string;
            dataQuerystring?: string;
        };
        orderedList?: boolean;
        unorderedList?: boolean;
        blockTag?: string;
    };
    /** Callback for replacing the selected text - each editing context provides its own implementation */
    replaceText?: (newText: string) => void;
};
export type AddCommentOptions = {
    field?: FieldDescriptor;
};
type BridgeTextRangeQuery = NonNullable<QueryGeometryCommandPayload["textRanges"]>[number];
export type ScrollIntoViewTarget = string | {
    kind: "component";
    componentId: string;
} | {
    kind: "field";
    field: FieldDescriptor;
    fallbackComponentId?: string;
} | {
    kind: "textRange";
    key: string;
    textRanges?: BridgeTextRangeQuery[];
    fallbackField?: FieldDescriptor;
    fallbackComponentId?: string;
};
export type RemoteCaretPosition = {
    sessionId: string;
    fieldId?: string | null;
    item?: ItemDescriptor | null;
    offset: number | null;
    value?: string | null;
};
export type DialogProps<T> = {
    onClose: (result: T | null) => void;
};
export type OpenDialog = <T, P>(Component: React.ComponentType<P & DialogProps<T>>, props: P) => Promise<T | null>;
export type EditorMode = "edit" | "preview" | "suggestions";
export type LayoutMode = "final" | "shared";
export type ChangeModeOptions = {
    reason?: string;
    updateUrl?: boolean;
};
export type ModeChangeParticipant = {
    beforeModeChange?: (nextMode: EditorMode, previousMode: EditorMode, options?: ChangeModeOptions) => Promise<void> | void;
    clearInlineDedupe?: (nextMode: EditorMode, previousMode: EditorMode, options?: ChangeModeOptions) => void;
};
export type EditorSlot = {
    slotId: string;
    itemDescriptor: ItemDescriptor;
    /** Token that changes to force the slot to reload its item. */
    refreshToken?: number;
    /**
     * Purpose tag stamped when a feature opens its own slot (e.g. "validation").
     * Lets that feature find and reuse the same slot on subsequent opens.
     */
    tag?: string;
};
export type EditContextType = {
    operations: {
        moveItems: (items: ItemDescriptor[], target: ItemDescriptor, index: number) => Promise<void>;
        copyItems: (items: ItemDescriptor[], target: ItemDescriptor, index: number) => Promise<ItemDescriptor[]>;
        cloneItems: (items: ItemDescriptor[], target: ItemDescriptor, index: number) => Promise<ItemDescriptor[]>;
        duplicateItem: (item: ItemDescriptor, target: ItemDescriptor, name: string) => Promise<ItemDescriptor | undefined>;
        addComponent: (componentTypeId: string, placeholderKey: string, index: number, pageItem: ItemDescriptor, options?: AddComponentOptions) => Promise<AddComponentOperation | undefined>;
        duplicateComponents: ({ componentIds }: {
            componentIds: string[];
        }) => void;
        ensureLock: (field: FieldDescriptor) => Promise<boolean>;
        lockItems: (item: ItemDescriptor[]) => void;
        unlockItems: (item: ItemDescriptor[]) => void;
        editField: (props: {
            field: FieldDescriptor;
            value?: any;
            rawValue?: string | null;
            forceMode?: EditorMode;
            refresh?: "none" | "immediate" | "delayed" | "waitForQuietPeriod";
            originatingSlotId?: string;
        }) => Promise<void>;
        updateRenderingParameters: (componentId: string, parameters: Record<string, string | null>) => Promise<void>;
        updateRenderingRules: (componentId: string, rules: unknown, pageItem?: ItemDescriptor) => Promise<void>;
        undo: (operationId?: string) => Promise<boolean>;
        undoOperations: (operationIds: string[]) => Promise<boolean>;
        redo: (operationId?: string) => Promise<boolean>;
        executeEditOperation: (operation: EditOperation) => Promise<EditOperation>;
        executeWorkflowCommand: (item: ItemDescriptor, commandId: string) => Promise<void>;
        renameItem: (item: ItemDescriptor, newName: string) => Promise<void>;
        deleteItems: (items: ItemDescriptor[], options?: {
            canUndo?: boolean;
            hardDelete?: boolean;
        }) => Promise<boolean | undefined>;
        createItem: (parent: ItemDescriptor, templateId: string, name: string) => Promise<ItemDescriptor | undefined>;
        createVersion: (item: ItemDescriptor) => Promise<void>;
        removeVersion: (item: ItemDescriptor) => Promise<void>;
        publishItem: (operation: PublishItemOperation) => Promise<PublishItemOperation>;
        onFieldBlur: () => void;
        undoing: boolean;
    };
    comments: Comment[];
    /** True once comments have finished loading for the current item. */
    commentsLoaded: boolean;
    suggestedEdits: SuggestedEdit[];
    setSuggestedEdits: React.Dispatch<React.SetStateAction<SuggestedEdit[]>>;
    getCurrentSuggestedEdits: () => SuggestedEdit[];
    availableCommentTags: {
        label: string;
        color: string;
    }[];
    loadComments: () => void;
    loadSuggestedEdits: () => void;
    setComments: React.Dispatch<React.SetStateAction<Comment[]>>;
    selectedComment: Comment | undefined;
    setSelectedComment: React.Dispatch<React.SetStateAction<Comment | undefined>>;
    selectedSuggestedEdit: SuggestedEdit | undefined;
    setSelectedSuggestedEdit: React.Dispatch<React.SetStateAction<SuggestedEdit | undefined>>;
    showComments: boolean;
    setShowComments: React.Dispatch<React.SetStateAction<boolean>>;
    showResolvedComments: boolean;
    setShowResolvedComments: React.Dispatch<React.SetStateAction<boolean>>;
    addComment: (options?: AddCommentOptions) => void;
    workboxItems: WorkboxItem[];
    isWorkboxLoading: boolean;
    /** Re-fetches the page-mode workbox list immediately (e.g. after a publish completes). */
    refreshWorkbox?: () => void;
    itemLanguages: LanguageVersions[];
    itemVersions: Version[];
    isLoadingVersions: boolean;
    loadItemVersions: () => Promise<void>;
    /** Current workspace definition */
    workspace?: WorkspaceDefinition;
    /** All visible workspaces */
    visibleWorkspaces: WorkspaceDefinition[];
    /** Current workspace ID */
    workspaceId: string;
    /** Previous workspace ID (for back navigation) */
    previousWorkspaceId?: string;
    /** Switch to a different workspace */
    switchWorkspace: (workspaceId: string, options?: {
        skipConfirmation?: boolean;
        skipNavigationHistory?: boolean;
        urlParams?: Record<string, string | undefined>;
    }) => void;
    /** All available sidebar definitions */
    availableSidebars: SidebarDefinition[];
    /** Currently open sidebar IDs */
    openSidebars: string[];
    /** Sidebar IDs pinned to the toolbar (icon always visible for quick access) */
    pinnedSidebars: string[];
    /** Sidebar IDs that are "locked open" (stay visible when selecting another sidebar) */
    lockedSidebars: string[];
    /**
     * Groups of open sidebar IDs that share the same left column.
     * Each inner array is a vertical stack (top -> bottom).
     *
     * Example: [["history","comments"],["validation"]]
     */
    sidebarStacks: string[][];
    /** Toggle a sidebar panel open/closed */
    toggleSidebar: (sidebarId: string, options?: {
        forceClose?: boolean;
    }) => void;
    /** Open a sidebar (without toggling it closed if already open) */
    openSidebar: (sidebarId: string, options?: {
        preserveOpenSidebars?: boolean;
    }) => void;
    /** Pin/unpin a sidebar icon to the toolbar */
    toggleSidebarPin: (sidebarId: string) => void;
    /** Lock/unlock an open sidebar to stay visible when selecting another */
    toggleSidebarLock: (sidebarId: string) => void;
    /** Stack one open sidebar with another (vertical top/bottom) */
    stackSidebar: (sidebarId: string, targetSidebarId: string, position?: "before" | "after") => void;
    /** Move a sidebar into its own column before or after another sidebar's column */
    moveSidebarToColumn: (sidebarId: string, targetSidebarId: string, position?: "before" | "after") => void;
    /** Remove a sidebar from its stack into its own column */
    unstackSidebar: (sidebarId: string) => void;
    /** Reorder a sidebar within its current stack */
    reorderSidebarInStack: (sidebarId: string, newIndex: number) => void;
    /** Reorder pinned sidebars in the toolbar */
    reorderPinnedSidebars: (newOrder: string[]) => void;
    /** Reorder open sidebars (affects displayed panel order) */
    reorderOpenSidebars: (newOrder: string[]) => void;
    /** Get resolved sidebar with panels materialized */
    getResolvedSidebar: (sidebarId: string) => ResolvedSidebar | undefined;
    /** Whether the mobile editor form panel is open (bottom panel) */
    mobileEditorPanelOpen: boolean;
    /** Open or close the mobile editor form panel */
    setMobileEditorPanelOpen: (open: boolean) => void;
    compareMode: boolean;
    setCompareMode: (compareMode: boolean) => void;
    /** Fullscreen mode for the editor shell (active slot). */
    fullscreen: boolean;
    setFullscreen: (fullscreen: boolean) => void;
    mode: EditorMode;
    changeMode: (nextMode: EditorMode, options?: ChangeModeOptions) => Promise<void>;
    setMode: React.Dispatch<React.SetStateAction<EditorMode>>;
    registerModeChangeParticipant: (participant: ModeChangeParticipant) => () => void;
    modeTransitionRevision: number;
    previewDate?: Date;
    setPreviewDate: React.Dispatch<React.SetStateAction<Date | undefined>>;
    /**
     * Which Sitecore layout layer the canvas is editing — "final" (default, per-version)
     * or "shared" (cross-version __Renderings). The canvas iframe re-renders against the
     * chosen layer; in "shared" mode the renderer sees only __Renderings.
     * Edit operations honoring this mode are introduced incrementally.
     */
    layoutMode: LayoutMode;
    setLayoutMode: React.Dispatch<React.SetStateAction<LayoutMode>>;
    showSuggestedEdits: boolean;
    setShowSuggestedEdits: React.Dispatch<React.SetStateAction<boolean>>;
    suggestionVariantSelection: Record<string, string>;
    setSuggestionVariantSelection: React.Dispatch<React.SetStateAction<Record<string, string>>>;
    showSuggestedEditsDiff: boolean;
    setShowSuggestedEditsDiff: React.Dispatch<React.SetStateAction<boolean>>;
    showVersionDiffHighlighting: boolean;
    setShowVersionDiffHighlighting: React.Dispatch<React.SetStateAction<boolean>>;
    setCenterPanelView: (view: ReactNode) => void;
    configuration: EditorConfiguration;
    showToast: (message: string) => void;
    sessionId: string;
    openSplashScreen: () => void;
    getComponentCommands: (components: Component[], context?: ComponentCommandProviderContext) => Promise<ComponentCommand[]>;
    selectMedia: ({ selectedIdPath, mode, initialSearchTerm, }: {
        selectedIdPath: string;
        mode: MediaSelectorMode;
        initialSearchTerm?: string;
    }) => Promise<string | null>;
    updateUrl: (params: Record<string, string | undefined>) => void;
    select: (ids: string[]) => void;
    /**
     * Select without triggering the canvas selection auto-scroll. For
     * programmatic selections (the drop target's parent, the post-operation
     * focus of a moved component) whose targets may still measure at pre-edit
     * geometry — scrolling there would yank the canvas to a stale position.
     */
    selectWithoutAutoScroll?: (ids: string[]) => void;
    /**
     * Consumed by the canvas selection-scroll effect: true when the latest
     * selection change was armed by {@link selectWithoutAutoScroll}.
     */
    consumeNextSelectionAutoScrollSuppression?: () => boolean;
    /**
     * Event-time read of the current canvas selection. The reactive value lives
     * in `SelectionContext` (see `useSelectionContext`) so selection churn does
     * not rebuild this context; command/handler code that only needs the value
     * at call time should use this accessor instead of subscribing.
     */
    getSelection: () => string[];
    contentTreeSelection: string[];
    setContentTreeSelection: React.Dispatch<React.SetStateAction<string[]>>;
    selectedForInsertion: string;
    selectedRange?: SelectionRange;
    setSelectedRange: React.Dispatch<React.SetStateAction<SelectionRange | undefined>>;
    /** Replace the currently selected text with new text */
    replaceSelectedText?: (newText: string, selectionOverride?: SelectionRange) => Promise<void>;
    insertTextAtCursor?: (newText: string) => Promise<void>;
    setSelectedForInsertion: React.Dispatch<React.SetStateAction<string>>;
    insertMode: boolean;
    setInsertMode: React.Dispatch<React.SetStateAction<boolean>>;
    inserting: InsertingState | undefined;
    removingComponentIds: string[];
    dragStart: (dragObject: DragObject) => void;
    dragEnd: () => void;
    dragObject?: DragObject;
    clipboard: ClipboardState;
    setClipboard: React.Dispatch<React.SetStateAction<ClipboardState>>;
    refreshCompletedFlag: boolean;
    isRefreshing: boolean;
    isHistoryLoading: boolean;
    openCreatePageDialog: () => void;
    reviews: {
        reviews: ReviewWithAssignments[];
        loadReviews: () => void;
    };
    droppedInPlaceholder: (placeholderKey: string, index: number, spotPositionElement?: Element, spotPositionAnchor?: "left" | "right" | "top" | "bottom", insertOption?: InsertOption, spotPosition?: {
        x: number;
        y: number;
    }, color?: ComponentFrameInsertColor) => Promise<{
        committed: boolean;
    }>;
    lockedField: FieldDescriptor | undefined;
    requestRefresh: (mode?: "immediate" | "delayed" | "waitForQuietPeriod") => void;
    page?: Page;
    currentItemDescriptor?: ItemDescriptor;
    /**
     * Editor slots shown side-by-side. Only one is active at a time.
     * Selecting items in the tree loads into the currently active slot.
     */
    editorSlots: EditorSlot[];
    /** The slotId of the currently focused editor (null when nothing is active) */
    activeSlotId: string | null;
    /**
     * Get the active slot ID synchronously from the ref (always current).
     * Use this instead of `activeSlotId` when you need the immediate value
     * during a render cycle before React state has updated.
     */
    getActiveSlotId: () => string | null;
    /** Full slot contexts (keyed by slotId); registered by `EditorSlot`. */
    slotContexts: Map<string, EditorSlotContext>;
    /** Register/unregister full slot contexts for global access (menubar, commands, etc). */
    registerSlotContext: (slotId: string, ctx: EditorSlotContext) => void;
    unregisterSlotContext: (slotId: string) => void;
    /** Convenience helper for consumers that act on the active slot. */
    getActiveSlotContext: () => EditorSlotContext | undefined;
    /** Returns the shell-owned marker store for a slot, creating it if needed. */
    getFieldModificationStoreForSlot: (slotId: string) => FieldModificationStore;
    /** Close an editor slot */
    closeEditorSlot: (slotId: string) => void;
    /** Switch the active slot */
    setActiveSlot: (slotId: string) => void;
    setScrollIntoView: React.Dispatch<React.SetStateAction<ScrollIntoViewTarget | undefined>>;
    scrollIntoView?: ScrollIntoViewTarget;
    focusedField?: FieldDescriptor;
    setFocusedField: (field: FieldDescriptor | undefined, requestLock: boolean) => Promise<boolean>;
    unlockField: (field: FieldDescriptor) => void;
    readonly: boolean;
    triggerFieldAction: (field: FieldDescriptor, action: FieldButton, parameters?: Record<string, string>, event?: React.SyntheticEvent | globalThis.MouseEvent) => void;
    dismissFieldAction: (field: FieldDescriptor) => void;
    addFieldAction: (action: FieldAction) => void;
    updateFieldAction: (field: FieldDescriptor, updates: Partial<FieldAction>) => void;
    activeFieldActions: FieldAction[];
    contextMenuSource?: ContextMenuSource;
    showContextMenu(e: any, menuItems: MenuItem[], options?: {
        source?: ContextMenuSource;
    }): void;
    updateContextMenu(menuItems: MenuItem[]): void;
    setCurrentOverlay: React.Dispatch<React.SetStateAction<any>>;
    currentOverlay?: any;
    showFieldEditorPopup: (fields: Field[], sections: string[], ev: any) => void;
    validationResult: ValidationResult[] | undefined;
    /** The currently active slot's loaded item (for legacy consumers). */
    item?: FullItem;
    loadItem: (itemToLoad: ItemDescriptor | string, options?: {
        addToBrowseHistory?: boolean;
        /** When true, do not push an entry onto the editor navigation history. */
        skipNavigationHistory?: boolean;
        /** Internal: avoid view/URL side effects when history already reflects state. */
        skipViewChange?: boolean;
        openInNewSlot?: boolean;
        /** Explicit target slot to load into (used when switching active slot). */
        targetSlotId?: string;
        /** Purpose tag stamped on a newly created slot (see EditorSlot.tag). */
        newSlotTag?: string;
        /** Force the slot to reload even if the item appears to be already loaded. */
        forceRefresh?: boolean;
    }) => Promise<FullItem | undefined>;
    editHistory: EditOperation[];
    historyMode: "global" | "page-centric" | "current-version" | "timeline";
    setHistoryMode: React.Dispatch<React.SetStateAction<"global" | "page-centric" | "current-version" | "timeline">>;
    showOnlyMyChanges: boolean;
    setShowOnlyMyChanges: React.Dispatch<React.SetStateAction<boolean>>;
    filterByCurrentLanguage: boolean;
    setFilterByCurrentLanguage: React.Dispatch<React.SetStateAction<boolean>>;
    historySearchQuery: string;
    setHistorySearchQuery: React.Dispatch<React.SetStateAction<string>>;
    refreshHistory: (mode?: "global" | "page-centric" | "current-version" | "timeline", filterBySession?: boolean, filterByLanguage?: boolean, historyLimitOverride?: number, loadingMode?: "initial" | "more") => Promise<void>;
    canLoadMoreHistory: boolean;
    isHistoryLoadingMore: boolean;
    loadMoreHistory: () => Promise<void>;
    activeSessions: EditSession[];
    remoteCarets: Record<string, RemoteCaretPosition>;
    validating: boolean;
    isCommandDisabled: <T extends CommandData>({ command, data, }: {
        command: Command<T>;
        data?: T;
    }) => boolean;
    executeCommand: <T extends CommandData>({ command, data, event, }: {
        command: Command<T>;
        data?: T;
        event?: React.SyntheticEvent;
    }) => Promise<any>;
    componentDesignerComponent: ComponentDetails | undefined;
    setComponentDesignerComponent: React.Dispatch<React.SetStateAction<ComponentDetails | undefined>>;
    componentDesignerRendering: FullItem | undefined;
    setComponentDesignerRendering: React.Dispatch<React.SetStateAction<FullItem | undefined>>;
    timings: Timings;
    setTimings: React.Dispatch<React.SetStateAction<Timings>>;
    confirm: (props: ConfirmationProps) => void;
    showMessageDialog: (props: {
        header: string;
        message: string;
    }) => void;
    showInfoToast: (props: {
        summary?: string;
        details?: string;
    }) => void;
    showErrorToast: (error: ErrorToastPayload | unknown) => void;
    itemsRepository: ItemsRepository;
    browseHistory: HistoryEntry[];
    handleKeyDown: (event: KeyboardEvent) => void;
    startTour: () => void;
    addSocketMessageListener: (callback: (message: {
        type: string;
        payload: any;
    }) => void) => () => void;
    sendSocketMessage: (message: {
        type: string;
        payload: any;
    }) => void;
    /** Increments on each WebSocket reconnection, used to trigger re-subscriptions */
    socketConnectionVersion: number;
    socketDiagnostics: EditorSocketDiagnostics;
    revision?: string;
    /** Called by editor slots when their page model finishes building, to trigger isRefreshing re-evaluation. */
    notifyPageModelReady?: () => void;
    /** Increments whenever a slot reports that a page model finished building. */
    pageModelReadyToken?: number;
    user?: User;
    statusMessage: React.ReactNode;
    setStatusMessage: React.Dispatch<React.SetStateAction<React.ReactNode>>;
    enableCompletions: boolean;
    setEnableCompletions: React.Dispatch<React.SetStateAction<boolean>>;
    quotaInfo: QuotaInfo | null;
    isQuotaExceeded: boolean;
    getQuotaWarningMessage: () => string | null;
    isMobile: boolean;
    showComponentNavigator: boolean;
    setShowComponentNavigator: React.Dispatch<React.SetStateAction<boolean>>;
    isComponentNavigatorOpenForSlot: (slotId: string | null) => boolean;
    setComponentNavigatorOpenForSlot: (slotId: string | null, value: boolean | ((prev: boolean) => boolean)) => void;
    showAgentsPanel: boolean;
    setShowAgentsPanel: React.Dispatch<React.SetStateAction<boolean>>;
    /**
     * Resolve when a reveal surface finishes its open/close animation. Await this
     * right after triggering the state change (e.g. setShowAgentsPanel) to
     * sequence dependent UI without guessing durations. Surface keys:
     * "agents-panel", "editor-form", "sidebar:<id>". Falls back to a safety
     * timeout if no transition is observed.
     */
    waitForSurfaceSettled: (key: string, opts?: {
        safetyMs?: number;
    }) => Promise<void>;
    /** Called by layout owners when a reveal surface's animation ends. */
    notifySurfaceSettled: (key: string) => void;
    isEditorFormHiddenForSlot: (slotId: string | null) => boolean;
    setEditorFormHiddenForSlot: (slotId: string | null, value: boolean) => void;
    /** Whether the user has seen the one-time hint that explains how to re-open the form. */
    editorFormHintSeen: boolean;
    markEditorFormHintSeen: () => void;
    /** Transient flag: the hint popover should be shown right now. */
    editorFormHintVisible: boolean;
    showEditorFormHint: () => void;
    dismissEditorFormHint: () => void;
    /** Ref registered by the toolbar toggle button; used to anchor the hint popover. */
    editorFormToggleButtonRef: React.MutableRefObject<HTMLButtonElement | null>;
    showMinimap: boolean;
    setShowMinimap: React.Dispatch<React.SetStateAction<boolean>>;
    showHelpTerminal: boolean;
    setShowHelpTerminal: React.Dispatch<React.SetStateAction<boolean>>;
    toggleHelpTerminal: (options?: {
        initialPrompt?: string;
        profileName?: string;
        activeTab?: "ask" | "manual";
    }) => void;
    helpTerminalInitialPrompt?: string;
    helpTerminalProfileName?: string;
    helpTerminalActiveTab?: "ask" | "manual";
    setHelpTerminalActiveTab: React.Dispatch<React.SetStateAction<"ask" | "manual" | undefined>>;
    selectedHelpSectionId: string | null;
    setSelectedHelpSectionId: React.Dispatch<React.SetStateAction<string | null>>;
    showAgentsWorkspaceEditor: boolean;
    setShowAgentsWorkspaceEditor: React.Dispatch<React.SetStateAction<boolean>>;
    selectedAgentsWorkspaceAgentId: string | null;
    setSelectedAgentsWorkspaceAgentId: React.Dispatch<React.SetStateAction<string | null>>;
    activeEditorTab: string | null;
    setActiveEditorTab: React.Dispatch<React.SetStateAction<string | null>>;
    showLayoutComponents: boolean;
    setShowLayoutComponents: React.Dispatch<React.SetStateAction<boolean>>;
    openDialog: OpenDialog;
    webSocketMessages: WebSocketMessage[];
    clearWebSocketMessages: () => void;
    userInfo: UserInfo;
    parheliaSettings?: ParheliaSettings;
    setUserPreferences: (preferences: Partial<UserPreferences>) => void;
    favorites: any[];
    loadFavorites: () => Promise<void>;
    currentWizardId: string | null;
    setCurrentWizardId: (wizardId: string | null) => void;
    isQuickSwitcherVisible: boolean;
    registerContextFactory?: (name: string, factory: () => Promise<any> | any) => void;
    unregisterContextFactory?: (name: string) => void;
    getContextFactory?: (name: string) => (() => Promise<any> | any) | undefined;
};
export declare const EditContextProvider: React.Provider<EditContextType | undefined>;
export declare const useEditContext: () => EditContextType | undefined;
export declare function useEditContextRef(): React.RefObject<EditContextType | undefined>;
/**
 * Canvas component selection, split out of EditContext (same pattern as
 * FieldsEditContext) so that selection changes only re-render subscribers of
 * this context instead of every EditContext consumer. Dispatch stays on
 * EditContext (`select`); event-time reads use `editContext.getSelection()`.
 */
export type SelectionContextType = {
    selection: string[];
};
export declare const SelectionContextProvider: React.Provider<SelectionContextType | undefined>;
export declare const useSelectionContext: () => SelectionContextType | undefined;
/** The current canvas selection; empty outside a SelectionContextProvider. */
export declare function useCanvasSelection(): string[];
export type EditButton = {
    id: string;
    icon: React.ReactNode;
    onClick: (ev: MouseEvent) => void;
    label: string;
};
export type ModifiedField = FieldDescriptor & {
    value: string | null;
    isDirty: boolean;
    modifiedBy: User;
    timestamp: number;
    saveSequence: number;
};
export type FieldsEditContextType = {
    modifiedFields: ModifiedField[];
    recentEdits: EditedField[];
    clearRecentEdits: () => void;
    addRecentEdit: (edit: EditedField) => void;
    focusedField?: FieldDescriptor;
    setFocusedField: (field: FieldDescriptor | undefined, requestLock: boolean) => Promise<boolean>;
    unlockField: (field: FieldDescriptor) => void;
};
export type EditedField = FieldDescriptor & {
    timestamp: number;
    user: User;
};
export declare const useFieldsEditContext: () => FieldsEditContextType | undefined;
export declare const FieldsEditContextProvider: React.Provider<FieldsEditContextType | undefined>;
export declare function useFieldsEditContextRef(): React.RefObject<FieldsEditContextType | undefined>;
export type OperationsContextType = {
    executingEditOperations: EditOperation[];
    editOperationExecuted: EditOperation | undefined;
    /** Mark an operation as no longer executing (called when operation:completed/failed received) */
    markOperationComplete: (operationId: string) => void;
    /** Upsert a long-running operation reported over the WebSocket (e.g. started in another tab) */
    addExecutingOperation: (operation: EditOperation) => void;
    /** Apply live progress from operation:progress messages to an executing operation */
    updateExecutingOperationProgress: (operationId: string, progress?: number, statusMessage?: string) => void;
};
export declare const useOperationsContext: () => OperationsContextType | undefined;
export declare const OperationsContextProvider: React.Provider<OperationsContextType | undefined>;
export {};
