import { Dn as resolveTextTarget } from "./SuperConverter-CEo_K44C.es.js";
import { d as processAndInsertImageFile, f as getFileOpener, l as generateLinkedStyleString, n as createToolbarRegistry, r as resolveToolbarSources, t as createHeadlessToolbar, u as getQuickFormatList } from "./create-headless-toolbar-D_0tIvYE.es.js";
const headlessToolbarConstants = {
	DEFAULT_TEXT_ALIGN_OPTIONS: [
		{
			label: "Left",
			value: "left"
		},
		{
			label: "Center",
			value: "center"
		},
		{
			label: "Right",
			value: "right"
		},
		{
			label: "Justify",
			value: "justify"
		}
	],
	DEFAULT_LINE_HEIGHT_OPTIONS: [
		{
			label: "1.00",
			value: 1
		},
		{
			label: "1.15",
			value: 1.15
		},
		{
			label: "1.50",
			value: 1.5
		},
		{
			label: "2.00",
			value: 2
		},
		{
			label: "2.50",
			value: 2.5
		},
		{
			label: "3.00",
			value: 3
		}
	],
	DEFAULT_ZOOM_OPTIONS: [
		{
			label: "50%",
			value: 50
		},
		{
			label: "75%",
			value: 75
		},
		{
			label: "90%",
			value: 90
		},
		{
			label: "100%",
			value: 100
		},
		{
			label: "125%",
			value: 125
		},
		{
			label: "150%",
			value: 150
		},
		{
			label: "200%",
			value: 200
		}
	],
	DEFAULT_DOCUMENT_MODE_OPTIONS: [
		{
			label: "Editing",
			value: "editing",
			description: "Edit document directly"
		},
		{
			label: "Suggesting",
			value: "suggesting",
			description: "Edits become suggestions"
		},
		{
			label: "Viewing",
			value: "viewing",
			description: "View clean version of document only"
		}
	],
	DEFAULT_FONT_SIZE_OPTIONS: [
		{
			label: "8",
			value: "8pt"
		},
		{
			label: "9",
			value: "9pt"
		},
		{
			label: "10",
			value: "10pt"
		},
		{
			label: "11",
			value: "11pt"
		},
		{
			label: "12",
			value: "12pt"
		},
		{
			label: "14",
			value: "14pt"
		},
		{
			label: "18",
			value: "18pt"
		},
		{
			label: "24",
			value: "24pt"
		},
		{
			label: "30",
			value: "30pt"
		},
		{
			label: "36",
			value: "36pt"
		},
		{
			label: "48",
			value: "48pt"
		},
		{
			label: "60",
			value: "60pt"
		},
		{
			label: "72",
			value: "72pt"
		},
		{
			label: "96",
			value: "96pt"
		}
	],
	DEFAULT_FONT_FAMILY_OPTIONS: [
		{
			label: "Aptos",
			value: "Aptos, Arial, sans-serif"
		},
		{
			label: "Georgia",
			value: "Georgia, serif"
		},
		{
			label: "Arial",
			value: "Arial, sans-serif"
		},
		{
			label: "Courier New",
			value: "Courier New, monospace"
		},
		{
			label: "Times New Roman",
			value: "Times New Roman, serif"
		}
	],
	DEFAULT_TEXT_COLOR_OPTIONS: [
		{
			label: "Black",
			value: "#000000"
		},
		{
			label: "Dark Gray",
			value: "#434343"
		},
		{
			label: "Gray",
			value: "#666666"
		},
		{
			label: "Light Gray",
			value: "#999999"
		},
		{
			label: "Red",
			value: "#ff0000"
		},
		{
			label: "Orange",
			value: "#ff9900"
		},
		{
			label: "Yellow",
			value: "#ffff00"
		},
		{
			label: "Green",
			value: "#00ff00"
		},
		{
			label: "Cyan",
			value: "#00ffff"
		},
		{
			label: "Blue",
			value: "#0000ff"
		},
		{
			label: "Purple",
			value: "#9900ff"
		},
		{
			label: "Magenta",
			value: "#ff00ff"
		},
		{
			label: "None",
			value: "none"
		}
	],
	DEFAULT_HIGHLIGHT_COLOR_OPTIONS: [
		{
			label: "Yellow",
			value: "#ffff00"
		},
		{
			label: "Green",
			value: "#00ff00"
		},
		{
			label: "Cyan",
			value: "#00ffff"
		},
		{
			label: "Pink",
			value: "#ff00ff"
		},
		{
			label: "Blue",
			value: "#0000ff"
		},
		{
			label: "Red",
			value: "#ff0000"
		},
		{
			label: "Orange",
			value: "#ff9900"
		},
		{
			label: "None",
			value: "none"
		}
	]
};
const headlessToolbarHelpers = {
	getQuickFormatList,
	generateLinkedStyleString,
	getFileOpener,
	processAndInsertImageFile
};
function shallowEqual(a, b) {
	if (Object.is(a, b)) return true;
	if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
	if (Array.isArray(a)) {
		if (!Array.isArray(b) || a.length !== b.length) return false;
		for (let i = 0; i < a.length; i += 1) if (!Object.is(a[i], b[i])) return false;
		return true;
	}
	if (Array.isArray(b)) return false;
	const keysA = Object.keys(a);
	const keysB = Object.keys(b);
	if (keysA.length !== keysB.length) return false;
	for (const key of keysA) {
		if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
		if (!Object.is(a[key], b[key])) return false;
	}
	return true;
}
async function scrollRangeIntoView(editor, input) {
	const presentation = editor.presentationEditor;
	if (!presentation) return { success: false };
	const target = input.target;
	if (target.kind === "entity") {
		if (typeof presentation.navigateTo !== "function") return { success: false };
		try {
			const ok = await presentation.navigateTo(target, {
				behavior: input.behavior ?? "smooth",
				block: input.block ?? "center"
			});
			return { success: Boolean(ok) };
		} catch {
			return { success: false };
		}
	}
	if (typeof presentation.scrollToPositionAsync !== "function") return { success: false };
	try {
		const firstSegment = Array.isArray(target.segments) && (target.segments.length ?? 0) > 0 ? target.segments[0] : {
			blockId: target.blockId,
			range: target.range
		};
		if (!firstSegment) return { success: false };
		const resolved = resolveTextTarget(editor, {
			kind: "text",
			blockId: firstSegment.blockId,
			range: firstSegment.range
		});
		if (!resolved) return { success: false };
		const ok = await presentation.scrollToPositionAsync(resolved.from, {
			block: input.block ?? "center",
			behavior: input.behavior ?? "smooth"
		});
		return { success: Boolean(ok) };
	} catch {
		return { success: false };
	}
}
var DEFAULT_BUILTIN_COLLISION_MESSAGE = (id) => `[superdoc/ui] ui.commands.register(): id '${id}' collides with a built-in command. Pass { override: true } to replace deliberately. Registration refused.`;
var DEFAULT_REPLACEMENT_MESSAGE = (id) => `[superdoc/ui] ui.commands.register(): id '${id}' was already registered. Replacing prior registration.`;
var STATIC_CUSTOM_STATE = {
	active: false,
	disabled: false,
	value: void 0
};
function createCustomCommandsRegistry(deps) {
	const entries = /* @__PURE__ */ new Map();
	const handleCache = /* @__PURE__ */ new Map();
	const subscribableCache = /* @__PURE__ */ new Map();
	const observerDisposers = /* @__PURE__ */ new Map();
	const getOrCreateSubscribable = (id) => {
		let sub = subscribableCache.get(id);
		if (sub) return sub;
		sub = deps.buildSubscribable(id);
		subscribableCache.set(id, sub);
		return sub;
	};
	const disposeAllObservers = (id) => {
		const set = observerDisposers.get(id);
		if (!set) return;
		const disposers = [...set];
		observerDisposers.delete(id);
		for (const dispose of disposers) try {
			dispose();
		} catch {}
	};
	const buildHandle = (id, ownEntry) => ({
		observe(listener) {
			let innerOff = null;
			let stopped = false;
			const dispose = () => {
				if (stopped) return;
				stopped = true;
				innerOff?.();
				innerOff = null;
				observerDisposers.get(id)?.delete(dispose);
			};
			let set = observerDisposers.get(id);
			if (!set) {
				set = /* @__PURE__ */ new Set();
				observerDisposers.set(id, set);
			}
			set.add(dispose);
			innerOff = getOrCreateSubscribable(id).subscribe((state) => {
				if (stopped) return;
				if (entries.get(id) !== ownEntry) {
					dispose();
					return;
				}
				const next = state ? {
					active: state.active,
					disabled: state.disabled,
					value: state.value,
					source: "custom"
				} : {
					...STATIC_CUSTOM_STATE,
					source: "custom",
					value: void 0
				};
				try {
					listener(next);
				} catch {}
			});
			return dispose;
		},
		execute: ((payload) => {
			if (entries.get(id) !== ownEntry) return false;
			return registry.execute(id, payload);
		})
	});
	const getHandle = (id) => {
		const entry = entries.get(id);
		if (!entry) return void 0;
		let cached = handleCache.get(id);
		if (cached) return cached;
		cached = buildHandle(id, entry);
		handleCache.set(id, cached);
		return cached;
	};
	const registry = {
		register(registration) {
			const { id, execute, getState, override = false } = registration;
			if (deps.isBuiltIn(id) && !override) {
				console.warn(DEFAULT_BUILTIN_COLLISION_MESSAGE(id));
				return {
					handle: buildNoOpHandle(id),
					invalidate() {},
					unregister() {}
				};
			}
			if (entries.has(id)) {
				console.warn(DEFAULT_REPLACEMENT_MESSAGE(id));
				disposeAllObservers(id);
			}
			const ownEntry = {
				id,
				execute,
				getState,
				override,
				lastErrorMessage: null
			};
			entries.set(id, ownEntry);
			handleCache.delete(id);
			deps.scheduleNotify();
			let unregistered = false;
			return {
				handle: getHandle(id),
				invalidate() {
					if (unregistered) return;
					if (entries.get(id) !== ownEntry) return;
					deps.scheduleNotify();
				},
				unregister() {
					if (unregistered) return;
					unregistered = true;
					if (entries.get(id) !== ownEntry) return;
					entries.delete(id);
					handleCache.delete(id);
					subscribableCache.delete(id);
					disposeAllObservers(id);
					deps.scheduleNotify();
				}
			};
		},
		has(id) {
			return entries.has(id);
		},
		computeStates(state) {
			const out = {};
			for (const entry of entries.values()) {
				let derived;
				if (entry.getState) try {
					const result = entry.getState({ state });
					derived = result == null ? void 0 : result;
				} catch (err) {
					derived = void 0;
					const message = err instanceof Error ? err.message : String(err);
					if (entry.lastErrorMessage !== message) {
						entry.lastErrorMessage = message;
						console.error(`[superdoc/ui] custom command '${entry.id}' getState threw: ${message}`);
					}
				}
				out[entry.id] = {
					active: derived?.active ?? STATIC_CUSTOM_STATE.active,
					disabled: derived?.disabled ?? STATIC_CUSTOM_STATE.disabled,
					value: derived ? derived.value : STATIC_CUSTOM_STATE.value,
					source: "custom"
				};
			}
			return out;
		},
		getHandle,
		execute(id, payload) {
			const entry = entries.get(id);
			if (!entry) return false;
			try {
				const result = entry.execute({
					payload,
					superdoc: deps.superdoc,
					editor: deps.getEditor()
				});
				if (result instanceof Promise) return result.then((value) => value !== false, (err) => {
					console.error(`[superdoc/ui] custom command '${id}' execute rejected:`, err);
					return false;
				});
				return result !== false;
			} catch (err) {
				console.error(`[superdoc/ui] custom command '${id}' execute threw:`, err);
				return false;
			}
		},
		destroy() {
			const ids = [...observerDisposers.keys()];
			for (const id of ids) disposeAllObservers(id);
			entries.clear();
			handleCache.clear();
			subscribableCache.clear();
		}
	};
	return registry;
}
function buildNoOpHandle(id) {
	return {
		observe() {
			return () => {};
		},
		execute: ((..._args) => {
			console.warn(`[superdoc/ui] ui.commands['${id}'].execute(): registration was refused (built-in collision without override).`);
			return false;
		})
	};
}
var EDITOR_EVENTS = [
	"transaction",
	"selectionUpdate",
	"commentsUpdate",
	"commentsLoaded",
	"comment-positions",
	"tracked-changes-changed"
];
var LIST_REFRESH_EVENTS = [
	"commentsUpdate",
	"commentsLoaded",
	"tracked-changes-changed"
];
var SUPERDOC_EVENTS = [
	"editorCreate",
	"document-mode-change",
	"zoomChange"
];
var PRESENTATION_EVENTS = [
	"headerFooterEditingContext",
	"headerFooterUpdate",
	"headerFooterTransaction",
	"activeSurfaceChange",
	"historyStateChange"
];
var FALLBACK_COMMAND_STATE = {
	active: false,
	disabled: true,
	value: void 0
};
var FALLBACK_DYNAMIC_STATE = {
	active: false,
	disabled: true,
	value: void 0,
	source: "built-in"
};
var ALL_TOOLBAR_COMMAND_IDS = Object.keys(createToolbarRegistry());
var EMPTY_ACTIVE_IDS = Object.freeze([]);
function deepClone(value) {
	if (value === null || typeof value !== "object") return value;
	if (Array.isArray(value)) return value.map((item) => deepClone(item));
	const out = {};
	for (const key of Object.keys(value)) out[key] = deepClone(value[key]);
	return out;
}
function deepFreeze(value) {
	if (value === null || typeof value !== "object") return value;
	if (Object.isFrozen(value)) return value;
	if (Array.isArray(value)) for (const item of value) deepFreeze(item);
	else for (const key of Object.keys(value)) deepFreeze(value[key]);
	return Object.freeze(value);
}
function resolveRoutedEditor(superdoc) {
	try {
		return resolveToolbarSources(superdoc).activeEditor ?? null;
	} catch {
		return superdoc.activeEditor ?? null;
	}
}
function resolveHostEditor(superdoc) {
	return superdoc.activeEditor ?? null;
}
function resolvePresentationEditor(superdoc) {
	try {
		return resolveToolbarSources(superdoc).presentationEditor ?? null;
	} catch {
		return null;
	}
}
function textTargetToSelectionTarget(textTarget) {
	if (!textTarget) return null;
	const segments = textTarget.segments;
	if (!segments || segments.length === 0) return null;
	const first = segments[0];
	const last = segments[segments.length - 1];
	const story = textTarget.story;
	const start = story ? {
		kind: "text",
		blockId: first.blockId,
		offset: first.range.start,
		story
	} : {
		kind: "text",
		blockId: first.blockId,
		offset: first.range.start
	};
	const end = story ? {
		kind: "text",
		blockId: last.blockId,
		offset: last.range.end,
		story
	} : {
		kind: "text",
		blockId: last.blockId,
		offset: last.range.end
	};
	return story ? {
		kind: "selection",
		start,
		end,
		story
	} : {
		kind: "selection",
		start,
		end
	};
}
function createSuperDocUI(options) {
	const { superdoc } = options;
	let destroyed = false;
	const stateChangeListeners = /* @__PURE__ */ new Set();
	const teardown = [];
	let scheduled = false;
	const scheduleNotify = () => {
		if (scheduled || destroyed) return;
		scheduled = true;
		queueMicrotask(() => {
			scheduled = false;
			if (destroyed) return;
			stateChangeListeners.forEach((listener) => {
				try {
					listener();
				} catch {}
			});
		});
	};
	const toolbarController = createHeadlessToolbar({
		superdoc,
		commands: ALL_TOOLBAR_COMMAND_IDS
	});
	let toolbarSnapshot = toolbarController.getSnapshot();
	const offToolbarSubscribe = toolbarController.subscribe(({ snapshot }) => {
		toolbarSnapshot = snapshot;
		scheduleNotify();
	});
	teardown.push(() => {
		offToolbarSubscribe();
		try {
			toolbarController.destroy();
		} catch {}
	});
	const BUILT_IN_COMMAND_ID_SET = new Set(ALL_TOOLBAR_COMMAND_IDS);
	const EMPTY_COMMENTS_LIST = {
		evaluatedRevision: "",
		total: 0,
		items: [],
		page: {
			limit: 0,
			offset: 0,
			returned: 0
		}
	};
	let commentsListCache = EMPTY_COMMENTS_LIST;
	const refreshCommentsListCache = () => {
		const editor = resolveRoutedEditor(superdoc);
		const list = editor?.doc?.comments?.list;
		if (typeof list !== "function") {
			commentsListCache = EMPTY_COMMENTS_LIST;
			return;
		}
		try {
			commentsListCache = list.call(editor.doc.comments, void 0) ?? EMPTY_COMMENTS_LIST;
		} catch {
			commentsListCache = EMPTY_COMMENTS_LIST;
		}
	};
	refreshCommentsListCache();
	const EMPTY_TRACK_CHANGES_LIST = {
		evaluatedRevision: "",
		total: 0,
		items: [],
		page: {
			limit: 0,
			offset: 0,
			returned: 0
		}
	};
	let trackChangesListCache = EMPTY_TRACK_CHANGES_LIST;
	const refreshTrackChangesListCache = () => {
		const editor = resolveRoutedEditor(superdoc);
		const list = editor?.doc?.trackChanges?.list;
		if (typeof list !== "function") {
			trackChangesListCache = EMPTY_TRACK_CHANGES_LIST;
			return;
		}
		try {
			trackChangesListCache = list.call(editor.doc.trackChanges, { in: "all" }) ?? EMPTY_TRACK_CHANGES_LIST;
		} catch {
			trackChangesListCache = EMPTY_TRACK_CHANGES_LIST;
		}
	};
	refreshTrackChangesListCache();
	let activeTrackChangeId = null;
	let lastSelectionDrivenId = null;
	let trackChangesMemo = null;
	let selectionMemo = null;
	let documentMemo = null;
	let dirty = false;
	const buildSelectionKey = (empty, target, activeMarks, activeCommentIds, activeChangeIds, quotedText) => {
		const story = target ? target.story : void 0;
		let storyKey = "";
		if (story) {
			const storyType = typeof story.storyType === "string" ? story.storyType : "";
			const refId = typeof story.refId === "string" ? story.refId : "";
			const noteId = typeof story.noteId === "string" ? story.noteId : "";
			const section = story.section && typeof story.section === "object" ? JSON.stringify(story.section) : "";
			storyKey = `s=${storyType}:r=${refId}:n=${noteId}:hf=${typeof story.headerFooterKind === "string" ? story.headerFooterKind : ""}:v=${typeof story.variant === "string" ? story.variant : ""}:sec=${section}`;
		}
		const targetKey = target ? target.segments.map((s) => `${s.blockId}:${s.range.start}-${s.range.end}`).join("|") : "null";
		const marks = [...activeMarks].sort().join(",");
		const comments$1 = [...activeCommentIds].sort().join(",");
		const changes = [...activeChangeIds].sort().join(",");
		return `${empty ? "1" : "0"}:${storyKey}:${targetKey}:m=${marks}:c=${comments$1}:tc=${changes}:t=${quotedText}`;
	};
	const computeState = () => {
		const editor = resolveRoutedEditor(superdoc);
		const ready = editor != null;
		const selectionInfo = editor?.doc?.selection?.current?.({ includeText: true });
		const empty = selectionInfo ? selectionInfo.empty : true;
		const quotedText = selectionInfo?.text ?? "";
		const documentMode = superdoc.config?.documentMode ?? null;
		const activeIds = selectionInfo?.activeCommentIds ?? EMPTY_ACTIVE_IDS;
		const activeChangeIdsFromSelection = selectionInfo?.activeChangeIds ?? EMPTY_ACTIVE_IDS;
		const selectionDrivenActiveId = activeChangeIdsFromSelection[0] ?? null;
		const selectionMoved = selectionDrivenActiveId !== lastSelectionDrivenId;
		lastSelectionDrivenId = selectionDrivenActiveId;
		if (selectionMoved && selectionDrivenActiveId) activeTrackChangeId = selectionDrivenActiveId;
		let trackChangesSlice;
		if (trackChangesMemo && trackChangesMemo.changesRef === trackChangesListCache.items && trackChangesMemo.activeId === activeTrackChangeId) trackChangesSlice = trackChangesMemo.slice;
		else {
			const items = trackChangesListCache.items.map((change) => ({
				id: change.id,
				change
			}));
			if (activeTrackChangeId && !items.some((item) => item.id === activeTrackChangeId)) activeTrackChangeId = null;
			trackChangesSlice = {
				items,
				total: items.length,
				activeId: activeTrackChangeId
			};
			trackChangesMemo = {
				changesRef: trackChangesListCache.items,
				activeId: activeTrackChangeId,
				slice: trackChangesSlice
			};
		}
		const selectionTextTarget = selectionInfo?.target ?? null;
		const selectionActiveMarks = selectionInfo?.activeMarks ?? EMPTY_ACTIVE_IDS;
		const selectionKey = buildSelectionKey(empty, selectionTextTarget, selectionActiveMarks, activeIds, activeChangeIdsFromSelection, quotedText);
		let selectionSlice;
		if (selectionMemo && selectionMemo.key === selectionKey) selectionSlice = selectionMemo.slice;
		else {
			selectionSlice = {
				empty,
				target: selectionTextTarget,
				selectionTarget: textTargetToSelectionTarget(selectionTextTarget),
				activeMarks: selectionActiveMarks,
				activeCommentIds: activeIds,
				activeChangeIds: activeChangeIdsFromSelection,
				quotedText
			};
			selectionMemo = {
				key: selectionKey,
				slice: selectionSlice
			};
		}
		const builtInCommands = {};
		if (toolbarSnapshot.commands) for (const [id, cmdState] of Object.entries(toolbarSnapshot.commands)) {
			if (!cmdState) continue;
			builtInCommands[id] = {
				active: cmdState.active,
				disabled: cmdState.disabled,
				value: cmdState.value,
				source: "built-in"
			};
		}
		let documentSlice;
		if (documentMemo && documentMemo.slice.ready === ready && documentMemo.slice.mode === documentMode && documentMemo.slice.dirty === dirty) documentSlice = documentMemo.slice;
		else {
			documentSlice = {
				ready,
				mode: documentMode,
				dirty
			};
			documentMemo = { slice: documentSlice };
		}
		const partial = {
			ready,
			documentMode,
			document: documentSlice,
			selection: selectionSlice,
			toolbar: {
				context: toolbarSnapshot.context,
				commands: builtInCommands
			},
			comments: {
				total: commentsListCache.total,
				items: commentsListCache.items,
				activeIds: selectionSlice.activeCommentIds
			},
			trackChanges: trackChangesSlice
		};
		const customCommandStates = customCommandsRegistry.computeStates(partial);
		const mergedCommands = customCommandStates ? {
			...builtInCommands,
			...customCommandStates
		} : builtInCommands;
		return {
			...partial,
			toolbar: {
				context: toolbarSnapshot.context,
				commands: mergedCommands
			}
		};
	};
	if (typeof superdoc.on === "function" && typeof superdoc.off === "function") {
		SUPERDOC_EVENTS.forEach((name) => {
			superdoc.on?.(name, scheduleNotify);
		});
		teardown.push(() => {
			SUPERDOC_EVENTS.forEach((name) => superdoc.off?.(name, scheduleNotify));
		});
	}
	let currentEditor = null;
	let currentEditorTeardown = null;
	const refreshAndNotify = () => {
		refreshCommentsListCache();
		refreshTrackChangesListCache();
		scheduleNotify();
	};
	const onTransaction = (payload) => {
		if (dirty) return;
		const tr = payload?.transaction;
		if (tr && tr.docChanged === true) dirty = true;
	};
	const attachEditorListeners = () => {
		const next = resolveRoutedEditor(superdoc);
		if (next === currentEditor) return;
		currentEditorTeardown?.();
		currentEditorTeardown = null;
		currentEditor = next;
		if (!next || typeof next.on !== "function" || typeof next.off !== "function") return;
		EDITOR_EVENTS.forEach((name) => {
			next.on?.(name, scheduleNotify);
		});
		LIST_REFRESH_EVENTS.forEach((name) => {
			next.on?.(name, refreshAndNotify);
		});
		next.on?.("transaction", onTransaction);
		currentEditorTeardown = () => {
			EDITOR_EVENTS.forEach((name) => next.off?.(name, scheduleNotify));
			LIST_REFRESH_EVENTS.forEach((name) => next.off?.(name, refreshAndNotify));
			next.off?.("transaction", onTransaction);
		};
		refreshCommentsListCache();
		scheduleNotify();
	};
	let currentPresentation = null;
	let currentPresentationTeardown = null;
	const attachPresentationListeners = () => {
		const next = resolvePresentationEditor(superdoc);
		if (next === currentPresentation) return;
		currentPresentationTeardown?.();
		currentPresentationTeardown = null;
		currentPresentation = next;
		if (!next || typeof next.on !== "function" || typeof next.off !== "function") return;
		const onPresentationChange = () => {
			attachEditorListeners();
			scheduleNotify();
		};
		PRESENTATION_EVENTS.forEach((name) => {
			next.on?.(name, onPresentationChange);
		});
		currentPresentationTeardown = () => {
			PRESENTATION_EVENTS.forEach((name) => next.off?.(name, onPresentationChange));
		};
	};
	const resetDirtyOnNewDocument = () => {
		if (dirty) {
			dirty = false;
			scheduleNotify();
		}
	};
	attachPresentationListeners();
	attachEditorListeners();
	if (typeof superdoc.on === "function") {
		superdoc.on?.("editorCreate", attachPresentationListeners);
		superdoc.on?.("editorCreate", attachEditorListeners);
		superdoc.on?.("editorCreate", resetDirtyOnNewDocument);
	}
	teardown.push(() => {
		if (typeof superdoc.off === "function") {
			superdoc.off?.("editorCreate", attachPresentationListeners);
			superdoc.off?.("editorCreate", attachEditorListeners);
			superdoc.off?.("editorCreate", resetDirtyOnNewDocument);
		}
		currentPresentationTeardown?.();
		currentPresentationTeardown = null;
		currentPresentation = null;
		currentEditorTeardown?.();
		currentEditorTeardown = null;
		currentEditor = null;
	});
	const select = (selector, equality = Object.is) => {
		let last = selector(computeState());
		const listeners = /* @__PURE__ */ new Set();
		const onStateChange = () => {
			const next = selector(computeState());
			if (equality(last, next)) return;
			last = next;
			listeners.forEach((listener) => {
				try {
					listener(next);
				} catch {}
			});
		};
		return {
			get() {
				if (listeners.size === 0) last = selector(computeState());
				return last;
			},
			subscribe(listener) {
				if (listeners.size === 0) {
					last = selector(computeState());
					stateChangeListeners.add(onStateChange);
				}
				listeners.add(listener);
				try {
					listener(last);
				} catch {}
				return () => {
					listeners.delete(listener);
					if (listeners.size === 0) stateChangeListeners.delete(onStateChange);
				};
			}
		};
	};
	const toolbar = {
		getSnapshot: () => computeState().toolbar,
		subscribe(listener) {
			return select((state) => state.toolbar, () => false).subscribe((snapshot) => {
				try {
					listener({ snapshot });
				} catch {}
			});
		},
		execute: ((id, payload) => {
			const result = dispatchCommand(id, payload);
			return result instanceof Promise ? true : result;
		})
	};
	const commandHandleCache = /* @__PURE__ */ new Map();
	const commandSubscribableCache = /* @__PURE__ */ new Map();
	const getCommandSubscribable = (id) => {
		let sub = commandSubscribableCache.get(id);
		if (sub) return sub;
		sub = select((state) => state.toolbar.commands?.[id], shallowEqual);
		commandSubscribableCache.set(id, sub);
		return sub;
	};
	const buildCommandHandle = (id) => {
		return {
			observe(listener) {
				return getCommandSubscribable(id).subscribe((cmdState) => {
					const next = cmdState ?? FALLBACK_COMMAND_STATE;
					try {
						listener(next);
					} catch {}
				});
			},
			execute: ((payload) => {
				const result = dispatchCommand(id, payload);
				return result instanceof Promise ? true : result;
			})
		};
	};
	const customCommandsRegistry = createCustomCommandsRegistry({
		superdoc,
		getEditor: () => resolveRoutedEditor(superdoc),
		isBuiltIn: (id) => BUILT_IN_COMMAND_ID_SET.has(id),
		scheduleNotify,
		buildSubscribable: (id) => select((state) => state.toolbar.commands?.[id], shallowEqual)
	});
	teardown.push(() => {
		customCommandsRegistry.destroy();
	});
	const dispatchCommand = (id, payload) => {
		if (customCommandsRegistry.has(id)) return customCommandsRegistry.execute(id, payload);
		return toolbarController.execute(id, payload);
	};
	const dynamicHandleCache = /* @__PURE__ */ new Map();
	const buildBuiltInDynamicHandle = (id) => {
		return {
			observe(listener) {
				return getCommandSubscribable(id).subscribe((cmdState) => {
					const next = cmdState ?? FALLBACK_DYNAMIC_STATE;
					try {
						listener(next);
					} catch {}
				});
			},
			execute(payload) {
				return dispatchCommand(id, payload);
			}
		};
	};
	const buildCustomDynamicHandle = (id) => {
		const customHandle = customCommandsRegistry.getHandle(id);
		if (!customHandle) return void 0;
		return {
			observe(listener) {
				return customHandle.observe(listener);
			},
			execute(payload) {
				return customHandle.execute(payload);
			}
		};
	};
	const getDynamicHandle = (id) => {
		if (typeof id !== "string" || id.length === 0) return void 0;
		if (customCommandsRegistry.has(id)) return buildCustomDynamicHandle(id);
		if (!BUILT_IN_COMMAND_ID_SET.has(id)) return void 0;
		let cached = dynamicHandleCache.get(id);
		if (cached) return cached;
		cached = buildBuiltInDynamicHandle(id);
		dynamicHandleCache.set(id, cached);
		return cached;
	};
	const commands = new Proxy({}, { get(_, prop) {
		if (typeof prop !== "string") return void 0;
		if (prop === "register") return customCommandsRegistry.register.bind(customCommandsRegistry);
		if (prop === "get") return getDynamicHandle;
		if (customCommandsRegistry.has(prop)) {
			const customHandle = customCommandsRegistry.getHandle(prop);
			if (customHandle) return customHandle;
		}
		let handle = commandHandleCache.get(prop);
		if (handle) return handle;
		handle = buildCommandHandle(prop);
		commandHandleCache.set(prop, handle);
		return handle;
	} });
	const requireDocComments = () => {
		const api = resolveRoutedEditor(superdoc)?.doc?.comments;
		if (!api) throw new Error("ui.comments: no active editor / comments API. Open a document first.");
		return api;
	};
	const runScrollIntoView = async (input) => {
		const editor = resolveHostEditor(superdoc);
		if (!editor) return { success: false };
		return scrollRangeIntoView(editor, input);
	};
	const comments = {
		getSnapshot: () => computeState().comments,
		subscribe(listener) {
			return select((state) => state.comments, shallowEqual).subscribe((snapshot) => {
				try {
					listener({ snapshot });
				} catch {}
			});
		},
		createFromSelection({ text }) {
			const target = resolveRoutedEditor(superdoc)?.doc?.selection?.current?.()?.target;
			if (!target) return {
				success: false,
				failure: {
					code: "NO_OP",
					message: "ui.comments.createFromSelection: no addressable selection target."
				}
			};
			const api = requireDocComments();
			const receipt = api.create.call(api, {
				target,
				text
			});
			refreshAndNotify();
			return receipt;
		},
		createFromCapture(capture, { text }) {
			const target = capture?.target ?? null;
			if (!target) return {
				success: false,
				failure: {
					code: "NO_OP",
					message: "ui.comments.createFromCapture: capture has no addressable target."
				}
			};
			const api = requireDocComments();
			const receipt = api.create.call(api, {
				target,
				text
			});
			refreshAndNotify();
			return receipt;
		},
		reply(parentCommentId, { text }) {
			if (!(typeof text === "string" ? text.trim() : "")) return {
				success: false,
				failure: {
					code: "NO_OP",
					message: "ui.comments.reply: text is empty."
				}
			};
			const api = requireDocComments();
			const receipt = api.create.call(api, {
				parentCommentId,
				text
			});
			refreshAndNotify();
			return receipt;
		},
		resolve(commentId) {
			const api = requireDocComments();
			const receipt = api.patch.call(api, {
				commentId,
				status: "resolved"
			});
			refreshAndNotify();
			return receipt;
		},
		reopen(commentId) {
			const api = requireDocComments();
			const receipt = api.patch.call(api, {
				commentId,
				status: "active"
			});
			refreshAndNotify();
			return receipt;
		},
		delete(commentId) {
			const api = requireDocComments();
			const receipt = api.delete.call(api, { commentId });
			refreshAndNotify();
			return receipt;
		},
		async scrollTo(commentId) {
			return runScrollIntoView({
				target: {
					kind: "entity",
					entityType: "comment",
					entityId: commentId
				},
				block: "center",
				behavior: "smooth"
			});
		}
	};
	const requireDocTrackChanges = () => {
		const api = resolveHostEditor(superdoc)?.doc?.trackChanges;
		if (!api?.decide) throw new Error("ui.trackChanges: no active editor / trackChanges API. Open a document first.");
		return api;
	};
	const buildChangeDecideTarget = (changeId) => {
		const story = trackChangesListCache.items.find((c) => c.id === changeId)?.address?.story;
		if (story != null) return {
			id: changeId,
			story
		};
		return { id: changeId };
	};
	const lookupChangeStory = (id) => {
		return trackChangesListCache.items.find((c) => c.id === id)?.address?.story;
	};
	const trackChanges = {
		getSnapshot: () => computeState().trackChanges,
		subscribe(listener) {
			return select((state) => state.trackChanges, shallowEqual).subscribe((snapshot) => {
				try {
					listener({ snapshot });
				} catch {}
			});
		},
		accept(changeId) {
			const api = requireDocTrackChanges();
			const receipt = api.decide.call(api, {
				decision: "accept",
				target: buildChangeDecideTarget(changeId)
			});
			refreshAndNotify();
			return receipt;
		},
		reject(changeId) {
			const api = requireDocTrackChanges();
			const receipt = api.decide.call(api, {
				decision: "reject",
				target: buildChangeDecideTarget(changeId)
			});
			refreshAndNotify();
			return receipt;
		},
		acceptAll() {
			const api = requireDocTrackChanges();
			const receipt = api.decide.call(api, {
				decision: "accept",
				target: { scope: "all" }
			});
			refreshAndNotify();
			return receipt;
		},
		rejectAll() {
			const api = requireDocTrackChanges();
			const receipt = api.decide.call(api, {
				decision: "reject",
				target: { scope: "all" }
			});
			refreshAndNotify();
			return receipt;
		},
		next() {
			const items = computeState().trackChanges.items;
			if (items.length === 0) return null;
			const current = activeTrackChangeId ? items.findIndex((i) => i.id === activeTrackChangeId) : -1;
			activeTrackChangeId = items[current < 0 || current >= items.length - 1 ? 0 : current + 1].id;
			scheduleNotify();
			return activeTrackChangeId;
		},
		previous() {
			const items = computeState().trackChanges.items;
			if (items.length === 0) return null;
			const current = activeTrackChangeId ? items.findIndex((i) => i.id === activeTrackChangeId) : -1;
			activeTrackChangeId = items[current <= 0 ? items.length - 1 : current - 1].id;
			scheduleNotify();
			return activeTrackChangeId;
		},
		async scrollTo(id) {
			activeTrackChangeId = id;
			scheduleNotify();
			const story = lookupChangeStory(id);
			return runScrollIntoView({
				target: story != null ? {
					kind: "entity",
					entityType: "trackedChange",
					entityId: id,
					story
				} : {
					kind: "entity",
					entityType: "trackedChange",
					entityId: id
				},
				block: "center",
				behavior: "smooth"
			});
		}
	};
	const toViewportRect = (rect) => ({
		top: rect.top,
		left: rect.left,
		width: rect.width,
		height: rect.height,
		pageIndex: rect.pageIndex
	});
	const viewport = {
		getRect(input) {
			const target = input?.target;
			if (!target || typeof target !== "object") return {
				success: false,
				reason: "invalid-target"
			};
			const presentation = resolveHostEditor(superdoc)?.presentationEditor;
			if (!presentation || typeof presentation.getEntityRects !== "function") return {
				success: false,
				reason: "not-ready"
			};
			if (!("kind" in target) || target.kind !== "entity") return {
				success: false,
				reason: "invalid-target"
			};
			const entity = target;
			if (typeof entity.entityType !== "string" || typeof entity.entityId !== "string" || !entity.entityId) return {
				success: false,
				reason: "invalid-target"
			};
			if (entity.entityType !== "comment" && entity.entityType !== "trackedChange") return {
				success: false,
				reason: "invalid-target"
			};
			const rangeRects = presentation.getEntityRects({
				entityType: entity.entityType,
				entityId: entity.entityId,
				story: entity.story
			});
			if (!rangeRects || rangeRects.length === 0) return {
				success: false,
				reason: "not-mounted"
			};
			const rects = rangeRects.map(toViewportRect);
			return {
				success: true,
				rect: rects[0],
				rects,
				pageIndex: rects[0].pageIndex
			};
		},
		async scrollIntoView(input) {
			return runScrollIntoView(input);
		}
	};
	const selection = {
		getSnapshot: () => computeState().selection,
		subscribe(listener) {
			return select((state) => state.selection, shallowEqual).subscribe((snapshot) => {
				try {
					listener({ snapshot });
				} catch {}
			});
		},
		capture() {
			const slice = computeState().selection;
			if (!slice.target && !slice.selectionTarget) return null;
			return deepFreeze(deepClone(slice));
		}
	};
	const document = {
		getSnapshot: () => computeState().document,
		subscribe(listener) {
			return select((state) => state.document, shallowEqual).subscribe((snapshot) => {
				try {
					listener({ snapshot });
				} catch {}
			});
		},
		setMode(mode) {
			const setter = superdoc.setDocumentMode;
			if (typeof setter !== "function") return;
			try {
				setter.call(superdoc, mode);
			} catch (err) {
				console.error("[superdoc/ui] ui.document.setMode failed:", err);
			}
		},
		async export(options$1) {
			const exportFn = superdoc.export;
			if (typeof exportFn !== "function") throw new Error("ui.document.export: host SuperDoc instance does not implement export().");
			const result = await exportFn.call(superdoc, options$1);
			if (dirty) {
				dirty = false;
				scheduleNotify();
			}
			return result;
		},
		async replaceFile(file) {
			const editor = superdoc.activeEditor;
			const replace = editor?.replaceFile;
			if (typeof replace !== "function") throw new Error("ui.document.replaceFile: host has no active editor with replaceFile().");
			await replace.call(editor, file);
			if (dirty) {
				dirty = false;
				scheduleNotify();
			}
			const emit = editor.emit;
			if (typeof emit === "function") try {
				emit.call(editor, "commentsLoaded", {
					editor,
					comments: editor.converter?.comments ?? []
				});
			} catch (err) {
				console.error("[superdoc/ui] ui.document.replaceFile commentsLoaded re-emit failed:", err);
			}
		}
	};
	const destroy = () => {
		if (destroyed) return;
		destroyed = true;
		stateChangeListeners.clear();
		commandHandleCache.clear();
		commandSubscribableCache.clear();
		dynamicHandleCache.clear();
		teardown.forEach((fn) => {
			try {
				fn();
			} catch {}
		});
		teardown.length = 0;
	};
	return {
		select,
		toolbar,
		commands,
		comments,
		trackChanges,
		selection,
		viewport,
		document,
		destroy
	};
}
export { headlessToolbarConstants as i, shallowEqual as n, headlessToolbarHelpers as r, createSuperDocUI as t };
