import { n as __exportAll } from "./chunk-B-1-B7_t.js";
import { n as cn } from "./utils-DXzSsU1e.js";
import { n as CardTitle_default, o as CardContent_default, r as CardHeader_default, s as Card_default } from "./card-X74FuL7B.js";
import { t as Skeleton_default } from "./skeleton-9Qm1V-Ys.js";
import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, h, normalizeClass, onMounted, openBlock, ref, renderList, toDisplayString, unref, watch, withCtx } from "vue";
import { ChevronDown, ChevronRight, File, Folder, FolderOpen } from "@lucide/vue";
//#region src/components/compositions/elements/code/FileTree.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = {
	key: 0,
	"data-testid": "file-tree-loading",
	class: "flex flex-col gap-1.5 px-2"
};
var _hoisted_2 = {
	key: 1,
	class: "px-2 text-sm text-destructive",
	"data-testid": "file-tree-error",
	role: "alert"
};
var _hoisted_3 = {
	key: 2,
	class: "px-2 text-sm text-muted-foreground",
	"data-testid": "file-tree-empty"
};
var _hoisted_4 = {
	key: 3,
	class: "flex flex-col",
	role: "tree",
	"data-testid": "file-tree-list"
};
var FileTree_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
	__name: "FileTree",
	props: {
		path: { default: "" },
		onLoadTree: {},
		selectedPath: {},
		onSelectPath: {},
		expandedPaths: {},
		onExpandedChange: {},
		label: { default: "File Tree" },
		name: {},
		classList: {}
	},
	emits: ["update:selectedPath", "update:expandedPaths"],
	setup(__props, { expose: __expose, emit: __emit }) {
		/**
		* Props-driven file tree composition. Renders a recursive list of files
		* and folders sourced by a `onLoadTree` callback. The composition makes
		* no HTTP calls itself — the consumer provides a Promise-returning
		* loader for each node.
		*
		*   <FileTree
		*     :on-load-tree="(path) => fetch(`/api/tree?path=${path}`).then(r => r.json())"
		*     v-model:selected-path="selected"
		*     v-model:expanded-paths="expanded"
		*   />
		*/
		const props = __props;
		const emit = __emit;
		function toInternal(node) {
			return {
				path: node.path,
				name: node.name,
				kind: node.kind,
				loading: false,
				loaded: Array.isArray(node.children),
				error: null,
				childNodes: Array.isArray(node.children) ? node.children.map(toInternal) : []
			};
		}
		const rootNodes = ref([]);
		const rootLoading = ref(false);
		const rootError = ref(null);
		const localExpanded = ref(new Set(props.expandedPaths ?? []));
		const expandedPathsList = computed(() => Array.from(localExpanded.value));
		watch(() => props.expandedPaths, (next) => {
			if (next === void 0) return;
			localExpanded.value = new Set(next);
		});
		function notifyExpanded() {
			const list = expandedPathsList.value;
			emit("update:expandedPaths", list);
			if (props.onExpandedChange) props.onExpandedChange(list);
		}
		async function loadRoot() {
			rootLoading.value = true;
			rootError.value = null;
			try {
				const result = await props.onLoadTree(props.path ?? "");
				rootNodes.value = Array.isArray(result) ? result.map(toInternal) : [];
			} catch (err) {
				rootError.value = err instanceof Error ? err.message : String(err);
				rootNodes.value = [];
			} finally {
				rootLoading.value = false;
			}
		}
		async function loadChildren(node) {
			if (node.kind !== "directory") return;
			if (node.loading || node.loaded) return;
			node.loading = true;
			node.error = null;
			try {
				const result = await props.onLoadTree(node.path);
				node.childNodes = Array.isArray(result) ? result.map(toInternal) : [];
				node.loaded = true;
			} catch (err) {
				node.error = err instanceof Error ? err.message : String(err);
				node.childNodes = [];
			} finally {
				node.loading = false;
			}
		}
		function isExpanded(path) {
			return localExpanded.value.has(path);
		}
		async function toggleDirectory(node) {
			const next = new Set(localExpanded.value);
			if (next.has(node.path)) next.delete(node.path);
			else {
				next.add(node.path);
				if (!node.loaded) await loadChildren(node);
			}
			localExpanded.value = next;
			notifyExpanded();
		}
		function selectFile(node) {
			if (node.kind !== "file") return;
			emit("update:selectedPath", node.path);
			if (props.onSelectPath) props.onSelectPath(node.path);
		}
		onMounted(() => {
			loadRoot();
		});
		watch(() => props.path, () => {
			loadRoot();
		});
		const FileTreeNode = {
			name: "FileTreeNode",
			props: {
				node: {
					type: Object,
					required: true
				},
				depth: {
					type: Number,
					required: true
				}
			},
			setup(nodeProps) {
				return () => {
					const node = nodeProps.node;
					const expanded = isExpanded(node.path);
					const indent = { paddingLeft: `${nodeProps.depth * 16 + 8}px` };
					const isFile = node.kind === "file";
					const selected = isFile && props.selectedPath === node.path;
					const row = h("div", {
						class: ["flex items-center gap-1.5 rounded-sm py-1 pr-2 text-sm cursor-pointer hover:bg-accent hover:text-accent-foreground", selected ? "bg-accent text-accent-foreground font-medium" : ""].join(" "),
						style: indent,
						role: "treeitem",
						"data-testid": `file-tree-row-${node.path}`,
						"data-kind": node.kind,
						"aria-expanded": isFile ? void 0 : String(expanded),
						"aria-selected": isFile ? String(selected) : void 0,
						onClick: (e) => {
							e.stopPropagation();
							if (isFile) selectFile(node);
							else toggleDirectory(node);
						}
					}, [
						isFile ? h("span", { class: "inline-block w-4 shrink-0" }) : h(expanded ? ChevronDown : ChevronRight, {
							class: "size-4 shrink-0 text-muted-foreground",
							"data-testid": expanded ? `file-tree-chevron-down-${node.path}` : `file-tree-chevron-right-${node.path}`
						}),
						isFile ? h(File, { class: "size-4 shrink-0 text-muted-foreground" }) : h(expanded ? FolderOpen : Folder, { class: "size-4 shrink-0 text-muted-foreground" }),
						h("span", { class: "truncate" }, node.name)
					]);
					const children = [];
					if (!isFile && expanded) {
						const childIndent = { paddingLeft: `${(nodeProps.depth + 1) * 16 + 8}px` };
						if (node.loading) children.push(h("li", {
							class: "py-1",
							style: childIndent,
							"data-testid": `file-tree-child-loading-${node.path}`
						}, [h(Skeleton_default, { class: "h-4 w-1/2" })]));
						else if (node.error) children.push(h("li", {
							class: "py-1 text-sm text-destructive",
							style: childIndent,
							role: "alert",
							"data-testid": `file-tree-child-error-${node.path}`
						}, node.error));
						else if (node.childNodes.length === 0) children.push(h("li", {
							class: "py-1 text-sm italic text-muted-foreground",
							style: childIndent,
							"data-testid": `file-tree-child-empty-${node.path}`
						}, "(empty)"));
						else for (const child of node.childNodes) children.push(h(FileTreeNode, {
							key: child.path,
							node: child,
							depth: nodeProps.depth + 1
						}));
					}
					return h("li", { class: "flex list-none flex-col" }, [row, ...children]);
				};
			}
		};
		__expose({ reload: loadRoot });
		return (_ctx, _cache) => {
			return openBlock(), createBlock(unref(Card_default), {
				class: normalizeClass(unref(cn)(props.classList)),
				"data-element-name": props.name ?? void 0,
				"data-testid": "file-tree"
			}, {
				default: withCtx(() => [createVNode(unref(CardHeader_default), null, {
					default: withCtx(() => [createVNode(unref(CardTitle_default), { class: "flex items-center gap-2 text-base" }, {
						default: withCtx(() => [createVNode(unref(Folder), { class: "size-5 text-muted-foreground" }), createElementVNode("span", null, toDisplayString(props.label), 1)]),
						_: 1
					})]),
					_: 1
				}), createVNode(unref(CardContent_default), { class: "px-2 pb-4" }, {
					default: withCtx(() => [rootLoading.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
						createVNode(unref(Skeleton_default), { class: "h-5 w-3/4" }),
						createVNode(unref(Skeleton_default), { class: "h-5 w-2/3" }),
						createVNode(unref(Skeleton_default), { class: "h-5 w-1/2" })
					])) : rootError.value ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString(rootError.value), 1)) : rootNodes.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_3, " No files. ")) : (openBlock(), createElementBlock("ul", _hoisted_4, [(openBlock(true), createElementBlock(Fragment, null, renderList(rootNodes.value, (node) => {
						return openBlock(), createBlock(FileTreeNode, {
							key: node.path,
							node,
							depth: 0
						}, null, 8, ["node"]);
					}), 128))]))]),
					_: 1
				})]),
				_: 1
			}, 8, ["class", "data-element-name"]);
		};
	}
});
//#endregion
//#region src/components/compositions/elements/code/FileTree.vue
var FileTree_exports = /* @__PURE__ */ __exportAll({ default: () => FileTree_default });
var FileTree_default = FileTree_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { FileTree_exports as n, FileTree_default as t };

//# sourceMappingURL=FileTree-DtEJLakV.js.map