import { onScopeDispose, toValue, watch } from "vue";
import EditorWorker from "monaco-editor/esm/vs/editor/editor.worker.js?worker";
import JsonWorker from "monaco-editor/esm/vs/language/json/json.worker.js?worker";
import YamlWorker from "monaco-yaml/yaml.worker.js?worker";
import * as monaco from "monaco-editor";
import { z } from "zod";
import "monaco-editor/esm/vs/editor/contrib/folding/browser/folding.css";
import "monaco-editor/esm/vs/base/browser/ui/codicons/codicon/codicon.css";
import { parseJsonPointerSegments } from "@scalar/helpers/json/parse-json-pointer-segments";
//#region src/v2/features/editor/helpers/ensure-monaco-environment.ts
/**
* This function ensures that the Monaco Editor's global environment is configured
* to provide the correct web worker for each language (json, yaml, or the default editor).
*/
var ensureMonacoEnvironment = () => {
	globalThis.MonacoEnvironment ??= { getWorker(_workerId, label) {
		switch (label) {
			case "json": return new JsonWorker();
			case "yaml": return new YamlWorker();
			default: return new EditorWorker();
		}
	} };
};
//#endregion
//#region src/v2/features/editor/helpers/theme/load-css-variables.ts
/**
* Extracts CSS custom properties (variables) from a given CSS string
* for .light-mode and .dark-mode selectors and returns an object
* with 'light' and 'dark' keys containing the filtered variables.
* Only variables matching hex color values (#RRGGBB or #RRGGBBAA) are accepted.
*
* @param css - The CSS string to parse.
* @returns An object with `light` and `dark` properties containing the extracted CSS variables.
*/
var loadCssVariables = async (css) => {
	const sheet = new CSSStyleSheet();
	await sheet.replace(css);
	return Array.from(sheet.cssRules).reduce((acc, cur) => {
		if (!(cur instanceof CSSStyleRule)) return acc;
		const styles = Object.values(cur.style).reduce((style, name) => {
			if (!name.startsWith("--")) return style;
			const value = z.string().regex(/^(?:#(?:[0-9A-F]{6}|[0-9A-F]{8}))$/i).safeParse(cur.style.getPropertyValue(name).toUpperCase().trim());
			if (value.error) return style;
			style[name] = value.data;
			return style;
		}, {});
		if (cur.selectorText.includes(".light-mode")) acc.light = {
			...acc.light,
			...styles
		};
		if (cur.selectorText.includes(".dark-mode")) acc.dark = {
			...acc.dark,
			...styles
		};
		return acc;
	}, {
		light: {},
		dark: {}
	});
};
//#endregion
//#region src/v2/features/editor/helpers/theme/apply-scalar-theme.ts
var THEME_NAME = "scalar-theme";
/**
* Applies a custom Scalar theme to the Monaco editor.
*
* This function loads CSS variables for either dark or light mode, maps the colors to Monaco's editor theme keys,
* and then applies the new theme. Theme color tokens are resolved from CSS custom properties.
*
* Example usage:
*
*   await applyScalarTheme('scalar', true);  // Apply dark mode
*   await applyScalarTheme('scalar', false); // Apply light mode
*
* @param theme - The theme string key to load variables for (e.g. 'scalar').
* @param isDarkMode - Whether to use dark or light variables.
*/
var applyScalarTheme = async (theme, isDarkMode) => {
	const allVars = await loadCssVariables(theme);
	const vars = isDarkMode ? allVars.dark : allVars.light;
	/**
	* Build a colors object for Monaco from the variable map.
	* Only assign if the variable exists in the loaded vars.
	*/
	const colors = Object.fromEntries(Object.entries({
		"editor.background": "--scalar-background-1",
		"editor.foreground": "--scalar-color-1",
		"editorLineNumber.foreground": "--scalar-color-3",
		"editorLineNumber.activeForeground": "--scalar-color-1",
		"editorLineHighlight.background": "--scalar-background-2",
		"editorCursor.foreground": "--scalar-color-1",
		"editorCursor.background": "--scalar-background-1",
		"editor.selectionBackground": "--scalar-background-3",
		"editor.inactiveSelectionBackground": "--scalar-background-3",
		"editorIndentGuide.background": "--scalar-background-3",
		"editorIndentGuide.activeBackground": "--scalar-background-2",
		"editorWhitespace.foreground": "--scalar-border-color",
		"editorBracketMatch.background": "--scalar-background-3",
		"editorBracketMatch.border": "--scalar-color-accent",
		"editor.selectionHighlightBackground": "--scalar-background-3",
		"editor.hoverHighlightBackground": "--scalar-background-3",
		"editorLink.activeForeground": "--scalar-color-3",
		"editorOverviewRuler.border": "--scalar-border-color"
	}).filter(([_, cssVar]) => !!vars[cssVar]).map(([prop, cssVar]) => [prop, vars[cssVar]]));
	monaco.editor.defineTheme(THEME_NAME, {
		base: isDarkMode ? "vs-dark" : "vs",
		inherit: true,
		rules: [
			{
				token: "",
				foreground: vars["--scalar-color-3"]
			},
			{
				token: "comment",
				foreground: vars["--scalar-color-2"],
				fontStyle: "italic"
			},
			{
				token: "keyword",
				foreground: vars["--scalar-color-accent"],
				fontStyle: "bold"
			},
			{
				token: "number",
				foreground: vars["--scalar-color-purple"]
			},
			{
				token: "string",
				foreground: vars["--scalar-color-2"]
			},
			{
				token: "delimiter",
				foreground: vars["--scalar-color-3"]
			}
		],
		colors
	});
	monaco.editor.setTheme(THEME_NAME);
};
//#endregion
//#region src/v2/features/editor/config.ts
/**
* Configures the Monaco editor with custom Scala theme settings.
* Applies the theme whenever the theme or dark mode value changes.
* In development mode, ensures Monaco environment is configured (worker setup handled by Vite config).
*/
var useMonacoEditorConfiguration = ({ theme, darkMode }) => {
	ensureMonacoEnvironment();
	watch([() => toValue(theme), () => toValue(darkMode)], ([theme, darkMode]) => applyScalarTheme(theme, darkMode), { immediate: true });
};
//#endregion
//#region src/v2/features/editor/helpers/range-to-whole-line.ts
/**
* Expands a given range to cover whole lines within a Monaco editor model.
*
* Given a text model and an input range, this function calculates the smallest
* and largest lines touched by the range and returns a new range that starts at
* the beginning of the first touched line and ends at the end of the last touched line.
*
* @param model The Monaco editor text model.
* @param range The Monaco Range to expand.
* @returns A new Monaco Range covering the whole lines spanned by the given range.
*/
var rangeToWholeLine = (model, range) => {
	const startOffset = model.getOffsetAt(range.getStartPosition());
	const endOffset = model.getOffsetAt(range.getEndPosition());
	const start = model.getPositionAt(startOffset);
	const inclusiveEndOffset = Math.max(startOffset, endOffset - 1);
	const end = model.getPositionAt(inclusiveEndOffset);
	const startLine = Math.max(1, start.lineNumber);
	const endLine = Math.max(startLine, end.lineNumber);
	return new monaco.Range(startLine, 1, endLine, model.getLineMaxColumn(endLine));
};
//#endregion
//#region src/v2/features/editor/hooks/use-editor.ts
var HIGHLIGHT_DECORATION_CLASS = "json-focus-highlight";
/**
* Creates and manages a Monaco editor instance.
*
* @param element - The HTML element to mount the editor into.
* @param onChange - A callback function to handle changes to the editor's value.
* @param actions - An array of actions to add to the editor.
* @param readOnly - Whether the editor should be read-only.
* @param model - The editor model to use.
* @returns An object containing the editor instance, model, and utility functions.
*/
var useEditor = ({ element, onChange, actions, readOnly = false, model }) => {
	const currentModel = () => toValue(model);
	const editor = monaco.editor.create(element, {
		model: currentModel().model,
		automaticLayout: true,
		folding: true,
		showFoldingControls: "always",
		glyphMargin: true,
		lineNumbers: "on",
		minimap: { enabled: false },
		overviewRulerLanes: 0,
		readOnly: toValue(readOnly),
		scrollbar: {
			useShadows: false,
			verticalScrollbarSize: 5
		},
		scrollBeyondLastLine: false,
		guides: { indentation: true },
		formatOnPaste: true,
		renderValidationDecorations: "on",
		formatOnType: true,
		lineHeight: 20,
		renderLineHighlight: "none",
		fontFamily: `'JetBrains Mono', monospace`
	});
	editor.updateOptions({
		insertSpaces: true,
		tabSize: 2
	});
	watch(model, (newModel) => {
		editor.setModel(toValue(newModel).model);
	});
	let highlightDecorations = [];
	let suppressedChangeEvents = 0;
	actions?.forEach((action) => {
		editor.addAction({
			id: action.id,
			label: action.label,
			keybindings: action.keybindings,
			run: async () => {
				await action.run();
			}
		});
	});
	const runEditorAction = async (actionId) => {
		await editor.getAction(actionId)?.run();
	};
	/**
	* Highlights the range at the given path using the model's getRangeFromPath.
	* Replaces any previous path highlight.
	*/
	const highlightPath = async (path) => {
		const range = await Promise.resolve(currentModel().getRangeFromPath(path));
		if (!range) return;
		highlightDecorations = editor.deltaDecorations(highlightDecorations, [{
			range,
			options: { className: HIGHLIGHT_DECORATION_CLASS }
		}]);
	};
	/**
	* Focuses the editor on the range at the given path: folds all, unfolds the target,
	* reveals it near the top, and highlights it using the model's getRangeFromPath.
	*/
	const focusPath = async (path) => {
		const range = await Promise.resolve(currentModel().getRangeFromPath(path));
		if (!range) return;
		const monacoModel = currentModel().model;
		const unfoldRange = new monaco.Range(range.startLineNumber, 1, range.endLineNumber, monacoModel.getLineMaxColumn(range.endLineNumber));
		await runEditorAction("editor.foldAll");
		editor.setSelection(unfoldRange);
		await runEditorAction("editor.unfoldRecursively");
		const position = {
			lineNumber: range.startLineNumber,
			column: range.startColumn
		};
		editor.setPosition(position);
		editor.revealPositionNearTop(position);
		await highlightPath(path);
	};
	editor.onDidChangeModelContent(() => {
		if (suppressedChangeEvents > 0) {
			suppressedChangeEvents -= 1;
			return;
		}
		const newValue = editor.getValue();
		if (typeof newValue !== "string") return;
		onChange?.(newValue ?? "");
	});
	watch(() => toValue(readOnly), (nextReadOnly) => {
		editor?.updateOptions({ readOnly: nextReadOnly });
	});
	const formatDocument = async () => {
		await runEditorAction("editor.action.formatDocument");
	};
	const getValue = () => editor.getValue();
	const setValue = (nextValue, isProgrammaticUpdate = false) => {
		if (isProgrammaticUpdate) suppressedChangeEvents += 1;
		editor.setValue(nextValue);
	};
	const hasTextFocus = () => editor.hasTextFocus();
	const dispose = () => {
		editor.dispose();
	};
	return {
		editor,
		model,
		highlightPath,
		focusPath,
		formatDocument,
		getValue,
		setValue,
		hasTextFocus,
		dispose
	};
};
//#endregion
//#region src/v2/features/editor/helpers/json/json-pointer-path.ts
var extractPointer = (input) => {
	const trimmed = input.trim();
	if (trimmed.startsWith("/")) return trimmed;
	if (trimmed.startsWith("#")) {
		const fragment = trimmed.slice(1);
		return fragment.startsWith("/") ? fragment : null;
	}
	const hashIndex = trimmed.indexOf("#");
	if (hashIndex === -1) return null;
	const fragment = trimmed.slice(hashIndex + 1);
	return fragment.startsWith("/") ? fragment : null;
};
/**
* Parses a JSON Pointer (RFC 6901) and returns a "JSON path" compatible with this editor.
* Supports raw pointers ("/paths/...") and URI fragments ("#/paths/...").
*/
var parseJsonPointerPath = (pointer) => {
	const extracted = extractPointer(pointer);
	if (!extracted) return null;
	return parseJsonPointerSegments(extracted);
};
//#endregion
//#region src/v2/features/editor/helpers/json/json-pointer-links.ts
var JSON_POINTER_LINK_SCHEME = "scalar-json-pointer";
/**
* Checks if the quote at the given index in the text is an unescaped double quote.
* An unescaped quote is one not preceded by an odd number of backslashes.
*
* @param text - The text string to check within.
* @param index - The index of the character in text to check.
* @returns True if the quote is unescaped, false otherwise.
*/
var isUnescapedQuote = (text, index) => {
	if (text[index] !== "\"") return false;
	let backslashes = 0;
	for (let i = index - 1; i >= 0 && text[i] === "\\"; i--) backslashes++;
	return backslashes % 2 === 0;
};
/**
* Finds the bounds (indices) of the nearest quoted string around a given index on the same line.
* Only considers unescaped double quotes (").
* Returns the left (opening) and right (closing) quote indices if both are found, or null otherwise.
*
* @param line - The string line in which to search.
* @param index - The index around which to search for the quoted string.
* @returns An object with `left` and `right` indices, or null if not found.
*/
var findQuotedStringBounds = (line, index) => {
	let left = -1;
	for (let i = index; i >= 0; i--) if (isUnescapedQuote(line, i)) {
		left = i;
		break;
	}
	if (left === -1) return null;
	let right = -1;
	for (let i = Math.max(left + 1, index); i < line.length; i++) if (isUnescapedQuote(line, i)) {
		right = i;
		break;
	}
	if (right === -1 || right <= left) return null;
	return {
		left,
		right
	};
};
/**
* Enables clickable JSON Pointer links in Monaco JSON editor.
*
* - Scans for "#/" inside JSON string values, but only within properly quoted, unescaped strings.
* - When a valid JSON Pointer is found, highlights it (excluding quotes) as a clickable link.
* - Clicking the link invokes the provided `navigate` function with the decoded pointer.
*
* @param navigate Callback to execute on pointer link click.
*/
var ensureJsonPointerLinkSupport = (navigate) => {
	monaco.languages.registerLinkProvider("json", { provideLinks(textModel) {
		const links = [];
		const lineCount = textModel.getLineCount();
		for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
			const line = textModel.getLineContent(lineNumber);
			let fromIndex = 0;
			while (true) {
				const idx = line.indexOf("#/", fromIndex);
				if (idx === -1) break;
				const bounds = findQuotedStringBounds(line, idx);
				if (!bounds) {
					fromIndex = idx + 2;
					continue;
				}
				const value = line.slice(bounds.left + 1, bounds.right);
				if (!parseJsonPointerPath(value)) {
					fromIndex = bounds.right + 1;
					continue;
				}
				const range = new monaco.Range(lineNumber, bounds.left + 2, lineNumber, bounds.right + 1);
				links.push({
					range,
					url: monaco.Uri.parse(`${JSON_POINTER_LINK_SCHEME}:${encodeURIComponent(value)}`),
					tooltip: "Follow link"
				});
				fromIndex = bounds.right + 1;
			}
		}
		return { links };
	} });
	const linkOpener = monaco.editor.registerLinkOpener({ open(resource) {
		if (resource.scheme !== JSON_POINTER_LINK_SCHEME) return false;
		const encoded = (resource.path || "").replace(/^\//, "");
		const pointer = decodeURIComponent(encoded);
		if (!pointer) return false;
		return Promise.resolve(navigate(pointer)).then(() => true);
	} });
	const dispose = () => {
		linkOpener.dispose();
	};
	return {
		linkOpener,
		dispose
	};
};
//#endregion
//#region src/v2/features/editor/hooks/use-json-pointer-link-support.ts
var JSON_LANGUAGE_ID = "json";
/**
* Enables clickable JSON Pointer links when the editor model is JSON.
* Relates link support to the current model and uses the provided editor API to navigate.
*
* Call this hook with the return value of useEditor (or a ref to it) and the same model
* passed to useEditor. When the model's language is JSON, links like "#/paths/..." in
* string values become clickable and trigger focusPath on the editor.
*/
var useJsonPointerLinkSupport = (editorApi, model) => {
	let disposeLinkSupport = null;
	const setupOrDispose = () => {
		const api = toValue(editorApi);
		const currentModel = toValue(model);
		if (disposeLinkSupport) {
			disposeLinkSupport();
			disposeLinkSupport = null;
		}
		if (!api?.focusPath || !currentModel) return;
		if (currentModel.model.getLanguageId() !== JSON_LANGUAGE_ID) return;
		const navigate = async (pointer) => {
			const path = parseJsonPointerPath(pointer);
			if (!path) return;
			await api.focusPath(path.map(String));
		};
		disposeLinkSupport = ensureJsonPointerLinkSupport(navigate).dispose;
	};
	watch(() => [toValue(editorApi), toValue(model)], () => {
		setupOrDispose();
	}, { immediate: true });
	onScopeDispose(() => {
		if (disposeLinkSupport) {
			disposeLinkSupport();
			disposeLinkSupport = null;
		}
	});
};
//#endregion
export { rangeToWholeLine as a, useEditor as i, ensureJsonPointerLinkSupport as n, useMonacoEditorConfiguration as o, parseJsonPointerPath as r, useJsonPointerLinkSupport as t };

//# sourceMappingURL=editor-BZ_J0eze.js.map