{"version":3,"sources":["../src/index.ts","../src/EventEmitter.ts","../src/styles.css","../src/styles.ts","../src/ContextMenu.ts","../src/DragDrop.ts","../src/icons.ts","../src/utils.ts","../src/FileTree.ts"],"sourcesContent":["export { FileTree, defaultStrings } from \"./FileTree\";\r\nexport { createNode, normalizePath, getName, getParentPath, getExtension } from \"./utils\";\r\n\r\n/**\r\n * Icon set used internally by the tree, exposed under the `icons` namespace\r\n * so consumers can reuse the same SVG strings (e.g. for custom toolbar\r\n * buttons or context-menu items). Includes `folder`, `folderOpen`, `file`,\r\n * per-extension file badges, toolbar icons, and the `defaultIconMap` /\r\n * `defaultNameIconMap` registries.\r\n */\r\nexport * as icons from \"./icons\";\r\n\r\nexport type {\r\n  FileTreeNodeData,\r\n  FileTreeOptions,\r\n  FileTreeEvent,\r\n  FileTreeEventType,\r\n  EventHandler,\r\n  Theme,\r\n  Direction,\r\n  ToolbarOptions,\r\n  ToolbarButton,\r\n  ContextMenuOptions,\r\n  ContextMenuItem,\r\n  FileTreeStringKey,\r\n  FileTreeTranslate,\r\n} from \"./types\";\r\n","export class EventEmitter<TEvents extends Record<string, unknown>> {\r\n  private listeners = new Map<keyof TEvents, Set<(payload: never) => void>>();\r\n\r\n  on<K extends keyof TEvents>(\r\n    event: K,\r\n    handler: (payload: TEvents[K]) => void,\r\n  ): void {\r\n    if (!this.listeners.has(event)) {\r\n      this.listeners.set(event, new Set());\r\n    }\r\n    this.listeners.get(event)!.add(handler as (payload: never) => void);\r\n  }\r\n\r\n  off<K extends keyof TEvents>(\r\n    event: K,\r\n    handler: (payload: TEvents[K]) => void,\r\n  ): void {\r\n    this.listeners.get(event)?.delete(handler as (payload: never) => void);\r\n  }\r\n\r\n  emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void {\r\n    this.listeners.get(event)?.forEach((handler) => {\r\n      try {\r\n        (handler as (payload: TEvents[K]) => void)(payload);\r\n      } catch (err) {\r\n        console.error(`[file-tree] Error in \"${String(event)}\" handler:`, err);\r\n      }\r\n    });\r\n  }\r\n\r\n  removeAllListeners(): void {\r\n    this.listeners.clear();\r\n  }\r\n}\r\n","export default \"@layer ft{.ft-root{--ft-font-family: system-ui, -apple-system, \\\"Segoe UI\\\", sans-serif;--ft-font-size: 13px;--ft-line-height: 1.5;--ft-border-radius: 4px;--ft-transition: .15s ease;--ft-icon-size: 16px;--ft-row-height: 28px}.ft-root[data-theme=dark]{--ft-bg: #1e1e2e;--ft-color: #cdd6f4;--ft-color-muted: #6c7086;--ft-border-color: #313244;--ft-toolbar-bg: #181825;--ft-toolbar-border: #313244;--ft-toolbar-btn-bg: transparent;--ft-toolbar-btn-hover: #313244;--ft-toolbar-btn-color: #a6adc8;--ft-toolbar-btn-color-hover: #cdd6f4;--ft-node-hover: #313244;--ft-node-selected: #45475a;--ft-node-color: #cdd6f4;--ft-node-color-hover: #f5f5f5;--ft-arrow-color: #6c7086;--ft-arrow-color-hover: #a6adc8;--ft-context-bg: #1e1e2e;--ft-context-border: #45475a;--ft-context-color: #cdd6f4;--ft-context-hover: #313244;--ft-context-color-hover: #f5f5f5;--ft-context-shortcut: #6c7086;--ft-context-separator: #313244;--ft-context-shadow: 0 4px 16px rgba(0, 0, 0, .4);--ft-input-bg: #313244;--ft-input-color: #cdd6f4;--ft-input-border: #585b70;--ft-drop-indicator: #89b4fa;--ft-drop-bg: rgba(137, 180, 250, .1);--ft-scrollbar-thumb: #313244;--ft-scrollbar-thumb-hover: #45475a}.ft-root[data-theme=light]{--ft-bg: #f8f9fc;--ft-color: #1e293b;--ft-color-muted: #94a3b8;--ft-border-color: #e2e8f0;--ft-toolbar-bg: #f1f5f9;--ft-toolbar-border: #e2e8f0;--ft-toolbar-btn-bg: transparent;--ft-toolbar-btn-hover: #e2e8f0;--ft-toolbar-btn-color: #64748b;--ft-toolbar-btn-color-hover: #1e293b;--ft-node-hover: #e2e8f0;--ft-node-selected: #cbd5e1;--ft-node-color: #334155;--ft-node-color-hover: #0f172a;--ft-arrow-color: #94a3b8;--ft-arrow-color-hover: #64748b;--ft-context-bg: #ffffff;--ft-context-border: #e2e8f0;--ft-context-color: #334155;--ft-context-hover: #f1f5f9;--ft-context-color-hover: #0f172a;--ft-context-shortcut: #94a3b8;--ft-context-separator: #e2e8f0;--ft-context-shadow: 0 4px 16px rgba(0, 0, 0, .12);--ft-input-bg: #ffffff;--ft-input-color: #1e293b;--ft-input-border: #cbd5e1;--ft-drop-indicator: #3b82f6;--ft-drop-bg: rgba(59, 130, 246, .08);--ft-scrollbar-thumb: #cbd5e1;--ft-scrollbar-thumb-hover: #94a3b8}.ft-root{background:var(--ft-bg);border-radius:var(--ft-border-radius);color:var(--ft-color);display:flex;flex-direction:column;font-family:var(--ft-font-family);font-size:var(--ft-font-size);height:100%;line-height:var(--ft-line-height);outline:none;overflow:hidden;position:relative;user-select:none;width:100%}.ft-root *::-webkit-scrollbar{width:6px}.ft-root *::-webkit-scrollbar-track{background:transparent}.ft-root *::-webkit-scrollbar-thumb{background:var(--ft-scrollbar-thumb);border-radius:3px}.ft-root *::-webkit-scrollbar-thumb:hover{background:var(--ft-scrollbar-thumb-hover)}@supports (scrollbar-width: auto){.ft-root *{scrollbar-color:var(--ft-scrollbar-thumb) transparent;scrollbar-width:thin}}.ft-toolbar{align-items:center;background:var(--ft-toolbar-bg);border-bottom:1px solid var(--ft-toolbar-border);display:flex;flex-shrink:0;gap:2px;justify-content:end;min-height:32px;padding:2px 4px}.ft-toolbar__btn{align-items:center;background:var(--ft-toolbar-btn-bg);border:none;border-radius:var(--ft-border-radius);color:var(--ft-toolbar-btn-color);cursor:pointer;display:inline-flex;height:26px;justify-content:center;min-width:26px;padding:4px;transition:background var(--ft-transition),color var(--ft-transition)}.ft-toolbar__btn:hover{background:var(--ft-toolbar-btn-hover);color:var(--ft-toolbar-btn-color-hover)}.ft-toolbar__btn svg{height:var(--ft-icon-size);width:var(--ft-icon-size)}.ft-tree{flex:1;overflow-x:hidden;overflow-y:auto;padding:4px 0;position:relative}.ft-node{position:relative}.ft-node--dragging{opacity:.4}.ft-node__content{align-items:center;border-radius:var(--ft-border-radius);color:var(--ft-node-color);cursor:pointer;display:flex;gap:4px;height:var(--ft-row-height);margin:0 4px;padding-inline-end:8px;transition:background var(--ft-transition),color var(--ft-transition);white-space:nowrap}.ft-node__content:hover{background:var(--ft-node-hover);color:var(--ft-node-color-hover)}.ft-node__content--selected,.ft-node__content--selected:hover{background:var(--ft-node-selected)}.ft-node__content--focused{outline:1px solid var(--ft-drop-indicator);outline-offset:-1px}.ft-node__content--cut{opacity:.5}.ft-node__content--drop-inside{background:var(--ft-drop-bg);outline:1px solid var(--ft-drop-indicator);outline-offset:-1px}.ft-node__content--drop-before,.ft-node__content--drop-after{background:transparent}.ft-tree--drop-root{box-shadow:inset 0 0 0 2px var(--ft-drop-indicator);border-radius:var(--ft-border-radius)}.ft-drop-indicator{background:var(--ft-drop-indicator);border-radius:1px;height:2px;left:0;pointer-events:none;position:absolute;z-index:10}.ft-node__arrow{align-items:center;color:var(--ft-arrow-color);display:inline-flex;flex-shrink:0;height:16px;justify-content:center;transition:transform .15s ease,color var(--ft-transition);width:16px}.ft-node__arrow:hover{color:var(--ft-arrow-color-hover)}.ft-node__arrow svg{height:12px;width:12px}.ft-node__arrow--open{transform:rotate(90deg)}.ft-root[dir=rtl] .ft-node__arrow{transform:rotate(180deg)}.ft-root[dir=rtl] .ft-node__arrow--open{transform:rotate(90deg)}.ft-node__arrow-spacer{display:inline-flex;flex-shrink:0;height:16px;width:16px}.ft-node__icon{align-items:center;display:inline-flex;flex-shrink:0;height:var(--ft-icon-size);justify-content:center;width:var(--ft-icon-size)}.ft-node__icon svg{height:var(--ft-icon-size);width:var(--ft-icon-size)}.ft-node__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}.ft-rename-input{background:var(--ft-input-bg);border:1px solid var(--ft-input-border);border-radius:var(--ft-border-radius);box-sizing:border-box;color:var(--ft-input-color);flex:1;font-family:inherit;font-size:inherit;height:22px;line-height:22px;min-width:0;outline:none;padding:0 4px;width:100%}.ft-rename-input:focus{border-color:var(--ft-drop-indicator);box-shadow:0 0 0 1px var(--ft-drop-indicator)}.ft-context-menu{background:var(--ft-context-bg);border:1px solid var(--ft-context-border);border-radius:6px;box-shadow:var(--ft-context-shadow);color:var(--ft-context-color);min-width:180px;overflow:hidden;padding:4px;position:absolute;z-index:1000}.ft-context-menu__item{align-items:center;cursor:pointer;display:flex;gap:8px;height:30px;padding:0 8px;transition:background var(--ft-transition),color var(--ft-transition)}.ft-context-menu__item:hover,.ft-context-menu__item--focused{background:var(--ft-context-hover);color:var(--ft-context-color-hover)}.ft-context-menu__item--disabled{cursor:default;opacity:.4}.ft-context-menu__item--disabled:hover{background:transparent}.ft-context-menu__icon{align-items:center;display:inline-flex;flex-shrink:0;height:16px;justify-content:center;opacity:.75;width:16px}.ft-context-menu__icon svg{height:14px;width:14px}.ft-context-menu__label{flex:1;font-size:13px}.ft-context-menu__shortcut{color:var(--ft-context-shortcut);font-size:11px;margin-inline-start:16px}.ft-context-menu__separator{background:var(--ft-context-separator);height:1px;margin:4px 8px}.ft-root[dir=rtl] .ft-context-menu{text-align:right}.ft-tree:empty:after{color:var(--ft-color-muted);content:\\\"No files\\\";display:block;font-size:12px;padding:16px;text-align:center}}\\n\";","import css from \"./styles.css\";\n\nconst STYLE_ID = \"ft-styles\";\n\n/**\n * Inject the library stylesheet into `document.head` once.\n * Safe to call multiple times across instances or bundle copies.\n */\nexport function injectStyles(): void {\n  if (typeof document === \"undefined\") return;\n  if (document.getElementById(STYLE_ID)) return;\n\n  const style = document.createElement(\"style\");\n  style.id = STYLE_ID;\n  style.textContent = css;\n  document.head.appendChild(style);\n}\n","export interface ContextMenuEntry {\r\n  id: string;\r\n  label: string;\r\n  icon?: string;\r\n  shortcut?: string;\r\n  separator?: boolean;\r\n  disabled?: boolean;\r\n  onClick: () => void;\r\n}\r\n\r\nexport class ContextMenu {\r\n  private el: HTMLElement;\r\n  private isOpen = false;\r\n  private boundClose: (e: MouseEvent) => void;\r\n  private boundKeydown: (e: KeyboardEvent) => void;\r\n  private focusedIndex = -1;\r\n  private items: HTMLElement[] = [];\r\n\r\n  constructor(private container: HTMLElement) {\r\n    this.el = document.createElement(\"div\");\r\n    this.el.className = \"ft-context-menu\";\r\n    this.el.setAttribute(\"role\", \"menu\");\r\n    this.el.style.display = \"none\";\r\n    this.container.appendChild(this.el);\r\n\r\n    this.boundClose = (e: MouseEvent) => {\r\n      if (!this.el.contains(e.target as Node)) {\r\n        this.hide();\r\n      }\r\n    };\r\n\r\n    this.boundKeydown = (e: KeyboardEvent) => {\r\n      if (!this.isOpen) return;\r\n      if (e.key === \"Escape\") {\r\n        e.preventDefault();\r\n        this.hide();\r\n      } else if (e.key === \"ArrowDown\") {\r\n        e.preventDefault();\r\n        this.moveFocus(1);\r\n      } else if (e.key === \"ArrowUp\") {\r\n        e.preventDefault();\r\n        this.moveFocus(-1);\r\n      } else if (e.key === \"Enter\") {\r\n        e.preventDefault();\r\n        if (this.focusedIndex >= 0 && this.items[this.focusedIndex]) {\r\n          this.items[this.focusedIndex].click();\r\n        }\r\n      }\r\n    };\r\n  }\r\n\r\n  show(x: number, y: number, entries: ContextMenuEntry[]): void {\r\n    this.el.innerHTML = \"\";\r\n    this.items = [];\r\n    this.focusedIndex = -1;\r\n\r\n    for (const entry of entries) {\r\n      if (entry.separator) {\r\n        const sep = document.createElement(\"div\");\r\n        sep.className = \"ft-context-menu__separator\";\r\n        sep.setAttribute(\"role\", \"separator\");\r\n        this.el.appendChild(sep);\r\n        continue;\r\n      }\r\n\r\n      const item = document.createElement(\"div\");\r\n      item.className = \"ft-context-menu__item\";\r\n      if (entry.disabled) item.classList.add(\"ft-context-menu__item--disabled\");\r\n      item.setAttribute(\"role\", \"menuitem\");\r\n      item.tabIndex = -1;\r\n\r\n      let html = \"\";\r\n      if (entry.icon) {\r\n        html += `<span class=\"ft-context-menu__icon\">${entry.icon}</span>`;\r\n      }\r\n      html += `<span class=\"ft-context-menu__label\">${entry.label}</span>`;\r\n      if (entry.shortcut) {\r\n        html += `<span class=\"ft-context-menu__shortcut\">${entry.shortcut}</span>`;\r\n      }\r\n      item.innerHTML = html;\r\n\r\n      if (!entry.disabled) {\r\n        item.addEventListener(\"click\", (e) => {\r\n          e.stopPropagation();\r\n          this.hide();\r\n          entry.onClick();\r\n        });\r\n      }\r\n\r\n      this.el.appendChild(item);\r\n      this.items.push(item);\r\n    }\r\n\r\n    // Position: prevent overflow\r\n    this.el.style.display = \"block\";\r\n    this.el.style.left = `${x}px`;\r\n    this.el.style.top = `${y}px`;\r\n\r\n    requestAnimationFrame(() => {\r\n      const rect = this.el.getBoundingClientRect();\r\n      const containerRect = this.container.getBoundingClientRect();\r\n\r\n      if (rect.right > containerRect.right) {\r\n        this.el.style.left = `${x - rect.width}px`;\r\n      }\r\n      if (rect.bottom > containerRect.bottom) {\r\n        this.el.style.top = `${Math.max(0, y - rect.height)}px`;\r\n      }\r\n\r\n      // Clamp within the container: the flip above can push the menu past\r\n      // the left edge, which `overflow: hidden` on the root would clip.\r\n      const left = Math.max(\r\n        containerRect.left,\r\n        Math.min(\r\n          containerRect.right - rect.width,\r\n          this.el.getBoundingClientRect().left,\r\n        ),\r\n      );\r\n      this.el.style.left = `${left - containerRect.left}px`;\r\n    });\r\n\r\n    this.isOpen = true;\r\n    document.addEventListener(\"mousedown\", this.boundClose, true);\r\n    document.addEventListener(\"keydown\", this.boundKeydown, true);\r\n  }\r\n\r\n  hide(): void {\r\n    if (!this.isOpen) return;\r\n    this.el.style.display = \"none\";\r\n    this.el.innerHTML = \"\";\r\n    this.isOpen = false;\r\n    this.items = [];\r\n    this.focusedIndex = -1;\r\n    document.removeEventListener(\"mousedown\", this.boundClose, true);\r\n    document.removeEventListener(\"keydown\", this.boundKeydown, true);\r\n  }\r\n\r\n  get visible(): boolean {\r\n    return this.isOpen;\r\n  }\r\n\r\n  private moveFocus(delta: number): void {\r\n    if (this.items.length === 0) return;\r\n    this.focusedIndex =\r\n      (this.focusedIndex + delta + this.items.length) % this.items.length;\r\n    this.items.forEach((it, i) => {\r\n      it.classList.toggle(\r\n        \"ft-context-menu__item--focused\",\r\n        i === this.focusedIndex,\r\n      );\r\n    });\r\n    this.items[this.focusedIndex]?.focus();\r\n  }\r\n\r\n  destroy(): void {\r\n    this.hide();\r\n    this.el.remove();\r\n  }\r\n}\r\n","import type { InternalNode } from \"./types\";\r\n\r\nexport type DropPosition = \"before\" | \"inside\" | \"after\";\r\n\r\nexport interface DragDropCallbacks {\r\n  getNode: (path: string) => InternalNode | undefined;\r\n  /**\r\n   * Return the paths that should be dragged together when a node is\r\n   * dragged (e.g. the full selection, including the dragged node).\r\n   */\r\n  getDragPaths: (path: string) => string[];\r\n  onMove: (\r\n    sourcePaths: string[],\r\n    targetPath: string,\r\n    position: DropPosition,\r\n  ) => void;\r\n  onExternalDrop: (\r\n    entries: { files: FileList; items: DataTransferItemList },\r\n    targetPath: string | null,\r\n    position: DropPosition,\r\n  ) => void;\r\n}\r\n\r\nexport class DragDrop {\r\n  private draggedPaths: string[] = [];\r\n  private currentDropTarget: HTMLElement | null = null;\r\n  /** Whether the current drop target is the tree's empty space (root). */\r\n  private rootDrop = false;\r\n  private dropIndicator: HTMLElement;\r\n  private dropPosition: DropPosition = \"inside\";\r\n  private treeEl: HTMLElement;\r\n\r\n  constructor(\r\n    treeEl: HTMLElement,\r\n    private callbacks: DragDropCallbacks,\r\n  ) {\r\n    this.treeEl = treeEl;\r\n\r\n    this.dropIndicator = document.createElement(\"div\");\r\n    this.dropIndicator.className = \"ft-drop-indicator\";\r\n    this.dropIndicator.style.display = \"none\";\r\n    treeEl.appendChild(this.dropIndicator);\r\n\r\n    this.onDragStart = this.onDragStart.bind(this);\r\n    this.onDragOver = this.onDragOver.bind(this);\r\n    this.onDragLeave = this.onDragLeave.bind(this);\r\n    this.onDrop = this.onDrop.bind(this);\r\n    this.onDragEnd = this.onDragEnd.bind(this);\r\n\r\n    treeEl.addEventListener(\"dragstart\", this.onDragStart);\r\n    treeEl.addEventListener(\"dragover\", this.onDragOver);\r\n    treeEl.addEventListener(\"dragleave\", this.onDragLeave);\r\n    treeEl.addEventListener(\"drop\", this.onDrop);\r\n    treeEl.addEventListener(\"dragend\", this.onDragEnd);\r\n  }\r\n\r\n  private onDragStart(e: DragEvent): void {\r\n    const nodeEl = (e.target as HTMLElement).closest(\r\n      \".ft-node\",\r\n    ) as HTMLElement | null;\r\n    if (!nodeEl) return;\r\n    const path = nodeEl.dataset.path;\r\n    if (!path) return;\r\n\r\n    this.draggedPaths = this.callbacks.getDragPaths(path);\r\n    e.dataTransfer!.effectAllowed = \"move\";\r\n    e.dataTransfer!.setData(\"text/plain\", this.draggedPaths.join(\"\\n\"));\r\n    // Mark all dragged nodes (fade via rAF so the drag image isn't affected)\r\n    for (const p of this.draggedPaths) {\r\n      this.callbacks.getNode(p)?.el.classList.add(\"ft-node--dragging\");\r\n    }\r\n    requestAnimationFrame(() => {\r\n      for (const p of this.draggedPaths) {\r\n        this.callbacks.getNode(p)?.el.style.setProperty(\"opacity\", \"0.4\");\r\n      }\r\n    });\r\n  }\r\n\r\n  private onDragOver(e: DragEvent): void {\r\n    e.preventDefault();\r\n    e.dataTransfer!.dropEffect = \"move\";\r\n\r\n    const contentEl = (e.target as HTMLElement).closest(\r\n      \".ft-node__content\",\r\n    ) as HTMLElement | null;\r\n    if (!contentEl) {\r\n      // Over empty space: if it's within an expanded folder's children\r\n      // area, treat it as a drop inside that folder; otherwise drop on\r\n      // the tree root.\r\n      const childrenEl = (e.target as HTMLElement).closest(\r\n        \".ft-node__children\",\r\n      ) as HTMLElement | null;\r\n      const folderEl = childrenEl?.closest(\".ft-node\") as HTMLElement | null;\r\n      const folderPath = folderEl?.dataset.path;\r\n      const folderContentEl = folderEl?.querySelector(\r\n        \":scope > .ft-node__content\",\r\n      ) as HTMLElement | null;\r\n      if (folderPath && folderContentEl) {\r\n        // Don't drop on any dragged node or into its descendants.\r\n        if (this.draggedPaths.includes(folderPath)) {\r\n          this.clearDropTarget();\r\n          return;\r\n        }\r\n        if (this.draggedPaths.some((s) => folderPath.startsWith(s + \"/\"))) {\r\n          this.clearDropTarget();\r\n          return;\r\n        }\r\n        this.setDropTarget(\r\n          folderContentEl,\r\n          \"inside\",\r\n          folderContentEl.getBoundingClientRect(),\r\n        );\r\n        return;\r\n      }\r\n      this.setRootDrop();\r\n      return;\r\n    }\r\n\r\n    const nodeEl = contentEl.closest(\".ft-node\") as HTMLElement;\r\n    const path = nodeEl?.dataset.path;\r\n    if (!path) return;\r\n\r\n    const nodeData = this.callbacks.getNode(path);\r\n    if (!nodeData) return;\r\n\r\n    // A file row nested inside a folder's children always drops into that\r\n    // folder: the whole row highlights the parent instead of showing\r\n    // before/after zones on the file itself.\r\n    if (nodeData.data.type === \"file\") {\r\n      const parentFolderEl = nodeEl.parentElement?.closest(\r\n        \".ft-node\",\r\n      ) as HTMLElement | null;\r\n      const parentPath = parentFolderEl?.dataset.path;\r\n      const parentContentEl = parentFolderEl?.querySelector(\r\n        \":scope > .ft-node__content\",\r\n      ) as HTMLElement | null;\r\n      if (parentPath && parentContentEl) {\r\n        // Don't drop into a dragged folder or into a dragged node's\r\n        // descendant.\r\n        if (this.draggedPaths.includes(parentPath)) {\r\n          this.clearDropTarget();\r\n          return;\r\n        }\r\n        if (this.draggedPaths.some((s) => parentPath.startsWith(s + \"/\"))) {\r\n          this.clearDropTarget();\r\n          return;\r\n        }\r\n        this.setDropTarget(\r\n          parentContentEl,\r\n          \"inside\",\r\n          parentContentEl.getBoundingClientRect(),\r\n        );\r\n        return;\r\n      }\r\n    }\r\n\r\n    // Don't drop on any dragged node or into its descendants.\r\n    if (this.draggedPaths.includes(path)) {\r\n      this.clearDropTarget();\r\n      return;\r\n    }\r\n    if (this.draggedPaths.some((s) => path.startsWith(s + \"/\"))) {\r\n      this.clearDropTarget();\r\n      return;\r\n    }\r\n\r\n    const rect = contentEl.getBoundingClientRect();\r\n    const y = e.clientY - rect.top;\r\n    const ratio = y / rect.height;\r\n\r\n    let position: DropPosition;\r\n    if (nodeData.data.type === \"folder\") {\r\n      if (ratio < 0.25) position = \"before\";\r\n      else if (ratio > 0.75) position = \"after\";\r\n      else position = \"inside\";\r\n    } else {\r\n      position = ratio < 0.5 ? \"before\" : \"after\";\r\n    }\r\n\r\n    this.setDropTarget(contentEl, position, rect);\r\n  }\r\n\r\n  private setDropTarget(\r\n    contentEl: HTMLElement,\r\n    position: DropPosition,\r\n    rect: DOMRect,\r\n  ): void {\r\n    if (this.currentDropTarget) {\r\n      this.currentDropTarget.classList.remove(\r\n        \"ft-node__content--drop-inside\",\r\n        \"ft-node__content--drop-before\",\r\n        \"ft-node__content--drop-after\",\r\n      );\r\n    }\r\n    if (this.rootDrop) {\r\n      this.rootDrop = false;\r\n      this.treeEl.classList.remove(\"ft-tree--drop-root\");\r\n    }\r\n\r\n    this.currentDropTarget = contentEl;\r\n    this.dropPosition = position;\r\n    contentEl.classList.add(`ft-node__content--drop-${position}`);\r\n\r\n    const treeRect = this.treeEl.getBoundingClientRect();\r\n\r\n    if (position === \"inside\") {\r\n      this.dropIndicator.style.display = \"none\";\r\n    } else {\r\n      this.dropIndicator.style.display = \"block\";\r\n      this.dropIndicator.style.left = `${rect.left - treeRect.left}px`;\r\n      this.dropIndicator.style.width = `${rect.width}px`;\r\n      const topOffset =\r\n        position === \"before\"\r\n          ? rect.top - treeRect.top\r\n          : rect.bottom - treeRect.top;\r\n      this.dropIndicator.style.top = `${topOffset - 1}px`;\r\n    }\r\n  }\r\n\r\n  private setRootDrop(): void {\r\n    if (this.rootDrop) return;\r\n    this.clearDropTarget();\r\n    this.rootDrop = true;\r\n    this.treeEl.classList.add(\"ft-tree--drop-root\");\r\n  }\r\n\r\n  private clearDropTarget(): void {\r\n    if (this.currentDropTarget) {\r\n      this.currentDropTarget.classList.remove(\r\n        \"ft-node__content--drop-inside\",\r\n        \"ft-node__content--drop-before\",\r\n        \"ft-node__content--drop-after\",\r\n      );\r\n      this.currentDropTarget = null;\r\n    }\r\n    if (this.rootDrop) {\r\n      this.rootDrop = false;\r\n      this.treeEl.classList.remove(\"ft-tree--drop-root\");\r\n    }\r\n    this.dropIndicator.style.display = \"none\";\r\n  }\r\n\r\n  private onDragLeave(e: DragEvent): void {\r\n    const treeRect = this.treeEl.getBoundingClientRect();\r\n    const { clientX, clientY } = e;\r\n    const outsideTree =\r\n      clientX < treeRect.left ||\r\n      clientX > treeRect.right ||\r\n      clientY < treeRect.top ||\r\n      clientY > treeRect.bottom;\r\n\r\n    if (outsideTree) {\r\n      // Pointer left the tree entirely: drop the root highlight or any\r\n      // node target so the indicator doesn't stay stuck.\r\n      this.clearDropTarget();\r\n      return;\r\n    }\r\n\r\n    const contentEl = (e.target as HTMLElement).closest(\r\n      \".ft-node__content\",\r\n    ) as HTMLElement | null;\r\n    if (contentEl && contentEl === this.currentDropTarget) {\r\n      const rect = contentEl.getBoundingClientRect();\r\n      if (\r\n        clientX < rect.left ||\r\n        clientX > rect.right ||\r\n        clientY < rect.top ||\r\n        clientY > rect.bottom\r\n      ) {\r\n        this.clearDropTarget();\r\n      }\r\n    }\r\n  }\r\n\r\n  private onDrop(e: DragEvent): void {\r\n    e.preventDefault();\r\n\r\n    // Determine drop target: a node's path, or null for the tree's root.\r\n    let targetPath: string | null = null;\r\n    if (this.currentDropTarget) {\r\n      const targetNodeEl = this.currentDropTarget.closest(\r\n        \".ft-node\",\r\n      ) as HTMLElement;\r\n      targetPath = targetNodeEl?.dataset.path ?? null;\r\n    }\r\n\r\n    const position = this.dropPosition;\r\n    const isExternal =\r\n      ((e.dataTransfer?.files && e.dataTransfer.files.length > 0) ||\r\n        (e.dataTransfer?.items && e.dataTransfer.items.length > 0)) &&\r\n      this.draggedPaths.length === 0;\r\n    const isRootDrop = this.rootDrop;\r\n\r\n    this.clearDropTarget();\r\n\r\n    if (!targetPath && !isRootDrop) {\r\n      // No drop target was set — the drop happened outside any node.\r\n      this.cleanup();\r\n      return;\r\n    }\r\n\r\n    if (isExternal) {\r\n      const files = e.dataTransfer.files;\r\n      const items = e.dataTransfer.items; // for directories\r\n      const entries = { files, items };\r\n      this.callbacks.onExternalDrop(entries, targetPath, position);\r\n      return;\r\n    }\r\n\r\n    if (this.draggedPaths.length > 0) {\r\n      this.callbacks.onMove(this.draggedPaths, targetPath ?? \"\", position);\r\n    }\r\n\r\n    this.cleanup();\r\n  }\r\n\r\n  private onDragEnd(): void {\r\n    this.cleanup();\r\n  }\r\n\r\n  private cleanup(): void {\r\n    for (const p of this.draggedPaths) {\r\n      const node = this.callbacks.getNode(p);\r\n      node?.el.classList.remove(\"ft-node--dragging\");\r\n      node?.el.style.removeProperty(\"opacity\");\r\n    }\r\n    this.draggedPaths = [];\r\n    this.clearDropTarget();\r\n  }\r\n\r\n  destroy(): void {\r\n    this.treeEl.removeEventListener(\"dragstart\", this.onDragStart);\r\n    this.treeEl.removeEventListener(\"dragover\", this.onDragOver);\r\n    this.treeEl.removeEventListener(\"dragleave\", this.onDragLeave);\r\n    this.treeEl.removeEventListener(\"drop\", this.onDrop);\r\n    this.treeEl.removeEventListener(\"dragend\", this.onDragEnd);\r\n    this.dropIndicator.remove();\r\n  }\r\n}\r\n","const svg = (body: string, vb = \"0 0 16 16\") =>\r\n  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${vb}\" width=\"16\" height=\"16\" fill=\"none\">${body}</svg>`;\r\n\r\nconst filled = (body: string, vb = \"0 0 16 16\") =>\r\n  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${vb}\" width=\"16\" height=\"16\">${body}</svg>`;\r\n\r\n// ── Navigation ──────────────────────────────────────────────\r\n\r\nexport const chevron = svg(\r\n  `<path d=\"M6 3l5 5-5 5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\n// ── Nodes ───────────────────────────────────────────────────\r\n\r\nexport const folder = filled(\r\n  `<path fill=\"#C09553\" d=\"M1.5 2A1.5 1.5 0 000 3.5v9A1.5 1.5 0 001.5 14h13a1.5 1.5 0 001.5-1.5V5.5A1.5 1.5 0 0014.5 4H8L6.854 2.146A.5.5 0 006.5 2H1.5z\"/>`,\r\n);\r\n\r\nexport const folderOpen = filled(\r\n  `<path fill=\"#DCAD6A\" d=\"M.5 4A1.5 1.5 0 012 2.5h4.5a.5.5 0 01.354.146L8.207 4H14a1.5 1.5 0 011.5 1.5V6H2.5A1.5 1.5 0 001 7.5V4.5A.5.5 0 00.5 4z\"/>\r\n   <path fill=\"#C09553\" d=\"M1 7.5A1.5 1.5 0 012.5 6h12a1.5 1.5 0 011.45 1.12l-1.5 6A1.5 1.5 0 0113 14H2.5A1.5 1.5 0 011 12.5v-5z\"/>`,\r\n);\r\n\r\nexport const file = svg(\r\n  `<path d=\"M3 1.5A1.5 1.5 0 014.5 0h4.879a1.5 1.5 0 011.06.44l2.122 2.12A1.5 1.5 0 0113 3.622V14.5a1.5 1.5 0 01-1.5 1.5h-7A1.5 1.5 0 013 14.5v-13z\" fill=\"currentColor\" opacity=\"0.3\"/>\r\n   <path d=\"M9 0v3.5A1.5 1.5 0 0010.5 5H13\" stroke=\"currentColor\" stroke-width=\"0.8\" opacity=\"0.5\"/>`,\r\n);\r\n\r\n// ── File type icons ─────────────────────────────────────────\r\n\r\nfunction fileTypeBadge(text: string, color: string): string {\r\n  return filled(\r\n    `<path d=\"M3 1.5A1.5 1.5 0 014.5 0h4.879a1.5 1.5 0 011.06.44l2.122 2.12A1.5 1.5 0 0113 3.622V14.5a1.5 1.5 0 01-1.5 1.5h-7A1.5 1.5 0 013 14.5v-13z\" fill=\"currentColor\" opacity=\"0.15\"/>\r\n     <text x=\"8\" y=\"11.5\" text-anchor=\"middle\" font-family=\"system-ui,sans-serif\" font-size=\"5.5\" font-weight=\"700\" fill=\"${color}\">${text}</text>`,\r\n  );\r\n}\r\n\r\nexport const fileTs = fileTypeBadge(\"TS\", \"#3178C6\");\r\nexport const fileJs = fileTypeBadge(\"JS\", \"#F0DB4F\");\r\nexport const fileTsx = fileTypeBadge(\"TX\", \"#3178C6\");\r\nexport const fileJsx = fileTypeBadge(\"JX\", \"#61DAFB\");\r\nexport const fileHtml = fileTypeBadge(\"H\", \"#E44D26\");\r\nexport const fileCss = fileTypeBadge(\"C\", \"#264DE4\");\r\nexport const fileScss = fileTypeBadge(\"S\", \"#CD6799\");\r\nexport const fileJson = fileTypeBadge(\"{}\", \"#A4A4A4\");\r\nexport const fileMd = fileTypeBadge(\"M\", \"#519ABA\");\r\nexport const fileYaml = fileTypeBadge(\"Y\", \"#CB171E\");\r\nexport const fileSvg = fileTypeBadge(\"SV\", \"#FFB13B\");\r\nexport const filePng = fileTypeBadge(\"Im\", \"#A074C4\");\r\nexport const fileJpg = fileTypeBadge(\"Im\", \"#A074C4\");\r\nexport const fileGif = fileTypeBadge(\"Im\", \"#A074C4\");\r\nexport const fileWebp = fileTypeBadge(\"Im\", \"#A074C4\");\r\nexport const filePy = fileTypeBadge(\"Py\", \"#3776AB\");\r\nexport const fileRb = fileTypeBadge(\"Rb\", \"#CC342D\");\r\nexport const fileRs = fileTypeBadge(\"Rs\", \"#DEA584\");\r\nexport const fileGo = fileTypeBadge(\"Go\", \"#00ADD8\");\r\nexport const fileJava = fileTypeBadge(\"Ja\", \"#ED8B00\");\r\nexport const filePhp = fileTypeBadge(\"P\", \"#777BB4\");\r\nexport const fileSh = fileTypeBadge(\"$\", \"#89E051\");\r\nexport const fileSql = fileTypeBadge(\"Q\", \"#F29111\");\r\nexport const fileXml = fileTypeBadge(\"X\", \"#E44D26\");\r\nexport const fileToml = fileTypeBadge(\"T\", \"#9C4121\");\r\nexport const fileLock = fileTypeBadge(\"lk\", \"#888\");\r\nexport const fileEnv = fileTypeBadge(\".e\", \"#ECD53F\");\r\nexport const fileVue = fileTypeBadge(\"V\", \"#42B883\");\r\nexport const fileTxt = fileTypeBadge(\"tx\", \"#A4A4A4\");\r\n\r\n// ── Toolbar icons ───────────────────────────────────────────\r\n\r\nexport const newFile = svg(\r\n  `<path d=\"M9 1H4.5A1.5 1.5 0 003 2.5v11A1.5 1.5 0 004.5 15h7a1.5 1.5 0 001.5-1.5V5L9 1z\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n   <path d=\"M9 1v4h4M8 9v4M6 11h4\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const newFolder = svg(\r\n  `<path d=\"M1.5 3A1.5 1.5 0 013 1.5h3l1.5 1.5H13A1.5 1.5 0 0114.5 4.5v7A1.5 1.5 0 0113 13H3A1.5 1.5 0 011.5 11.5v-8.5z\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n   <path d=\"M8 7v4M6 9h4\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const expandAllIcon = svg(\r\n  `<rect x=\"1.5\" y=\"1.5\" width=\"13\" height=\"13\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n   <path d=\"M8 5.5v5M5.5 8h5\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const collapseAllIcon = svg(\r\n  `<rect x=\"1.5\" y=\"1.5\" width=\"13\" height=\"13\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n   <path d=\"M5.5 8h5\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const editIcon = svg(\r\n  `<path d=\"M11.5 1.5a2.121 2.121 0 013 3L5.5 13.5 1 15l1.5-4.5 9-9z\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const trashIcon = svg(\r\n  `<path d=\"M2 4h12M5.333 4V2.667a1.333 1.333 0 011.334-1.334h2.666a1.333 1.333 0 011.334 1.334V4m2 0v9.333a1.333 1.333 0 01-1.334 1.334H4.667a1.333 1.333 0 01-1.334-1.334V4h9.334z\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const copyIcon = svg(\r\n  `<rect x=\"5\" y=\"5\" width=\"9\" height=\"10\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\r\n   <path d=\"M3 11V3a1 1 0 011-1h6\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>`,\r\n);\r\n\r\nexport const cutIcon = svg(\r\n  `<circle cx=\"5\" cy=\"4\" r=\"2\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\r\n   <circle cx=\"5\" cy=\"12\" r=\"2\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\r\n   <path d=\"M6.5 5.5L14 13M6.5 10.5L14 3\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>`,\r\n);\r\n\r\nexport const pasteIcon = svg(\r\n  `<rect x=\"4\" y=\"3\" width=\"9\" height=\"12\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\r\n   <path d=\"M6 3V1.5h5V3M6.5 8l2 2 3-3.5\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\nexport const refreshIcon = svg(\r\n  `<path d=\"M13.5 8a5.5 5.5 0 11-1.61-3.89M13.5 5L12.89 1.55M13.5 5L10.05 5.61\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\r\n);\r\n\r\n// ── Icon Registry ───────────────────────────────────────────\r\n\r\nexport const defaultIconMap: Record<string, string> = {\r\n  ts: fileTs,\r\n  tsx: fileTsx,\r\n  js: fileJs,\r\n  jsx: fileJsx,\r\n  mjs: fileJs,\r\n  cjs: fileJs,\r\n  html: fileHtml,\r\n  htm: fileHtml,\r\n  css: fileCss,\r\n  scss: fileScss,\r\n  sass: fileScss,\r\n  less: fileCss,\r\n  json: fileJson,\r\n  md: fileMd,\r\n  mdx: fileMd,\r\n  markdown: fileMd,\r\n  yaml: fileYaml,\r\n  yml: fileYaml,\r\n  svg: fileSvg,\r\n  png: filePng,\r\n  jpg: fileJpg,\r\n  jpeg: fileJpg,\r\n  gif: fileGif,\r\n  webp: fileWebp,\r\n  py: filePy,\r\n  rb: fileRb,\r\n  rs: fileRs,\r\n  go: fileGo,\r\n  java: fileJava,\r\n  php: filePhp,\r\n  sh: fileSh,\r\n  bash: fileSh,\r\n  zsh: fileSh,\r\n  sql: fileSql,\r\n  xml: fileXml,\r\n  toml: fileToml,\r\n  lock: fileLock,\r\n  env: fileEnv,\r\n  vue: fileVue,\r\n  txt: fileTxt,\r\n  log: fileTxt,\r\n};\r\n\r\n/** Special name-based icons (entire filename match). */\r\nexport const defaultNameIconMap: Record<string, string> = {\r\n  \".gitignore\": fileTypeBadge(\"gi\", \"#F05032\"),\r\n  \".npmignore\": fileTypeBadge(\"ni\", \"#CB3837\"),\r\n  \".env\": fileEnv,\r\n  \".env.local\": fileEnv,\r\n  \".env.production\": fileEnv,\r\n  \".env.development\": fileEnv,\r\n  Dockerfile: fileTypeBadge(\"D\", \"#2496ED\"),\r\n  Makefile: fileTypeBadge(\"Mk\", \"#427819\"),\r\n  LICENSE: fileTypeBadge(\"Li\", \"#DA2128\"),\r\n  \"README.md\": fileTypeBadge(\"R\", \"#519ABA\"),\r\n};\r\n","import type { FileTreeNodeData, HierarchyNode } from \"./types\";\r\n\r\n/** Normalize a path: forward slashes, no leading/trailing slashes, collapse multiples. */\r\nexport function normalizePath(p: string): string {\r\n  return p\r\n    .replace(/\\\\/g, \"/\")\r\n    .replace(/\\/+/g, \"/\")\r\n    .replace(/^\\/|\\/$/g, \"\");\r\n}\r\n\r\n/** Get the parent path. Returns empty string for root-level paths. */\r\nexport function getParentPath(path: string): string {\r\n  const idx = path.lastIndexOf(\"/\");\r\n  return idx === -1 ? \"\" : path.substring(0, idx);\r\n}\r\n\r\n/** Get the file/folder name (last segment of the path). */\r\nexport function getName(path: string): string {\r\n  const idx = path.lastIndexOf(\"/\");\r\n  return idx === -1 ? path : path.substring(idx + 1);\r\n}\r\n\r\n/** Get file extension without the leading dot, lowercased. */\r\nexport function getExtension(name: string): string {\r\n  const idx = name.lastIndexOf(\".\");\r\n  if (idx <= 0) return \"\";\r\n  return name.slice(idx + 1).toLowerCase();\r\n}\r\n\r\n/**\r\n * Normalize a flat data array:\r\n * - Normalize all paths\r\n * - Remove empty paths\r\n * - Auto-create missing parent folders\r\n * - Deduplicate (first occurrence wins)\r\n */\r\nexport function normalizeData(data: FileTreeNodeData[]): FileTreeNodeData[] {\r\n  const map = new Map<string, FileTreeNodeData>();\r\n\r\n  for (const item of data) {\r\n    const path = normalizePath(item.path);\r\n    if (!path) continue;\r\n    if (!map.has(path)) {\r\n      map.set(path, { ...item, path });\r\n    }\r\n  }\r\n\r\n  // Auto-create parent folders\r\n  for (const path of [...map.keys()]) {\r\n    const segments = path.split(\"/\");\r\n    for (let i = 1; i < segments.length; i++) {\r\n      const folderPath = segments.slice(0, i).join(\"/\");\r\n      if (!map.has(folderPath)) {\r\n        map.set(folderPath, { path: folderPath, type: \"folder\" });\r\n      }\r\n    }\r\n  }\r\n\r\n  return [...map.values()];\r\n}\r\n\r\n/**\r\n * Build a hierarchical tree from flat data for rendering.\r\n */\r\nexport function buildHierarchy(\r\n  data: FileTreeNodeData[],\r\n  sort?: boolean | ((a: FileTreeNodeData, b: FileTreeNodeData) => number),\r\n): HierarchyNode[] {\r\n  const map = new Map<string, HierarchyNode>();\r\n  const roots: HierarchyNode[] = [];\r\n\r\n  // Sort flat data by depth so parents are processed before children\r\n  const sorted = [...data].sort((a, b) => {\r\n    const da = a.path.split(\"/\").length;\r\n    const db = b.path.split(\"/\").length;\r\n    return da - db;\r\n  });\r\n\r\n  for (const item of sorted) {\r\n    const node: HierarchyNode = {\r\n      name: getName(item.path),\r\n      path: item.path,\r\n      type: item.type,\r\n      data: item,\r\n      children: [],\r\n    };\r\n    map.set(item.path, node);\r\n\r\n    const parent = getParentPath(item.path);\r\n    if (parent === \"\") {\r\n      roots.push(node);\r\n    } else {\r\n      const parentNode = map.get(parent);\r\n      if (parentNode) {\r\n        parentNode.children.push(node);\r\n      }\r\n    }\r\n  }\r\n\r\n  // Sort if requested\r\n  if (sort) {\r\n    const cmp = typeof sort === \"function\" ? sort : defaultSort;\r\n    const hCmp = (a: HierarchyNode, b: HierarchyNode) => cmp(a.data, b.data);\r\n    sortHierarchyNodes(roots, hCmp);\r\n  }\r\n\r\n  return roots;\r\n}\r\n\r\nfunction sortHierarchyNodes(\r\n  nodes: HierarchyNode[],\r\n  cmp: (a: HierarchyNode, b: HierarchyNode) => number,\r\n): void {\r\n  nodes.sort(cmp);\r\n  for (const node of nodes) {\r\n    if (node.children.length > 0) {\r\n      sortHierarchyNodes(node.children, cmp);\r\n    }\r\n  }\r\n}\r\n\r\n/** Default sort: folders first, then alphabetical case-insensitive. */\r\nexport function defaultSort(a: FileTreeNodeData, b: FileTreeNodeData): number {\r\n  if (a.type !== b.type) return a.type === \"folder\" ? -1 : 1;\r\n  const nameA = getName(a.path);\r\n  const nameB = getName(b.path);\r\n  return nameA.localeCompare(nameB, undefined, { sensitivity: \"base\" });\r\n}\r\n\r\n/** Deep clone a flat data array. */\r\nexport function cloneData(data: FileTreeNodeData[]): FileTreeNodeData[] {\r\n  return data.map((d) => ({\r\n    ...d,\r\n    meta: d.meta ? { ...d.meta } : undefined,\r\n  }));\r\n}\r\n\r\n/** Update all matching paths in a data array when a node is renamed or moved. */\r\nexport function updatePathsInData(\r\n  data: FileTreeNodeData[],\r\n  oldPath: string,\r\n  newPath: string,\r\n): void {\r\n  const oldPrefix = oldPath + \"/\";\r\n  const newPrefix = newPath + \"/\";\r\n  for (let i = 0; i < data.length; i++) {\r\n    if (data[i].path === oldPath) {\r\n      data[i] = { ...data[i], path: newPath };\r\n    } else if (data[i].path.startsWith(oldPrefix)) {\r\n      data[i] = {\r\n        ...data[i],\r\n        path: newPrefix + data[i].path.slice(oldPrefix.length),\r\n      };\r\n    }\r\n  }\r\n}\r\n\r\n/** Update all matching paths in a Set. */\r\nexport function updatePathsInSet(\r\n  set: Set<string>,\r\n  oldPath: string,\r\n  newPath: string,\r\n): void {\r\n  const oldPrefix = oldPath + \"/\";\r\n  const newPrefix = newPath + \"/\";\r\n  const toRemove: string[] = [];\r\n  const toAdd: string[] = [];\r\n  for (const p of set) {\r\n    if (p === oldPath) {\r\n      toRemove.push(p);\r\n      toAdd.push(newPath);\r\n    } else if (p.startsWith(oldPrefix)) {\r\n      toRemove.push(p);\r\n      toAdd.push(newPrefix + p.slice(oldPrefix.length));\r\n    }\r\n  }\r\n  for (const p of toRemove) set.delete(p);\r\n  for (const p of toAdd) set.add(p);\r\n}\r\n\r\n/**\r\n * Create one or more FileTreeNodeData entries for a path,\r\n * automatically including all intermediate parent folders.\r\n */\r\nexport function createNode(\r\n  path: string,\r\n  type: \"file\" | \"folder\",\r\n  meta?: Record<string, unknown>,\r\n): FileTreeNodeData[] {\r\n  const normalized = normalizePath(path);\r\n  if (!normalized) return [];\r\n\r\n  const result: FileTreeNodeData[] = [];\r\n  const segments = normalized.split(\"/\");\r\n\r\n  // Create parent folders\r\n  for (let i = 1; i < segments.length; i++) {\r\n    const folderPath = segments.slice(0, i).join(\"/\");\r\n    result.push({ path: folderPath, type: \"folder\" });\r\n  }\r\n\r\n  // Create the node itself\r\n  result.push({ path: normalized, type, ...(meta ? { meta } : {}) });\r\n\r\n  return result;\r\n}\r\n\r\n/** Check if `childPath` is a descendant of `parentPath`. */\r\nexport function isDescendant(parentPath: string, childPath: string): boolean {\r\n  return childPath.startsWith(parentPath + \"/\");\r\n}\r\n","import { EventEmitter } from \"./EventEmitter\";\r\nimport { injectStyles } from \"./styles\";\r\nimport { ContextMenu, type ContextMenuEntry } from \"./ContextMenu\";\r\nimport { DragDrop, type DropPosition } from \"./DragDrop\";\r\nimport {\r\n  chevron,\r\n  folder,\r\n  folderOpen,\r\n  file as fileIcon,\r\n  newFile,\r\n  newFolder,\r\n  expandAllIcon,\r\n  collapseAllIcon,\r\n  editIcon,\r\n  trashIcon,\r\n  copyIcon,\r\n  cutIcon,\r\n  pasteIcon,\r\n  defaultIconMap,\r\n  defaultNameIconMap,\r\n} from \"./icons\";\r\nimport {\r\n  normalizePath,\r\n  getParentPath,\r\n  getName,\r\n  getExtension,\r\n  normalizeData,\r\n  buildHierarchy,\r\n  cloneData,\r\n  updatePathsInData,\r\n  updatePathsInSet,\r\n  isDescendant,\r\n  createNode,\r\n} from \"./utils\";\r\nimport type {\r\n  FileTreeNodeData,\r\n  FileTreeOptions,\r\n  FileTreeEvent,\r\n  FileTreeEventType,\r\n  FileTreeEventSource,\r\n  EventHandler,\r\n  InternalNode,\r\n  HierarchyNode,\r\n  Theme,\r\n  Direction,\r\n  ToolbarOptions,\r\n  ContextMenuOptions,\r\n  FileTreeStringKey,\r\n  FileTreeTranslate,\r\n} from \"./types\";\r\n\r\n/** Built-in English strings used when no `t` function is provided. */\r\nexport const defaultStrings: Record<FileTreeStringKey, string> = {\r\n  newFile: \"New File\",\r\n  newFolder: \"New Folder\",\r\n  expandAll: \"Expand All\",\r\n  collapseAll: \"Collapse All\",\r\n  copyPath: \"Copy Path\",\r\n  copy: \"Copy\",\r\n  cut: \"Cut\",\r\n  paste: \"Paste\",\r\n  rename: \"Rename\",\r\n  delete: \"Delete\",\r\n};\r\n\r\nconst DEFAULT_OPTIONS: Required<FileTreeOptions> = {\r\n  data: [],\r\n  selected: \"\",\r\n  theme: \"dark\",\r\n  direction: \"ltr\",\r\n  indent: 16,\r\n  dragAndDrop: true,\r\n  readOnly: false,\r\n  injectStyles: true,\r\n  toolbar: {\r\n    createFile: true,\r\n    createFolder: true,\r\n    expandAll: true,\r\n    collapseAll: true,\r\n    custom: [],\r\n  },\r\n  contextMenu: {\r\n    createFile: true,\r\n    createFolder: true,\r\n    rename: true,\r\n    delete: true,\r\n    copy: true,\r\n    cut: true,\r\n    paste: true,\r\n    copyPath: true,\r\n    custom: [],\r\n  },\r\n  icons: {},\r\n  sort: true,\r\n  t: (key: FileTreeStringKey): string => defaultStrings[key],\r\n};\r\n\r\nexport class FileTree {\r\n  // ── Internal State ──────────────────────────────────────\r\n\r\n  private root: HTMLElement;\r\n  private toolbarEl: HTMLElement | null = null;\r\n  private treeEl: HTMLElement;\r\n  private data: FileTreeNodeData[];\r\n  private hierarchy: HierarchyNode[] = [];\r\n  private options: Required<FileTreeOptions>;\r\n  private nodeMap = new Map<string, InternalNode>();\r\n  private expandedNodes = new Set<string>();\r\n  /** All selected node paths. */\r\n  private selectedPaths = new Set<string>();\r\n  /** Anchor path for shift-range selection. */\r\n  private anchorPath: string | null = null;\r\n  /** Keyboard focus path (may differ from selection while ctrl+arrowing). */\r\n  private lastFocusPath: string | null = null;\r\n  private emitter = new EventEmitter<\r\n    Record<FileTreeEventType, FileTreeEvent>\r\n  >();\r\n  private contextMenu: ContextMenu;\r\n  private dragDrop: DragDrop | null = null;\r\n  private iconMap: Record<string, string>;\r\n  private nameIconMap: Record<string, string>;\r\n  private renamingPath: string | null = null;\r\n  private pendingNewNodePath: string | null = null;\r\n  private clipboard: { paths: string[]; type: \"copy\" | \"cut\" } | null = null;\r\n  private t: FileTreeTranslate;\r\n\r\n  /** Primary selected path (first in insertion order), for backward compat. */\r\n  private get selectedPath(): string | null {\r\n    return this.selectedPaths.size > 0\r\n      ? [...this.selectedPaths][0]\r\n      : null;\r\n  }\r\n\r\n  // ── Constructor ─────────────────────────────────────────\r\n\r\n  constructor(container: HTMLElement | string, options?: FileTreeOptions) {\r\n    const el =\r\n      typeof container === \"string\"\r\n        ? document.querySelector(container)\r\n        : container;\r\n    if (!el || !(el instanceof HTMLElement)) {\r\n      throw new Error(\"[file-tree-js] Invalid container element.\");\r\n    }\r\n\r\n    this.options = this.mergeOptions(options);\r\n    this.data = normalizeData(this.options.data);\r\n    this.t = (key: FileTreeStringKey): string =>\r\n      this.options.t(key) ?? defaultStrings[key];\r\n    this.iconMap = { ...defaultIconMap, ...this.options.icons };\r\n    this.nameIconMap = { ...defaultNameIconMap };\r\n\r\n    // Inject the bundled stylesheet into the document once (unless disabled).\r\n    if (this.options.injectStyles) {\r\n      injectStyles();\r\n    }\r\n\r\n    // Root element\r\n    this.root = document.createElement(\"div\");\r\n    this.root.className = \"ft-root\";\r\n    this.root.dataset.theme = this.options.theme;\r\n    this.root.dir = this.options.direction;\r\n    this.root.tabIndex = 0;\r\n    el.appendChild(this.root);\r\n\r\n    // Toolbar\r\n    if (this.options.toolbar !== false) {\r\n      this.toolbarEl = this.renderToolbar();\r\n      this.root.appendChild(this.toolbarEl);\r\n    }\r\n\r\n    // Tree container\r\n    this.treeEl = document.createElement(\"div\");\r\n    this.treeEl.className = \"ft-tree\";\r\n    this.treeEl.setAttribute(\"role\", \"tree\");\r\n    this.root.appendChild(this.treeEl);\r\n\r\n    // Context menu\r\n    this.contextMenu = new ContextMenu(this.root);\r\n\r\n    // Drag & drop\r\n    if (this.options.dragAndDrop && !this.options.readOnly) {\r\n      this.dragDrop = new DragDrop(this.treeEl, {\r\n        getNode: (path) => this.nodeMap.get(path),\r\n        getDragPaths: (path) => {\r\n          // Dragging a selected node drags the whole selection (minus any\r\n          // selected descendants of other selected nodes).\r\n          if (this.selectedPaths.has(path) && this.selectedPaths.size > 1) {\r\n            return this.getSelectedForOp();\r\n          }\r\n          return [path];\r\n        },\r\n        onMove: (src, tgt, pos) => this.handleDragMove(src, tgt, pos),\r\n        onExternalDrop: (entries, tgt, pos) =>\r\n          this.handleExternalDrop(entries, tgt, pos),\r\n      });\r\n    }\r\n\r\n    // Render tree\r\n    this.renderTree();\r\n\r\n    // Apply initial selection (expands parents automatically)\r\n    if (this.options.selected) {\r\n      this.select(this.options.selected);\r\n    }\r\n\r\n    // Keyboard\r\n    this.root.addEventListener(\"keydown\", this.onKeydown.bind(this));\r\n  }\r\n\r\n  // ── Options ─────────────────────────────────────────────\r\n\r\n  private mergeOptions(opts?: FileTreeOptions): Required<FileTreeOptions> {\r\n    if (!opts) return { ...DEFAULT_OPTIONS };\r\n    return {\r\n      data: opts.data ?? DEFAULT_OPTIONS.data,\r\n      selected: opts.selected ?? DEFAULT_OPTIONS.selected,\r\n      theme: opts.theme ?? DEFAULT_OPTIONS.theme,\r\n      direction: opts.direction ?? DEFAULT_OPTIONS.direction,\r\n      indent: opts.indent ?? DEFAULT_OPTIONS.indent,\r\n      dragAndDrop: opts.dragAndDrop ?? DEFAULT_OPTIONS.dragAndDrop,\r\n      readOnly: opts.readOnly ?? DEFAULT_OPTIONS.readOnly,\r\n      injectStyles: opts.injectStyles ?? DEFAULT_OPTIONS.injectStyles,\r\n      toolbar:\r\n        opts.toolbar === undefined\r\n          ? DEFAULT_OPTIONS.toolbar\r\n          : opts.toolbar === false\r\n            ? false\r\n            : {\r\n                ...(DEFAULT_OPTIONS.toolbar as ToolbarOptions),\r\n                ...opts.toolbar,\r\n              },\r\n      contextMenu:\r\n        opts.contextMenu === undefined\r\n          ? DEFAULT_OPTIONS.contextMenu\r\n          : opts.contextMenu === false\r\n            ? false\r\n            : {\r\n                ...(DEFAULT_OPTIONS.contextMenu as ContextMenuOptions),\r\n                ...opts.contextMenu,\r\n              },\r\n      icons: opts.icons ?? DEFAULT_OPTIONS.icons,\r\n      sort: opts.sort ?? DEFAULT_OPTIONS.sort,\r\n      t: opts.t ?? DEFAULT_OPTIONS.t,\r\n    };\r\n  }\r\n\r\n  // ── Toolbar ─────────────────────────────────────────────\r\n\r\n  private renderToolbar(): HTMLElement {\r\n    const tb = document.createElement(\"div\");\r\n    tb.className = \"ft-toolbar\";\r\n    const cfg = this.options.toolbar as ToolbarOptions;\r\n\r\n    // Built-in buttons occupy fixed slots so custom buttons can be\r\n    // interleaved via `order` (see ToolbarButton.order): createFile=0,\r\n    // createFolder=1, expandAll=2, collapseAll=3. Disabled built-ins simply\r\n    // leave their slot empty.\r\n    const buttons: Array<{ order: number; el: HTMLElement }> = [];\r\n\r\n    if (cfg.createFile && !this.options.readOnly) {\r\n      buttons.push({\r\n        order: 0,\r\n        el: this.toolbarBtn(this.t(\"newFile\"), newFile, () =>\r\n          this.createNewNode(\"file\"),\r\n        ),\r\n      });\r\n    }\r\n    if (cfg.createFolder && !this.options.readOnly) {\r\n      buttons.push({\r\n        order: 1,\r\n        el: this.toolbarBtn(this.t(\"newFolder\"), newFolder, () =>\r\n          this.createNewNode(\"folder\"),\r\n        ),\r\n      });\r\n    }\r\n    if (cfg.expandAll) {\r\n      buttons.push({\r\n        order: 2,\r\n        el: this.toolbarBtn(this.t(\"expandAll\"), expandAllIcon, () =>\r\n          this.expandAll(),\r\n        ),\r\n      });\r\n    }\r\n    if (cfg.collapseAll) {\r\n      buttons.push({\r\n        order: 3,\r\n        el: this.toolbarBtn(this.t(\"collapseAll\"), collapseAllIcon, () =>\r\n          this.collapseAll(),\r\n        ),\r\n      });\r\n    }\r\n\r\n    if (cfg.custom) {\r\n      for (const btn of cfg.custom) {\r\n        const el = this.toolbarBtn(\r\n          btn.label,\r\n          btn.icon ?? \"\",\r\n          btn.onClick,\r\n          btn.id,\r\n        );\r\n        // `order` slots among the built-ins; otherwise append at the end,\r\n        // keeping the array order of custom buttons.\r\n        buttons.push({ order: btn.order ?? 4, el });\r\n      }\r\n    }\r\n\r\n    // Stable sort by order (built-ins keep their fixed slots; custom buttons\r\n    // with the same order as an existing built-in come after it).\r\n    buttons\r\n      .sort((a, b) => a.order - b.order)\r\n      .forEach(({ el }) => tb.appendChild(el));\r\n\r\n    return tb;\r\n  }\r\n\r\n  private toolbarBtn(\r\n    title: string,\r\n    icon: string,\r\n    onClick: () => void,\r\n    id?: string,\r\n  ): HTMLElement {\r\n    const btn = document.createElement(\"button\");\r\n    btn.className = \"ft-toolbar__btn\";\r\n    btn.title = title;\r\n    btn.type = \"button\";\r\n    if (id) btn.dataset.btnId = id;\r\n    btn.innerHTML = icon;\r\n    btn.addEventListener(\"click\", (e) => {\r\n      e.stopPropagation();\r\n      onClick();\r\n    });\r\n    return btn;\r\n  }\r\n\r\n  // ── Tree Rendering ──────────────────────────────────────\r\n\r\n  private renderTree(): void {\r\n    this.treeEl.innerHTML = \"\";\r\n    this.nodeMap.clear();\r\n    this.hierarchy = buildHierarchy(this.data, this.options.sort);\r\n    for (const hNode of this.hierarchy) {\r\n      this.renderNode(hNode, 0, \"\", this.treeEl);\r\n    }\r\n  }\r\n\r\n  private renderNode(\r\n    hNode: HierarchyNode,\r\n    depth: number,\r\n    parentPath: string,\r\n    container: HTMLElement,\r\n  ): void {\r\n    const isFolder = hNode.type === \"folder\";\r\n    const expanded = this.expandedNodes.has(hNode.path);\r\n\r\n    // Node wrapper\r\n    const el = document.createElement(\"div\");\r\n    el.className = \"ft-node\";\r\n    el.dataset.path = hNode.path;\r\n    el.dataset.type = hNode.type;\r\n    el.setAttribute(\"role\", \"treeitem\");\r\n    if (this.options.dragAndDrop && !this.options.readOnly) {\r\n      el.draggable = true;\r\n    }\r\n\r\n    // Content row\r\n    const contentEl = document.createElement(\"div\");\r\n    contentEl.className = \"ft-node__content\";\r\n    contentEl.style.paddingInlineStart = `${depth * this.options.indent + 4}px`;\r\n\r\n    // Arrow (folders only)\r\n    let arrowEl: HTMLElement | null = null;\r\n    if (isFolder) {\r\n      arrowEl = document.createElement(\"span\");\r\n      arrowEl.className = \"ft-node__arrow\";\r\n      if (expanded) arrowEl.classList.add(\"ft-node__arrow--open\");\r\n      arrowEl.innerHTML = chevron;\r\n      contentEl.appendChild(arrowEl);\r\n    } else {\r\n      const spacer = document.createElement(\"span\");\r\n      spacer.className = \"ft-node__arrow-spacer\";\r\n      contentEl.appendChild(spacer);\r\n    }\r\n\r\n    // Icon\r\n    const iconEl = document.createElement(\"span\");\r\n    iconEl.className = \"ft-node__icon\";\r\n    iconEl.innerHTML = this.resolveIcon(hNode.data, hNode.name, expanded);\r\n    contentEl.appendChild(iconEl);\r\n\r\n    // Name\r\n    const nameEl = document.createElement(\"span\");\r\n    nameEl.className = \"ft-node__name\";\r\n    nameEl.textContent = hNode.name;\r\n    contentEl.appendChild(nameEl);\r\n\r\n    el.appendChild(contentEl);\r\n\r\n    // Children container (folders only)\r\n    let childrenEl: HTMLElement | null = null;\r\n    if (isFolder) {\r\n      childrenEl = document.createElement(\"div\");\r\n      childrenEl.className = \"ft-node__children\";\r\n      if (!expanded) childrenEl.style.display = \"none\";\r\n      el.appendChild(childrenEl);\r\n\r\n      for (const child of hNode.children) {\r\n        this.renderNode(child, depth + 1, hNode.path, childrenEl);\r\n      }\r\n    }\r\n\r\n    // Store internal reference\r\n    const internalNode: InternalNode = {\r\n      path: hNode.path,\r\n      parentPath,\r\n      name: hNode.name,\r\n      data: hNode.data,\r\n      depth,\r\n      expanded,\r\n      el,\r\n      contentEl,\r\n      childrenEl,\r\n      nameEl,\r\n      arrowEl,\r\n      iconEl,\r\n    };\r\n    this.nodeMap.set(hNode.path, internalNode);\r\n\r\n    // Events\r\n    const nodePath = hNode.path;\r\n\r\n    contentEl.addEventListener(\"click\", (e) => {\r\n      e.stopPropagation();\r\n      if (this.renamingPath) return;\r\n      if (e.shiftKey) {\r\n        this.selectNode(nodePath, \"ui\", \"range\");\r\n      } else if (e.ctrlKey || e.metaKey) {\r\n        this.selectNode(nodePath, \"ui\", \"toggle\");\r\n      } else {\r\n        this.selectNode(nodePath);\r\n      }\r\n      if (isFolder) this.toggleExpand(nodePath);\r\n    });\r\n\r\n    contentEl.addEventListener(\"dblclick\", (e) => {\r\n      e.stopPropagation();\r\n      if (this.renamingPath || this.options.readOnly) return;\r\n      if (\r\n        this.options.contextMenu !== false &&\r\n        (this.options.contextMenu as ContextMenuOptions).rename\r\n      ) {\r\n        this.startRename(nodePath);\r\n      }\r\n    });\r\n\r\n    contentEl.addEventListener(\"contextmenu\", (e) => {\r\n      e.preventDefault();\r\n      e.stopPropagation();\r\n      // Right-clicking an already-selected node keeps the multi-selection;\r\n      // right-clicking an unselected node replaces the selection with it.\r\n      if (!this.selectedPaths.has(nodePath)) {\r\n        this.selectNode(nodePath);\r\n      }\r\n      this.showContextMenu(nodePath, e.clientX, e.clientY);\r\n    });\r\n\r\n    container.appendChild(el);\r\n  }\r\n\r\n  // ── Icon Resolution ─────────────────────────────────────\r\n\r\n  private resolveIcon(\r\n    data: FileTreeNodeData,\r\n    name: string,\r\n    expanded: boolean,\r\n  ): string {\r\n    if (data.icon) return data.icon;\r\n    if (data.type === \"folder\") return expanded ? folderOpen : folder;\r\n\r\n    // Check name-based icons\r\n    if (this.nameIconMap[name]) return this.nameIconMap[name];\r\n\r\n    // Extension-based\r\n    const ext = getExtension(name);\r\n    if (ext && this.iconMap[ext]) return this.iconMap[ext];\r\n\r\n    return fileIcon;\r\n  }\r\n\r\n  // ── Selection ───────────────────────────────────────────\r\n\r\n  /** The ordered array of selected node paths. */\r\n  private selectedPathsArray(): string[] {\r\n    return [...this.selectedPaths];\r\n  }\r\n\r\n  /** Synchronize the `--selected` class and `aria-selected` across all nodes. */\r\n  private applySelectionClasses(): void {\r\n    this.nodeMap.forEach((node) => {\r\n      const selected = this.selectedPaths.has(node.path);\r\n      node.contentEl.classList.toggle(\r\n        \"ft-node__content--selected\",\r\n        selected,\r\n      );\r\n      node.el.setAttribute(\"aria-selected\", String(selected));\r\n    });\r\n    // Re-sync the focus ring too: a selection change re-anchors focus, so\r\n    // any stale `--focused` ring from a previous Ctrl+Arrow must be cleared.\r\n    this.applyFocusRing();\r\n  }\r\n\r\n  private clearSelectionInternal(\r\n    source: FileTreeEventSource = \"ui\",\r\n    emit = true,\r\n  ): void {\r\n    this.selectedPaths.clear();\r\n    this.applySelectionClasses();\r\n    if (emit) this.emitSelectEvent(source);\r\n  }\r\n\r\n  private selectAllInternal(source: FileTreeEventSource = \"ui\"): void {\r\n    this.selectedPaths = new Set(this.nodeMap.keys());\r\n    if (this.selectedPaths.size > 0) {\r\n      this.anchorPath = this.selectedPath;\r\n      this.lastFocusPath = this.selectedPath;\r\n    }\r\n    this.applySelectionClasses();\r\n    this.emitSelectEvent(source);\r\n  }\r\n\r\n  /**\r\n   * Select `path` plus everything in the visible order between the anchor\r\n   * (or last focus) and `path`.\r\n   */\r\n  private rangeSelect(path: string, source: FileTreeEventSource = \"ui\"): void {\r\n    const from =\r\n      this.anchorPath && this.nodeMap.has(this.anchorPath)\r\n        ? this.anchorPath\r\n        : this.lastFocusPath ?? this.selectedPath ?? path;\r\n    // Reveal both endpoints so the range spans their visible siblings:\r\n    // expand the target's parents, and expand a collapsed folder endpoint.\r\n    this.expandAncestors(path);\r\n    this.expandAncestors(from);\r\n    const fromData = from ? this.data.find((d) => d.path === from) : undefined;\r\n    if (fromData?.type === \"folder\") this.expand(from);\r\n    const visible = this.getVisibleNodePaths();\r\n    const fromIdx = visible.indexOf(from);\r\n    const toIdx = visible.indexOf(path);\r\n    if (fromIdx === -1 || toIdx === -1) {\r\n      this.selectedPaths = new Set([path]);\r\n      this.anchorPath = path;\r\n    } else {\r\n      const [lo, hi] =\r\n        fromIdx <= toIdx ? [fromIdx, toIdx] : [toIdx, fromIdx];\r\n      this.selectedPaths = new Set(visible.slice(lo, hi + 1));\r\n      this.anchorPath = from;\r\n    }\r\n    this.lastFocusPath = path;\r\n    this.applySelectionClasses();\r\n    this.emitSelectEvent(source);\r\n  }\r\n\r\n  private emitSelectEvent(source: FileTreeEventSource): void {\r\n    const paths = this.selectedPathsArray();\r\n    const primary = paths[0] ?? \"\";\r\n    this.emitEvent(\"select\", primary, undefined, undefined, source, paths);\r\n  }\r\n\r\n  private selectNode(\r\n    path: string,\r\n    source: FileTreeEventSource = \"ui\",\r\n    mode: \"replace\" | \"toggle\" | \"range\" = \"replace\",\r\n  ): void {\r\n    switch (mode) {\r\n      case \"toggle\":\r\n        if (this.selectedPaths.has(path)) {\r\n          this.selectedPaths.delete(path);\r\n        } else {\r\n          this.selectedPaths.add(path);\r\n          this.anchorPath = path;\r\n        }\r\n        break;\r\n      case \"range\":\r\n        this.rangeSelect(path, source);\r\n        return;\r\n      case \"replace\":\r\n      default:\r\n        this.selectedPaths = new Set([path]);\r\n        this.anchorPath = path;\r\n        break;\r\n    }\r\n    this.lastFocusPath = path;\r\n    this.applySelectionClasses();\r\n    this.emitSelectEvent(source);\r\n  }\r\n\r\n  /**\r\n   * Normalize an input (single path or array) into a deduped, sorted array.\r\n   * A path that is a descendant of another path in the list is dropped, so\r\n   * operations never process a node twice (e.g. a folder and its child).\r\n   */\r\n  private dedupePaths(paths: string | string[]): string[] {\r\n    const list = (Array.isArray(paths) ? paths : [paths])\r\n      .map((p) => normalizePath(p))\r\n      .filter(Boolean);\r\n    const unique = [...new Set(list)];\r\n    return unique.filter(\r\n      (p) => !unique.some((q) => q !== p && isDescendant(q, p)),\r\n    );\r\n  }\r\n\r\n  /** The selected paths to apply an operation to (deduped, sorted). */\r\n  private getSelectedForOp(): string[] {\r\n    return this.dedupePaths(this.selectedPathsArray());\r\n  }\r\n\r\n  // ── Expand / Collapse ───────────────────────────────────\r\n\r\n  private toggleExpand(path: string): void {\r\n    if (this.expandedNodes.has(path)) {\r\n      this.collapse(path);\r\n    } else {\r\n      this.expand(path);\r\n    }\r\n  }\r\n\r\n  expand(path: string, source: FileTreeEventSource = \"ui\"): void {\r\n    const p = normalizePath(path);\r\n    const node = this.nodeMap.get(p);\r\n    if (!node || node.data.type !== \"folder\") return;\r\n    if (this.expandedNodes.has(p)) return;\r\n\r\n    this.expandedNodes.add(p);\r\n    node.expanded = true;\r\n    node.arrowEl?.classList.add(\"ft-node__arrow--open\");\r\n    if (node.childrenEl) node.childrenEl.style.display = \"\";\r\n    node.iconEl.innerHTML = this.resolveIcon(node.data, node.name, true);\r\n\r\n    this.emitEvent(\"expand\", p, undefined, undefined, source);\r\n  }\r\n\r\n  collapse(path: string, source: FileTreeEventSource = \"ui\"): void {\r\n    const p = normalizePath(path);\r\n    const node = this.nodeMap.get(p);\r\n    if (!node || node.data.type !== \"folder\") return;\r\n    if (!this.expandedNodes.has(p)) return;\r\n\r\n    this.expandedNodes.delete(p);\r\n    node.expanded = false;\r\n    node.arrowEl?.classList.remove(\"ft-node__arrow--open\");\r\n    if (node.childrenEl) node.childrenEl.style.display = \"none\";\r\n    node.iconEl.innerHTML = this.resolveIcon(node.data, node.name, false);\r\n\r\n    this.emitEvent(\"collapse\", p, undefined, undefined, source);\r\n  }\r\n\r\n  expandAll(source: FileTreeEventSource = \"ui\"): void {\r\n    this.nodeMap.forEach((node) => {\r\n      if (node.data.type === \"folder\") this.expand(node.path, source);\r\n    });\r\n  }\r\n\r\n  collapseAll(source: FileTreeEventSource = \"ui\"): void {\r\n    this.nodeMap.forEach((node) => {\r\n      if (node.data.type === \"folder\") this.collapse(node.path, source);\r\n    });\r\n  }\r\n\r\n  // ── Context Menu ────────────────────────────────────────\r\n\r\n  private showContextMenu(path: string, x: number, y: number): void {\r\n    if (this.options.contextMenu === false || this.options.readOnly) return;\r\n    const cfg = this.options.contextMenu as ContextMenuOptions;\r\n    const nodeData = this.data.find((d) => d.path === path);\r\n    if (!nodeData) return;\r\n\r\n    // Operations act on the whole selection; the right-clicked node is primary.\r\n    const selection = this.getSelectedForOp();\r\n    const opPaths = selection.length > 0 ? selection : [path];\r\n    const primaryNode = nodeData;\r\n\r\n    const entries: ContextMenuEntry[] = [];\r\n\r\n    const addSeparator = (): void => {\r\n      if (entries.length > 0) {\r\n        entries.push({\r\n          id: `sep-${entries.length}`,\r\n          label: \"\",\r\n          separator: true,\r\n          onClick: () => {},\r\n        });\r\n      }\r\n    };\r\n\r\n    if (nodeData.type === \"folder\" && cfg.createFile) {\r\n      entries.push({\r\n        id: \"create-file\",\r\n        label: this.t(\"newFile\"),\r\n        icon: newFile,\r\n        onClick: () => this.createNewNode(\"file\", path),\r\n      });\r\n    }\r\n\r\n    if (nodeData.type === \"folder\" && cfg.createFolder) {\r\n      entries.push({\r\n        id: \"create-folder\",\r\n        label: this.t(\"newFolder\"),\r\n        icon: newFolder,\r\n        onClick: () => this.createNewNode(\"folder\", path),\r\n      });\r\n    }\r\n\r\n    // Clipboard actions\r\n    if (cfg.copy) {\r\n      addSeparator();\r\n      entries.push({\r\n        id: \"copy\",\r\n        label: this.t(\"copy\"),\r\n        icon: copyIcon,\r\n        shortcut: this.modKeyLabel + \"C\",\r\n        onClick: () => this.copyToClipboard(opPaths),\r\n      });\r\n    }\r\n\r\n    if (cfg.cut) {\r\n      entries.push({\r\n        id: \"cut\",\r\n        label: this.t(\"cut\"),\r\n        icon: cutIcon,\r\n        shortcut: this.modKeyLabel + \"X\",\r\n        onClick: () => this.cutNode(opPaths),\r\n      });\r\n    }\r\n\r\n    if (cfg.paste && this.clipboard) {\r\n      entries.push({\r\n        id: \"paste\",\r\n        label: this.t(\"paste\"),\r\n        icon: pasteIcon,\r\n        shortcut: this.modKeyLabel + \"V\",\r\n        onClick: () => this.pasteNode(path),\r\n      });\r\n    }\r\n\r\n    if (cfg.copyPath) {\r\n      addSeparator();\r\n      entries.push({\r\n        id: \"copy-path\",\r\n        label: this.t(\"copyPath\"),\r\n        icon: copyIcon,\r\n        onClick: () => {\r\n          navigator.clipboard?.writeText(opPaths.join(\"\\n\")).catch(() => {});\r\n        },\r\n      });\r\n    }\r\n\r\n    if (cfg.rename) {\r\n      addSeparator();\r\n      entries.push({\r\n        id: \"rename\",\r\n        label: this.t(\"rename\"),\r\n        icon: editIcon,\r\n        shortcut: \"F2\",\r\n        onClick: () => this.startRename(path),\r\n      });\r\n    }\r\n\r\n    if (cfg.delete) {\r\n      entries.push({\r\n        id: \"delete\",\r\n        label: this.t(\"delete\"),\r\n        icon: trashIcon,\r\n        shortcut: \"Del\",\r\n        onClick: () => this.deleteNode(opPaths),\r\n      });\r\n    }\r\n\r\n    if (cfg.custom && cfg.custom.length > 0) {\r\n      const visibleCustom = cfg.custom.filter(\r\n        (c) => !c.visible || c.visible(nodeData),\r\n      );\r\n      if (visibleCustom.length > 0) {\r\n        addSeparator();\r\n      }\r\n      for (const c of visibleCustom) {\r\n        entries.push({\r\n          id: c.id,\r\n          label: c.label,\r\n          icon: c.icon,\r\n          shortcut: c.shortcut,\r\n          onClick: () => {\r\n            const nodes = opPaths\r\n              .map((p) => this.getNode(p))\r\n              .filter((n): n is FileTreeNodeData => Boolean(n));\r\n            c.onClick(nodes, primaryNode);\r\n          },\r\n        });\r\n      }\r\n    }\r\n\r\n    if (entries.length === 0) return;\r\n\r\n    // Convert page coordinates to root-relative\r\n    const rootRect = this.root.getBoundingClientRect();\r\n    this.contextMenu.show(x - rootRect.left, y - rootRect.top, entries);\r\n  }\r\n\r\n  /** Modifier key label for shortcut hints (⌘ on macOS, Ctrl elsewhere). */\r\n  private get modKeyLabel(): string {\r\n    return navigator.platform.toLowerCase().includes(\"mac\") ? \"⌘\" : \"Ctrl+\";\r\n  }\r\n\r\n  // ── Clipboard (Copy / Cut / Paste) ──────────────────────\r\n\r\n  /** Copy one or more nodes to the internal clipboard for later Paste (duplicate). */\r\n  copyToClipboard(path: string | string[]): void {\r\n    const paths = this.dedupePaths(path);\r\n    if (paths.length === 0) return;\r\n    if (!paths.every((p) => this.data.some((d) => d.path === p))) return;\r\n    this.clipboard = { paths, type: \"copy\" };\r\n    this.clearCutHighlight();\r\n  }\r\n\r\n  /** Cut one or more nodes to the internal clipboard for later Paste (move). */\r\n  cutNode(path: string | string[]): void {\r\n    const paths = this.dedupePaths(path);\r\n    if (paths.length === 0) return;\r\n    if (!paths.every((p) => this.data.some((d) => d.path === p))) return;\r\n    this.clipboard = { paths, type: \"cut\" };\r\n    this.clearCutHighlight();\r\n    this.applyCutHighlight();\r\n  }\r\n\r\n  /** Paste the internal clipboard into a target folder (defaults to the selected node's parent). */\r\n  pasteNode(targetPath?: string): void {\r\n    if (!this.clipboard) return;\r\n    const { paths: srcPaths, type } = this.clipboard;\r\n\r\n    // Resolve the destination folder.\r\n    let destPath = \"\";\r\n    if (targetPath) {\r\n      const targetData = this.data.find((d) => d.path === targetPath);\r\n      destPath =\r\n        targetData?.type === \"folder\" ? targetPath : getParentPath(targetPath);\r\n    } else if (this.selectedPath) {\r\n      const selData = this.data.find((d) => d.path === this.selectedPath);\r\n      if (selData?.type === \"folder\") {\r\n        // Pasting while a folder is selected targets that folder's parent,\r\n        // unless it is the clipboard source itself (same location → duplicate).\r\n        destPath =\r\n          this.clipboard.paths.includes(selData.path)\r\n            ? getParentPath(selData.path)\r\n            : selData.path;\r\n      } else {\r\n        destPath = getParentPath(this.selectedPath);\r\n      }\r\n    }\r\n\r\n    if (type === \"copy\") {\r\n      // Guard: none of the copied nodes may be pasted into a descendant of itself.\r\n      for (const src of srcPaths) {\r\n        if (destPath !== \"\" && isDescendant(src, destPath)) return;\r\n      }\r\n      const newPaths: string[] = [];\r\n      for (const src of srcPaths) {\r\n        const newPath = this.copyNodeInternal(src, destPath, \"ui\", true);\r\n        if (newPath) newPaths.push(newPath);\r\n      }\r\n      if (newPaths.length > 0) this.emitChange();\r\n    } else {\r\n      // Cut: move the nodes (and descendants) to the destination.\r\n      this.moveNodeInternal(srcPaths, destPath);\r\n      this.clipboard = null;\r\n      this.clearCutHighlight();\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Duplicate a node (and its descendants) into a target parent folder.\r\n   * Copies to the same location resolve a unique name by appending\r\n   * ` copy` (or ` copy-1`, ...). Returns the new path, or `null` if the\r\n   * copy cannot be performed (invalid source, or copying into a descendant).\r\n   * When `silent` is true, no `change` event is emitted (caller batches).\r\n   */\r\n  copyNodeInternal(\r\n    sourcePath: string,\r\n    targetParentPath: string,\r\n    source: FileTreeEventSource = \"ui\",\r\n    silent = false,\r\n  ): string | null {\r\n    const src = normalizePath(sourcePath);\r\n    const destPath = targetParentPath ? normalizePath(targetParentPath) : \"\";\r\n    if (!this.data.some((d) => d.path === src)) return null;\r\n\r\n    // Guard: copying into itself or into a descendant of the source.\r\n    const destIsInSrc =\r\n      destPath === src || (destPath !== \"\" && isDescendant(src, destPath));\r\n    if (destIsInSrc) return null;\r\n\r\n    // Resolve a unique name in the destination.\r\n    const srcName = getName(src);\r\n    let newPath = destPath ? `${destPath}/${srcName}` : srcName;\r\n    const sameLocation = newPath === src;\r\n    const ext = getExtension(srcName);\r\n    const baseName = ext ? srcName.slice(0, -(ext.length + 1)) : srcName;\r\n    // Same-location copies duplicate with a ` copy` suffix before the extension.\r\n    if (sameLocation) {\r\n      newPath = `${destPath ? `${destPath}/` : \"\"}${baseName} copy${ext ? \".\" + ext : \"\"}`;\r\n    }\r\n    let counter = 1;\r\n    while (this.data.some((d) => d.path === newPath)) {\r\n      newPath = sameLocation\r\n        ? `${destPath ? `${destPath}/` : \"\"}${baseName} copy-${counter}${ext ? \".\" + ext : \"\"}`\r\n        : destPath\r\n          ? `${destPath}/${baseName}-${counter}${ext ? \".\" + ext : \"\"}`\r\n          : `${baseName}-${counter}${ext ? \".\" + ext : \"\"}`;\r\n      counter++;\r\n    }\r\n\r\n    // Clone the node and all its descendants.\r\n    const prefix = src + \"/\";\r\n    const copies = this.data\r\n      .filter((d) => d.path === src || d.path.startsWith(prefix))\r\n      .map((d) => {\r\n        const suffix = d.path === src ? \"\" : d.path.slice(src.length);\r\n        return { ...d, path: newPath + suffix };\r\n      });\r\n    this.data = normalizeData([...this.data, ...copies]);\r\n    if (destPath) this.expandedNodes.add(destPath);\r\n    this.fullRerender();\r\n    this.selectNode(newPath, source);\r\n    this.emitEvent(\"create\", newPath, undefined, undefined, source);\r\n    this.emitEvent(\"copy\", newPath, src, undefined, source);\r\n    if (!silent) this.emitChange(source);\r\n\r\n    return newPath;\r\n  }\r\n\r\n  private applyCutHighlight(): void {\r\n    if (!this.clipboard || this.clipboard.type !== \"cut\") return;\r\n    for (const p of this.clipboard.paths) {\r\n      this.nodeMap.get(p)?.contentEl.classList.add(\"ft-node__content--cut\");\r\n    }\r\n  }\r\n\r\n  private clearCutHighlight(): void {\r\n    this.root\r\n      .querySelectorAll(\".ft-node__content--cut\")\r\n      .forEach((el) => el.classList.remove(\"ft-node__content--cut\"));\r\n  }\r\n\r\n  // ── Rename ──────────────────────────────────────────────\r\n\r\n  private startRename(path: string): void {\r\n    const node = this.nodeMap.get(path);\r\n    if (!node) return;\r\n    if (this.renamingPath) this.cancelRename();\r\n\r\n    this.renamingPath = path;\r\n    const currentName = node.name;\r\n\r\n    const input = document.createElement(\"input\");\r\n    input.className = \"ft-rename-input\";\r\n    input.type = \"text\";\r\n    input.value = currentName;\r\n\r\n    node.nameEl.textContent = \"\";\r\n    node.nameEl.appendChild(input);\r\n    input.focus();\r\n\r\n    // Select name without extension for files\r\n    if (node.data.type === \"file\") {\r\n      const dotIndex = currentName.lastIndexOf(\".\");\r\n      if (dotIndex > 0) {\r\n        input.setSelectionRange(0, dotIndex);\r\n      } else {\r\n        input.select();\r\n      }\r\n    } else {\r\n      input.select();\r\n    }\r\n\r\n    const commit = (): void => {\r\n      if (this.renamingPath !== path) return; // Already handled\r\n\r\n      const newName = input.value.trim();\r\n      const isNewNode = this.pendingNewNodePath === path;\r\n      // Committing a fresh node with an unchanged name keeps it; a regular\r\n      // rename with an unchanged name is a no-op.\r\n      if (\r\n        newName &&\r\n        this.isValidName(newName) &&\r\n        (isNewNode || newName !== currentName)\r\n      ) {\r\n        const parentPath = getParentPath(path);\r\n        const newPath = parentPath ? `${parentPath}/${newName}` : newName;\r\n\r\n        // A name containing slashes creates intermediate folders on the fly.\r\n        if (newName.includes(\"/\")) {\r\n          if (!this.renameToNestedPath(path, newPath, isNewNode, \"ui\")) {\r\n            this.handleRenameCancel(path);\r\n          }\r\n          return;\r\n        }\r\n\r\n        // Check for name conflict\r\n        if (this.data.some((d) => d.path === newPath && d.path !== path)) {\r\n          this.handleRenameCancel(path);\r\n          return;\r\n        }\r\n\r\n        if (isNewNode) {\r\n          // Committing a newly created node\r\n          updatePathsInData(this.data, path, newPath);\r\n          updatePathsInSet(this.expandedNodes, path, newPath);\r\n          this.updateSelectedPath(path, newPath);\r\n          this.pendingNewNodePath = null;\r\n          this.renamingPath = null;\r\n          this.fullRerender();\r\n          this.selectNode(newPath);\r\n          this.root.focus();\r\n          this.emitEvent(\"create\", newPath);\r\n          this.emitChange();\r\n        } else {\r\n          // Regular rename\r\n          const oldPath = path;\r\n          updatePathsInData(this.data, oldPath, newPath);\r\n          updatePathsInSet(this.expandedNodes, oldPath, newPath);\r\n          this.updateSelectedPath(oldPath, newPath);\r\n          this.renamingPath = null;\r\n          this.fullRerender();\r\n          this.selectNode(newPath);\r\n          this.root.focus();\r\n          this.emitEvent(\"rename\", newPath, oldPath);\r\n          this.emitChange();\r\n        }\r\n      } else {\r\n        this.handleRenameCancel(path);\r\n      }\r\n    };\r\n\r\n    input.addEventListener(\"keydown\", (e) => {\r\n      if (e.key === \"Enter\") {\r\n        e.preventDefault();\r\n        input.blur();\r\n      } else if (e.key === \"Escape\") {\r\n        e.preventDefault();\r\n        this.renamingPath = path; // Ensure cancel sees the correct path\r\n        this.handleRenameCancel(path);\r\n      }\r\n    });\r\n\r\n    input.addEventListener(\r\n      \"blur\",\r\n      () => {\r\n        if (this.renamingPath === path) commit();\r\n      },\r\n      { once: true },\r\n    );\r\n  }\r\n\r\n  private handleRenameCancel(path: string): void {\r\n    if (this.pendingNewNodePath === path) {\r\n      // Remove the pending new node\r\n      this.data = this.data.filter((d) => d.path !== path);\r\n      this.pendingNewNodePath = null;\r\n    }\r\n    this.renamingPath = null;\r\n    this.fullRerender();\r\n    this.root.focus();\r\n  }\r\n\r\n  private cancelRename(): void {\r\n    if (!this.renamingPath) return;\r\n    this.handleRenameCancel(this.renamingPath);\r\n  }\r\n\r\n  private isValidName(name: string): boolean {\r\n    return name.length > 0 && !/\\\\/.test(name);\r\n  }\r\n\r\n  /** Rewrite `oldPath` → `newPath` in the selection (and anchor/focus). */\r\n  private updateSelectedPath(oldPath: string, newPath: string): void {\r\n    const rewrite = (p: string): string =>\r\n      p === oldPath\r\n        ? newPath\r\n        : p.startsWith(oldPath + \"/\")\r\n          ? newPath + p.slice(oldPath.length)\r\n          : p;\r\n\r\n    if (this.selectedPaths.size > 0) {\r\n      this.selectedPaths = new Set(\r\n        [...this.selectedPaths].map((p) =>\r\n          p === oldPath || p.startsWith(oldPath + \"/\") ? rewrite(p) : p,\r\n        ),\r\n      );\r\n    }\r\n    if (this.anchorPath) this.anchorPath = rewrite(this.anchorPath);\r\n    if (this.lastFocusPath) this.lastFocusPath = rewrite(this.lastFocusPath);\r\n  }\r\n\r\n  /**\r\n   * Rename a node to a new path containing slashes, creating intermediate\r\n   * folders on the fly. Returns `false` if the rename is invalid (conflict,\r\n   * or a folder renamed inside itself).\r\n   */\r\n  private renameToNestedPath(\r\n    oldPath: string,\r\n    newPath: string,\r\n    isNewNode: boolean,\r\n    source: FileTreeEventSource,\r\n  ): boolean {\r\n    // A folder cannot be moved inside itself (e.g. `a` → `a/b`).\r\n    if (isDescendant(oldPath, newPath)) return false;\r\n    if (this.data.some((d) => d.path === newPath)) return false;\r\n\r\n    if (isNewNode) {\r\n      // Committing a freshly created node: replace the temporary node with\r\n      // the full folder chain, keeping the node's original type on the end.\r\n      const oldType = this.data.find((d) => d.path === oldPath)?.type ?? \"file\";\r\n      this.data = this.data.filter((d) => d.path !== oldPath);\r\n      this.data.push(...createNode(newPath, oldType));\r\n      this.data = normalizeData(this.data);\r\n      this.pendingNewNodePath = null;\r\n      this.renamingPath = null;\r\n      this.fullRerender();\r\n      this.expandAncestors(newPath);\r\n      this.selectNode(newPath);\r\n      this.root.focus();\r\n      this.emitEvent(\"create\", newPath, undefined, undefined, source);\r\n      this.emitChange(source);\r\n      return true;\r\n    }\r\n\r\n    // Regular rename: move the node (and descendants) to the new path.\r\n    // `normalizeData` auto-creates any missing intermediate folders.\r\n    updatePathsInData(this.data, oldPath, newPath);\r\n    updatePathsInSet(this.expandedNodes, oldPath, newPath);\r\n    this.updateSelectedPath(oldPath, newPath);\r\n    this.data = normalizeData(this.data);\r\n\r\n    this.renamingPath = null;\r\n    this.fullRerender();\r\n    this.expandAncestors(newPath);\r\n    this.selectNode(newPath, source);\r\n    this.root.focus();\r\n    this.emitEvent(\"rename\", newPath, oldPath, undefined, source);\r\n    this.emitChange(source);\r\n    return true;\r\n  }\r\n\r\n  // ── Create ──────────────────────────────────────────────\r\n\r\n  private createNewNode(\r\n    type: \"file\" | \"folder\",\r\n    parentFolderPath?: string,\r\n  ): void {\r\n    let parentPath = parentFolderPath ?? \"\";\r\n\r\n    // If no parentPath given, infer from selection\r\n    if (!parentPath && this.selectedPath) {\r\n      const selData = this.data.find((d) => d.path === this.selectedPath);\r\n      if (selData) {\r\n        parentPath =\r\n          selData.type === \"folder\"\r\n            ? selData.path\r\n            : getParentPath(selData.path);\r\n      }\r\n    }\r\n\r\n    const tempName = type === \"file\" ? \"untitled\" : \"new-folder\";\r\n    let finalPath = parentPath ? `${parentPath}/${tempName}` : tempName;\r\n\r\n    // Handle name conflicts\r\n    let counter = 1;\r\n    while (this.data.some((d) => d.path === finalPath)) {\r\n      finalPath = parentPath\r\n        ? `${parentPath}/${tempName}-${counter}`\r\n        : `${tempName}-${counter}`;\r\n      counter++;\r\n    }\r\n\r\n    const newNode: FileTreeNodeData = { path: finalPath, type };\r\n    this.data.push(newNode);\r\n    this.data = normalizeData(this.data);\r\n\r\n    // Expand parent folder\r\n    if (parentPath) this.expandedNodes.add(parentPath);\r\n\r\n    this.fullRerender();\r\n    this.selectNode(finalPath);\r\n\r\n    this.pendingNewNodePath = finalPath;\r\n    this.startRename(finalPath);\r\n  }\r\n\r\n  // ── Delete ──────────────────────────────────────────────\r\n\r\n  /**\r\n   * Attempt to delete one or more nodes. Emits a single `delete` event\r\n   * *before* removal, carrying all target paths in `paths`. If a listener\r\n   * calls `event.preventDefault()`, **none** of the nodes are removed,\r\n   * giving the consumer the chance to show a confirmation dialog and later\r\n   * call `removeNode()` to carry out the deletion.\r\n   */\r\n  deleteNode(paths: string | string[]): void {\r\n    const targets = this.dedupePaths(paths);\r\n    if (targets.length === 0) return;\r\n    const event = this.emitEvent(\r\n      \"delete\",\r\n      targets[0],\r\n      undefined,\r\n      undefined,\r\n      \"ui\",\r\n      targets,\r\n    );\r\n    if (event.defaultPrevented) return;\r\n    for (const p of targets) this.removeNodeInternal(p);\r\n    this.emitChange();\r\n  }\r\n\r\n  /** Remove one node and all its descendants, cleaning up all derived state. */\r\n  private removeNodeInternal(path: string): void {\r\n    const prefix = path + \"/\";\r\n\r\n    // Remove node and all descendants from data\r\n    this.data = this.data.filter(\r\n      (d) => d.path !== path && !d.path.startsWith(prefix),\r\n    );\r\n\r\n    // Clean up expanded nodes\r\n    this.expandedNodes.delete(path);\r\n    for (const p of [...this.expandedNodes]) {\r\n      if (p.startsWith(prefix)) this.expandedNodes.delete(p);\r\n    }\r\n\r\n    // Clean up selection (only the removed entries)\r\n    for (const p of [...this.selectedPaths]) {\r\n      if (p === path || p.startsWith(prefix)) this.selectedPaths.delete(p);\r\n    }\r\n    if (this.anchorPath === path || this.anchorPath?.startsWith(prefix)) {\r\n      this.anchorPath = null;\r\n    }\r\n    if (\r\n      this.lastFocusPath === path ||\r\n      this.lastFocusPath?.startsWith(prefix)\r\n    ) {\r\n      this.lastFocusPath = null;\r\n    }\r\n\r\n    // Clean up clipboard if it referenced the removed node\r\n    if (this.clipboard) {\r\n      this.clipboard.paths = this.clipboard.paths.filter(\r\n        (p) => p !== path && !p.startsWith(prefix),\r\n      );\r\n      if (this.clipboard.paths.length === 0) this.clipboard = null;\r\n    }\r\n\r\n    this.fullRerender();\r\n  }\r\n\r\n  // ── Move (Drag & Drop) ─────────────────────────────────\r\n\r\n  private handleDragMove(\r\n    sourcePaths: string[],\r\n    targetPath: string,\r\n    position: DropPosition,\r\n  ): void {\r\n    if (sourcePaths.length === 0) return;\r\n    if (sourcePaths.some((s) => s === targetPath)) return;\r\n    // Can't drop any dragged node into its own descendant.\r\n    if (sourcePaths.some((s) => isDescendant(s, targetPath))) return;\r\n\r\n    // Empty target path means a drop on the tree root.\r\n    let newParentPath: string;\r\n    if (targetPath === \"\") {\r\n      newParentPath = \"\";\r\n    } else {\r\n      const targetData = this.data.find((d) => d.path === targetPath);\r\n      if (!targetData) return;\r\n\r\n      if (position === \"inside\" && targetData.type === \"folder\") {\r\n        newParentPath = targetPath;\r\n      } else {\r\n        newParentPath = getParentPath(targetPath);\r\n      }\r\n    }\r\n\r\n    this.moveNodeInternal(sourcePaths, newParentPath);\r\n  }\r\n\r\n  private moveNodeInternal(\r\n    sourcePaths: string | string[],\r\n    newParentPath: string,\r\n    source: FileTreeEventSource = \"ui\",\r\n  ): void {\r\n    const paths = this.dedupePaths(sourcePaths);\r\n    if (paths.length === 0) return;\r\n\r\n    const moved: Array<{ oldPath: string; newPath: string }> = [];\r\n    for (const sourcePath of paths) {\r\n      const sourceName = getName(sourcePath);\r\n      const newPath = newParentPath\r\n        ? `${newParentPath}/${sourceName}`\r\n        : sourceName;\r\n\r\n      if (sourcePath === newPath) continue;\r\n      if (this.data.some((d) => d.path === newPath)) continue;\r\n\r\n      updatePathsInData(this.data, sourcePath, newPath);\r\n      updatePathsInSet(this.expandedNodes, sourcePath, newPath);\r\n      this.updateSelectedPath(sourcePath, newPath);\r\n      moved.push({ oldPath: sourcePath, newPath });\r\n    }\r\n\r\n    if (moved.length === 0) return;\r\n    this.data = normalizeData(this.data);\r\n\r\n    if (newParentPath) this.expandedNodes.add(newParentPath);\r\n\r\n    this.fullRerender();\r\n    this.selectNode(moved[0].newPath, source);\r\n\r\n    for (const { oldPath, newPath } of moved) {\r\n      this.emitEvent(\"move\", newPath, oldPath, undefined, source);\r\n    }\r\n    this.emitChange(source);\r\n  }\r\n\r\n  private handleExternalDrop(\r\n    entries: { files: FileList; items: DataTransferItemList },\r\n    targetPath: string | null,\r\n    position: DropPosition,\r\n  ): void {\r\n    let parentPath = \"\";\r\n    if (targetPath) {\r\n      const targetData = this.data.find((d) => d.path === targetPath);\r\n      if (position === \"inside\" && targetData?.type === \"folder\") {\r\n        parentPath = targetPath;\r\n      } else {\r\n        parentPath = getParentPath(targetPath);\r\n      }\r\n    }\r\n\r\n    const event = this.emitEvent(\r\n      \"drop\",\r\n      parentPath,\r\n      undefined,\r\n      entries,\r\n      \"ui\",\r\n    );\r\n    if (event.defaultPrevented) return;\r\n\r\n    const files = entries.files;\r\n    for (let i = 0; i < files.length; i++) {\r\n      const f = files[i];\r\n      let filePath = parentPath ? `${parentPath}/${f.name}` : f.name;\r\n\r\n      let counter = 1;\r\n      while (this.data.some((d) => d.path === filePath)) {\r\n        const ext = getExtension(f.name);\r\n        const baseName = ext ? f.name.slice(0, -(ext.length + 1)) : f.name;\r\n        filePath = parentPath\r\n          ? `${parentPath}/${baseName}-${counter}${ext ? \".\" + ext : \"\"}`\r\n          : `${baseName}-${counter}${ext ? \".\" + ext : \"\"}`;\r\n        counter++;\r\n      }\r\n\r\n      const newNode: FileTreeNodeData = {\r\n        path: filePath,\r\n        type: \"file\",\r\n        meta: { file: f },\r\n      };\r\n\r\n      this.data.push(newNode);\r\n      this.data = normalizeData(this.data);\r\n    }\r\n\r\n    if (parentPath) this.expandedNodes.add(parentPath);\r\n    this.fullRerender();\r\n    this.emitChange();\r\n  }\r\n\r\n  // ── Full Rerender ───────────────────────────────────────\r\n\r\n  private fullRerender(): void {\r\n    this.renderTree();\r\n\r\n    // Restore expanded state from set\r\n    for (const path of this.expandedNodes) {\r\n      const node = this.nodeMap.get(path);\r\n      if (node && node.data.type === \"folder\") {\r\n        node.expanded = true;\r\n        node.arrowEl?.classList.add(\"ft-node__arrow--open\");\r\n        if (node.childrenEl) node.childrenEl.style.display = \"\";\r\n        node.iconEl.innerHTML = this.resolveIcon(node.data, node.name, true);\r\n      }\r\n    }\r\n\r\n    // Restore selection\r\n    this.applySelectionClasses();\r\n\r\n    // Restore cut highlight\r\n    this.applyCutHighlight();\r\n  }\r\n\r\n  // ── Keyboard ────────────────────────────────────────────\r\n\r\n  private onKeydown(e: KeyboardEvent): void {\r\n    if (this.renamingPath) return;\r\n\r\n    // In read-only mode only pure navigation keys are handled — everything\r\n    // else (rename, delete, copy/cut/paste, ...) is ignored.\r\n    if (this.options.readOnly) {\r\n      const isNavKey =\r\n        e.key === \"ArrowDown\" ||\r\n        e.key === \"ArrowUp\" ||\r\n        e.key === \"ArrowLeft\" ||\r\n        e.key === \"ArrowRight\" ||\r\n        e.key === \"Enter\" ||\r\n        e.key === \" \";\r\n      if (!isNavKey) return;\r\n    }\r\n\r\n    const visible = this.getVisibleNodePaths();\r\n    if (visible.length === 0) return;\r\n\r\n    const focusPath = this.lastFocusPath ?? this.selectedPath;\r\n    const currentIdx = focusPath ? visible.indexOf(focusPath) : -1;\r\n\r\n    switch (e.key) {\r\n      case \"ArrowDown\": {\r\n        e.preventDefault();\r\n        const nextIdx = Math.min(currentIdx + 1, visible.length - 1);\r\n        const next = visible[nextIdx];\r\n        if (e.shiftKey) {\r\n          this.selectNode(next, \"ui\", \"range\");\r\n        } else if (e.ctrlKey || e.metaKey) {\r\n          this.lastFocusPath = next;\r\n          this.applyFocusRing();\r\n        } else {\r\n          this.selectNode(next);\r\n        }\r\n        this.scrollIntoView(next);\r\n        break;\r\n      }\r\n      case \"ArrowUp\": {\r\n        e.preventDefault();\r\n        const prevIdx = Math.max(currentIdx - 1, 0);\r\n        const prev = visible[prevIdx];\r\n        if (e.shiftKey) {\r\n          this.selectNode(prev, \"ui\", \"range\");\r\n        } else if (e.ctrlKey || e.metaKey) {\r\n          this.lastFocusPath = prev;\r\n          this.applyFocusRing();\r\n        } else {\r\n          this.selectNode(prev);\r\n        }\r\n        this.scrollIntoView(prev);\r\n        break;\r\n      }\r\n      case \"ArrowRight\": {\r\n        e.preventDefault();\r\n        if (this.selectedPath) {\r\n          const nodeData = this.data.find((d) => d.path === this.selectedPath);\r\n          if (nodeData?.type === \"folder\") {\r\n            if (!this.expandedNodes.has(this.selectedPath)) {\r\n              this.expand(this.selectedPath);\r\n            } else {\r\n              const children = this.getChildPaths(this.selectedPath);\r\n              if (children.length > 0) {\r\n                this.selectNode(children[0]);\r\n                this.scrollIntoView(children[0]);\r\n              }\r\n            }\r\n          }\r\n        }\r\n        break;\r\n      }\r\n      case \"ArrowLeft\": {\r\n        e.preventDefault();\r\n        if (this.selectedPath) {\r\n          const nodeData = this.data.find((d) => d.path === this.selectedPath);\r\n          if (\r\n            nodeData?.type === \"folder\" &&\r\n            this.expandedNodes.has(this.selectedPath)\r\n          ) {\r\n            this.collapse(this.selectedPath);\r\n          } else {\r\n            const parentPath = getParentPath(this.selectedPath);\r\n            if (parentPath && this.nodeMap.has(parentPath)) {\r\n              this.selectNode(parentPath);\r\n              this.scrollIntoView(parentPath);\r\n            }\r\n          }\r\n        }\r\n        break;\r\n      }\r\n      case \"Enter\":\r\n        e.preventDefault();\r\n        if (this.selectedPath) {\r\n          const nodeData = this.data.find((d) => d.path === this.selectedPath);\r\n          if (nodeData?.type === \"folder\") this.toggleExpand(this.selectedPath);\r\n        }\r\n        break;\r\n      case \" \":\r\n        // Space toggles the focused node in/out of the selection. When\r\n        // Ctrl+Arrow moved focus away from the selection, this adds/removes\r\n        // that focused node; otherwise it toggles the selected node itself.\r\n        e.preventDefault();\r\n        if (this.lastFocusPath && this.nodeMap.has(this.lastFocusPath)) {\r\n          this.selectNode(this.lastFocusPath, \"ui\", \"toggle\");\r\n        } else if (this.selectedPath) {\r\n          this.selectNode(this.selectedPath, \"ui\", \"toggle\");\r\n        }\r\n        break;\r\n      case \"a\":\r\n      case \"A\": {\r\n        if (\r\n          (e.ctrlKey || e.metaKey) &&\r\n          this.options.contextMenu !== false\r\n        ) {\r\n          e.preventDefault();\r\n          this.selectAllInternal();\r\n        }\r\n        break;\r\n      }\r\n      case \"F2\": {\r\n        e.preventDefault();\r\n        if (this.selectedPath && this.options.contextMenu !== false) {\r\n          this.startRename(this.selectedPath);\r\n        }\r\n        break;\r\n      }\r\n      case \"Delete\": {\r\n        e.preventDefault();\r\n        if (this.selectedPaths.size > 0 && this.options.contextMenu !== false) {\r\n          this.deleteNode(this.getSelectedForOp());\r\n        }\r\n        break;\r\n      }\r\n      case \"c\":\r\n      case \"C\": {\r\n        if (\r\n          (e.ctrlKey || e.metaKey) &&\r\n          this.selectedPaths.size > 0 &&\r\n          this.options.contextMenu !== false &&\r\n          (this.options.contextMenu as ContextMenuOptions).copy\r\n        ) {\r\n          e.preventDefault();\r\n          this.copyToClipboard(this.getSelectedForOp());\r\n        }\r\n        break;\r\n      }\r\n      case \"x\":\r\n      case \"X\": {\r\n        if (\r\n          (e.ctrlKey || e.metaKey) &&\r\n          this.selectedPaths.size > 0 &&\r\n          this.options.contextMenu !== false &&\r\n          (this.options.contextMenu as ContextMenuOptions).cut\r\n        ) {\r\n          e.preventDefault();\r\n          this.cutNode(this.getSelectedForOp());\r\n        }\r\n        break;\r\n      }\r\n      case \"v\":\r\n      case \"V\": {\r\n        if (\r\n          (e.ctrlKey || e.metaKey) &&\r\n          this.clipboard &&\r\n          this.options.contextMenu !== false &&\r\n          (this.options.contextMenu as ContextMenuOptions).paste\r\n        ) {\r\n          e.preventDefault();\r\n          this.pasteNode();\r\n        }\r\n        break;\r\n      }\r\n    }\r\n  }\r\n\r\n  /** Toggle the keyboard-focus ring on the focused node. */\r\n  private applyFocusRing(): void {\r\n    this.nodeMap.forEach((node) => {\r\n      node.contentEl.classList.toggle(\r\n        \"ft-node__content--focused\",\r\n        node.path === this.lastFocusPath,\r\n      );\r\n    });\r\n  }\r\n\r\n  private getVisibleNodePaths(): string[] {\r\n    const paths: string[] = [];\r\n    const walk = (nodes: HierarchyNode[]): void => {\r\n      for (const node of nodes) {\r\n        paths.push(node.path);\r\n        if (\r\n          node.type === \"folder\" &&\r\n          this.expandedNodes.has(node.path) &&\r\n          node.children.length > 0\r\n        ) {\r\n          walk(node.children);\r\n        }\r\n      }\r\n    };\r\n    walk(this.hierarchy);\r\n    return paths;\r\n  }\r\n\r\n  private getChildPaths(folderPath: string): string[] {\r\n    const find = (nodes: HierarchyNode[]): HierarchyNode | null => {\r\n      for (const n of nodes) {\r\n        if (n.path === folderPath) return n;\r\n        if (n.children.length > 0) {\r\n          const found = find(n.children);\r\n          if (found) return found;\r\n        }\r\n      }\r\n      return null;\r\n    };\r\n    const node = find(this.hierarchy);\r\n    return node?.children.map((c) => c.path) ?? [];\r\n  }\r\n\r\n  private scrollIntoView(path: string): void {\r\n    const node = this.nodeMap.get(path);\r\n    node?.contentEl.scrollIntoView({ block: \"nearest\" });\r\n  }\r\n\r\n  // ── Expand Ancestors ────────────────────────────────────\r\n\r\n  private expandAncestors(path: string): void {\r\n    const segments = path.split(\"/\");\r\n    for (let i = 1; i < segments.length; i++) {\r\n      const ancestorPath = segments.slice(0, i).join(\"/\");\r\n      this.expand(ancestorPath);\r\n    }\r\n  }\r\n\r\n  // ── Events ──────────────────────────────────────────────\r\n\r\n  on(event: FileTreeEventType, handler: EventHandler): void {\r\n    this.emitter.on(event, handler);\r\n  }\r\n\r\n  off(event: FileTreeEventType, handler: EventHandler): void {\r\n    this.emitter.off(event, handler);\r\n  }\r\n\r\n  private emitEvent(\r\n    type: FileTreeEventType,\r\n    path: string,\r\n    oldPath?: string,\r\n    data?: { files: FileList; items: DataTransferItemList },\r\n    source: FileTreeEventSource = \"ui\",\r\n    paths?: string[],\r\n  ): FileTreeEvent {\r\n    const nodeData = this.data.find((d) => d.path === path);\r\n    const parentPath = getParentPath(path);\r\n    const parentNode = parentPath\r\n      ? (this.data.find((d) => d.path === parentPath) ?? null)\r\n      : null;\r\n\r\n    const event: FileTreeEvent = {\r\n      type,\r\n      source,\r\n      node: nodeData ? { ...nodeData } : { path, type: \"file\" },\r\n      path,\r\n      oldPath,\r\n      ...(paths && paths.length > 0\r\n        ? {\r\n            paths,\r\n            nodes: paths.map(\r\n              (p) => this.data.find((d) => d.path === p) ?? { path: p, type: \"file\" },\r\n            ),\r\n          }\r\n        : {}),\r\n      parentPath,\r\n      parentNode: parentNode ? { ...parentNode } : null,\r\n      tree: cloneData(this.data),\r\n      data,\r\n      defaultPrevented: false,\r\n      preventDefault() {\r\n        this.defaultPrevented = true;\r\n      },\r\n    };\r\n\r\n    this.emitter.emit(type, event);\r\n    return event;\r\n  }\r\n\r\n  private emitChange(source: FileTreeEventSource = \"ui\"): void {\r\n    const event: FileTreeEvent = {\r\n      type: \"change\",\r\n      source,\r\n      node: { path: \"\", type: \"folder\" },\r\n      path: \"\",\r\n      parentPath: \"\",\r\n      parentNode: null,\r\n      tree: cloneData(this.data),\r\n      defaultPrevented: false,\r\n      preventDefault() {\r\n        this.defaultPrevented = true;\r\n      },\r\n    };\r\n    this.emitter.emit(\"change\", event);\r\n  }\r\n\r\n  // ── Public API: Data ────────────────────────────────────\r\n\r\n  getData(): FileTreeNodeData[] {\r\n    return cloneData(this.data);\r\n  }\r\n\r\n  getNode(path: string): FileTreeNodeData | undefined {\r\n    const p = normalizePath(path);\r\n    const item = this.data.find((d) => d.path === p);\r\n    return item ? { ...item } : undefined;\r\n  }\r\n\r\n  getSelectedNode(): FileTreeNodeData | null {\r\n    if (!this.selectedPath) return null;\r\n    return this.getNode(this.selectedPath) ?? null;\r\n  }\r\n\r\n  /** All currently selected nodes (in selection order). */\r\n  getSelectedNodes(): FileTreeNodeData[] {\r\n    return this.selectedPathsArray()\r\n      .map((p) => this.getNode(p))\r\n      .filter((n): n is FileTreeNodeData => Boolean(n));\r\n  }\r\n\r\n  setData(data: FileTreeNodeData[]): void {\r\n    this.data = normalizeData(data);\r\n    this.selectedPaths.clear();\r\n    this.anchorPath = null;\r\n    this.lastFocusPath = null;\r\n    this.expandedNodes.clear();\r\n    this.fullRerender();\r\n  }\r\n\r\n  // ── Public API: Node Operations ─────────────────────────\r\n\r\n  addNode(node: FileTreeNodeData): void {\r\n    const normalized = { ...node, path: normalizePath(node.path) };\r\n    if (!normalized.path) return;\r\n    if (this.data.some((d) => d.path === normalized.path)) return;\r\n\r\n    this.data.push(normalized);\r\n    this.data = normalizeData(this.data);\r\n\r\n    this.fullRerender();\r\n\r\n    // Expand all ancestors so auto-created parent folders are visible.\r\n    this.expandAncestors(normalized.path);\r\n\r\n    this.emitEvent(\"create\", normalized.path, undefined, undefined, \"api\");\r\n    this.emitChange(\"api\");\r\n  }\r\n\r\n  /**\r\n   * Programmatically remove one or more nodes (and their descendants).\r\n   * Unlike the UI-triggered `deleteNode`, this is **not cancellable** —\r\n   * it always removes the nodes immediately. Use this from your\r\n   * confirmation callback after intercepting a `delete` event.\r\n   */\r\n  removeNode(path: string | string[]): void {\r\n    const targets = this.dedupePaths(path);\r\n    for (const p of targets) this.removeNodeInternal(p);\r\n    this.emitChange(\"api\");\r\n  }\r\n\r\n  renameNode(path: string, newName: string): void {\r\n    const p = normalizePath(path);\r\n    if (!this.isValidName(newName)) return;\r\n\r\n    const parentPath = getParentPath(p);\r\n    const newPath = parentPath ? `${parentPath}/${newName}` : newName;\r\n\r\n    if (newPath === p) return;\r\n    if (this.data.some((d) => d.path === newPath)) return;\r\n\r\n    if (newName.includes(\"/\")) {\r\n      // Slashes create intermediate folders on the fly.\r\n      this.renameToNestedPath(p, newPath, false, \"api\");\r\n      return;\r\n    }\r\n\r\n    updatePathsInData(this.data, p, newPath);\r\n    updatePathsInSet(this.expandedNodes, p, newPath);\r\n    this.updateSelectedPath(p, newPath);\r\n\r\n    this.fullRerender();\r\n\r\n    this.emitEvent(\"rename\", newPath, p, undefined, \"api\");\r\n    this.emitChange(\"api\");\r\n  }\r\n\r\n  moveNode(\r\n    sourcePath: string | string[],\r\n    targetParentPath: string | null,\r\n  ): void {\r\n    const tgt = targetParentPath ? normalizePath(targetParentPath) : \"\";\r\n    this.moveNodeInternal(sourcePath, tgt, \"api\");\r\n  }\r\n\r\n  /**\r\n   * Move a node to an exact destination path, renaming it in the same step\r\n   * (e.g. `moveTo(\"src/index.ts\", \"lib/main.ts\")`). This combines\r\n   * `moveNode` (change parent) and `renameNode` (change name). The\r\n   * destination may contain slashes: missing intermediate folders are\r\n   * auto-created, and `source` is set to `\"api\"` so a `rename` event is\r\n   * emitted (consistent with `renameNode`). Returns `false` if the move is\r\n   * invalid (conflict, or a folder moved inside itself).\r\n   */\r\n  moveTo(oldPath: string, newPath: string): boolean {\r\n    const p = normalizePath(oldPath);\r\n    const target = normalizePath(newPath);\r\n    if (p === target) return false;\r\n    return this.renameToNestedPath(p, target, false, \"api\");\r\n  }\r\n\r\n  /**\r\n   * Copy one or more nodes (and their descendants) to a new parent folder\r\n   * (`''` or `null` for root). Copying to the same location duplicates\r\n   * the node with a unique name (` copy` before the extension, e.g.\r\n   * `index copy.ts`). Emits `copy` and `create` events.\r\n   * Returns the new path(s), or `null` if the copy cannot be performed.\r\n   */\r\n  copyNode(\r\n    sourcePath: string | string[],\r\n    targetParentPath: string | null,\r\n  ): string | string[] | null {\r\n    const sources = this.dedupePaths(sourcePath);\r\n    if (sources.length === 0) return null;\r\n    const tgt = targetParentPath ? normalizePath(targetParentPath) : \"\";\r\n    const results = sources\r\n      .map((src) => this.copyNodeInternal(src, tgt, \"api\"))\r\n      .filter((r): r is string => Boolean(r));\r\n    if (results.length === 0) return null;\r\n    return results.length === 1 ? results[0] : results;\r\n  }\r\n\r\n  select(path: string | string[]): void {\r\n    // Filter to existing nodes only, so stale/missing paths are ignored\r\n    // rather than aborting the whole call.\r\n    const paths = this.dedupePaths(path).filter((p) => this.nodeMap.has(p));\r\n\r\n    if (paths.length === 0) {\r\n      // Selecting only missing paths clears the selection.\r\n      this.clearSelectionInternal(\"api\");\r\n      return;\r\n    }\r\n\r\n    if (paths.length === 1) {\r\n      const p = paths[0];\r\n      this.expandAncestors(p);\r\n      this.selectNode(p, \"api\");\r\n      this.scrollIntoView(p);\r\n    } else {\r\n      for (const p of paths) {\r\n        this.expandAncestors(p);\r\n      }\r\n      this.selectedPaths = new Set(paths);\r\n      this.anchorPath = paths[0];\r\n      this.lastFocusPath = paths[paths.length - 1];\r\n      this.applySelectionClasses();\r\n      this.emitSelectEvent(\"api\");\r\n    }\r\n  }\r\n\r\n  /** Clear the current selection. */\r\n  clearSelection(): void {\r\n    this.clearSelectionInternal(\"api\");\r\n  }\r\n\r\n  /** Select every node in the tree. */\r\n  selectAll(): void {\r\n    this.selectAllInternal(\"api\");\r\n  }\r\n\r\n  // ── Theme & Direction ───────────────────────────────────\r\n\r\n  setTheme(theme: Theme): void {\r\n    this.options.theme = theme;\r\n    this.root.dataset.theme = theme;\r\n  }\r\n\r\n  getTheme(): Theme {\r\n    return this.options.theme;\r\n  }\r\n\r\n  setDirection(direction: Direction): void {\r\n    this.options.direction = direction;\r\n    this.root.dir = direction;\r\n  }\r\n\r\n  getDirection(): Direction {\r\n    return this.options.direction;\r\n  }\r\n\r\n  // ── Destroy ─────────────────────────────────────────────\r\n\r\n  destroy(): void {\r\n    this.contextMenu.destroy();\r\n    this.dragDrop?.destroy();\r\n    this.emitter.removeAllListeners();\r\n    this.nodeMap.clear();\r\n    this.expandedNodes.clear();\r\n    this.root.remove();\r\n  }\r\n}\r\n"],"mappings":"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","names":["index_exports","__export","FileTree","createNode","defaultStrings","getExtension","getName","getParentPath","icons_exports","normalizePath","__toCommonJS","EventEmitter","event","handler","payload","err","styles_default","STYLE_ID","injectStyles","style","styles_default","ContextMenu","container","x","y","entries","entry","sep","item","html","e","rect","containerRect","left","delta","it","i","DragDrop","treeEl","callbacks","e","nodeEl","path","p","contentEl","folderEl","folderPath","folderContentEl","s","nodeData","parentFolderEl","parentPath","parentContentEl","rect","ratio","position","treeRect","topOffset","clientX","clientY","targetPath","isExternal","isRootDrop","files","items","entries","node","icons_exports","__export","chevron","collapseAllIcon","copyIcon","cutIcon","defaultIconMap","defaultNameIconMap","editIcon","expandAllIcon","file","fileCss","fileEnv","fileGif","fileGo","fileHtml","fileJava","fileJpg","fileJs","fileJson","fileJsx","fileLock","fileMd","filePhp","filePng","filePy","fileRb","fileRs","fileScss","fileSh","fileSql","fileSvg","fileToml","fileTs","fileTsx","fileTxt","fileVue","fileWebp","fileXml","fileYaml","folder","folderOpen","newFile","newFolder","pasteIcon","refreshIcon","trashIcon","svg","body","vb","filled","fileTypeBadge","text","color","normalizePath","p","getParentPath","path","idx","getName","getExtension","name","normalizeData","data","map","item","segments","i","folderPath","buildHierarchy","sort","roots","sorted","a","b","da","db","node","parent","parentNode","cmp","defaultSort","sortHierarchyNodes","nodes","nameA","nameB","cloneData","d","updatePathsInData","oldPath","newPath","oldPrefix","newPrefix","updatePathsInSet","set","toRemove","toAdd","createNode","type","meta","normalized","result","isDescendant","parentPath","childPath","defaultStrings","DEFAULT_OPTIONS","key","FileTree","container","options","EventEmitter","el","normalizeData","defaultIconMap","defaultNameIconMap","injectStyles","ContextMenu","DragDrop","path","src","tgt","pos","entries","opts","tb","cfg","buttons","newFile","newFolder","expandAllIcon","collapseAllIcon","btn","a","b","title","icon","onClick","id","e","buildHierarchy","hNode","depth","parentPath","isFolder","expanded","contentEl","arrowEl","chevron","spacer","iconEl","nameEl","childrenEl","child","internalNode","nodePath","data","name","folderOpen","folder","ext","getExtension","file","node","selected","source","emit","from","d","visible","fromIdx","toIdx","lo","hi","paths","primary","mode","list","p","normalizePath","unique","q","isDescendant","x","y","nodeData","selection","opPaths","primaryNode","addSeparator","copyIcon","cutIcon","pasteIcon","editIcon","trashIcon","visibleCustom","c","nodes","n","rootRect","targetPath","srcPaths","type","destPath","getParentPath","selData","newPaths","newPath","sourcePath","targetParentPath","silent","srcName","getName","sameLocation","baseName","counter","prefix","copies","suffix","currentName","input","dotIndex","commit","newName","isNewNode","updatePathsInData","updatePathsInSet","oldPath","rewrite","oldType","createNode","parentFolderPath","tempName","finalPath","newNode","targets","sourcePaths","position","s","newParentPath","targetData","moved","sourceName","files","i","f","filePath","focusPath","currentIdx","nextIdx","next","prevIdx","prev","children","walk","folderPath","find","found","segments","ancestorPath","event","handler","parentNode","cloneData","item","normalized","target","sources","results","theme","direction"]}