momoi-explorer 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/event-processor.ts","../src/core/flatten.ts","../src/core/selection.ts","../src/core/sort.ts","../src/core/filter.ts","../src/core/search.ts","../src/core/tree.ts","../src/core/keybindings.ts"],"sourcesContent":["// イベント処理ユーティリティ\r\n//\r\n// VSCode準拠のデバウンス・イベント合体・スロットリング\r\n\r\nimport type { RawWatchEvent, WatchEvent, WatchOptions } from './types'\r\n\r\nconst DEFAULT_DEBOUNCE_MS = 75\r\nconst DEFAULT_THROTTLE_CHUNK_SIZE = 500\r\nconst DEFAULT_THROTTLE_DELAY_MS = 200\r\n\r\n/**\r\n * 生イベントを合体する(VSCode EventCoalescer 準拠)。\r\n * - rename → delete(old) + create(new) に分解\r\n * - delete + create(同一パス) → modify に合体\r\n * - create + modify(同一パス) → create を維持\r\n * - 親フォルダ delete → 子の delete を除去\r\n *\r\n * @param raw - アダプタから受け取った生イベントの配列\r\n * @returns 合体処理後のWatchEvent配列\r\n */\r\nexport function coalesceEvents(raw: RawWatchEvent[]): WatchEvent[] {\r\n // rename を分解\r\n const expanded: WatchEvent[] = []\r\n for (const event of raw) {\r\n if (event.type === 'rename' && event.newPath) {\r\n expanded.push({ type: 'delete', path: event.path, isDirectory: event.isDirectory })\r\n expanded.push({ type: 'create', path: event.newPath, isDirectory: event.isDirectory })\r\n } else {\r\n expanded.push({ type: event.type as WatchEvent['type'], path: event.path, isDirectory: event.isDirectory })\r\n }\r\n }\r\n\r\n // 同一パスのイベントを合体\r\n const byPath = new Map<string, WatchEvent>()\r\n for (const event of expanded) {\r\n const existing = byPath.get(event.path)\r\n if (!existing) {\r\n byPath.set(event.path, event)\r\n continue\r\n }\r\n\r\n // delete + create → modify\r\n if (existing.type === 'delete' && event.type === 'create') {\r\n byPath.set(event.path, { type: 'modify', path: event.path, isDirectory: event.isDirectory })\r\n continue\r\n }\r\n\r\n // create + modify → create を維持\r\n if (existing.type === 'create' && event.type === 'modify') {\r\n continue\r\n }\r\n\r\n // create + delete → 相殺して除去\r\n if (existing.type === 'create' && event.type === 'delete') {\r\n byPath.delete(event.path)\r\n continue\r\n }\r\n\r\n // それ以外は後勝ち\r\n byPath.set(event.path, event)\r\n }\r\n\r\n const result = Array.from(byPath.values())\r\n\r\n // 親フォルダ delete がある場合、子の delete を除去\r\n const deletedDirs = new Set<string>()\r\n for (const event of result) {\r\n if (event.type === 'delete' && event.isDirectory) {\r\n deletedDirs.add(event.path)\r\n }\r\n }\r\n\r\n if (deletedDirs.size === 0) return result\r\n\r\n return result.filter((event) => {\r\n if (event.type !== 'delete') return true\r\n for (const dir of deletedDirs) {\r\n if (event.path !== dir && event.path.startsWith(dir + '/')) {\r\n return false\r\n }\r\n }\r\n return true\r\n })\r\n}\r\n\r\n/**\r\n * イベントプロセッサを生成する。\r\n * デバウンス → 合体 → スロットリング → コールバック のパイプラインで処理する。\r\n *\r\n * @param callback - 処理済みイベントを受け取るコールバック\r\n * @param options - デバウンス・合体・スロットリングの設定\r\n * @returns push/flush/destroyメソッドを持つプロセッサオブジェクト\r\n */\r\nexport function createEventProcessor(\r\n callback: (events: WatchEvent[]) => void,\r\n options: WatchOptions = {},\r\n): {\r\n push(events: RawWatchEvent[]): void\r\n flush(): void\r\n destroy(): void\r\n} {\r\n const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS\r\n const shouldCoalesce = options.coalesce ?? true\r\n const throttleChunkSize = options.throttle?.maxChunkSize ?? DEFAULT_THROTTLE_CHUNK_SIZE\r\n const throttleDelayMs = options.throttle?.delayMs ?? DEFAULT_THROTTLE_DELAY_MS\r\n\r\n let buffer: RawWatchEvent[] = []\r\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\r\n let throttleTimer: ReturnType<typeof setTimeout> | null = null\r\n let destroyed = false\r\n\r\n function processBuffer(): void {\r\n if (destroyed || buffer.length === 0) return\r\n\r\n const raw = buffer\r\n buffer = []\r\n\r\n const events = shouldCoalesce ? coalesceEvents(raw) : raw.map((e) => ({\r\n type: e.type === 'rename' ? 'modify' as const : e.type as WatchEvent['type'],\r\n path: e.type === 'rename' && e.newPath ? e.newPath : e.path,\r\n isDirectory: e.isDirectory,\r\n }))\r\n\r\n if (events.length === 0) return\r\n\r\n // スロットリング: 大量イベントをチャンクに分割\r\n if (events.length <= throttleChunkSize) {\r\n callback(events)\r\n return\r\n }\r\n\r\n let offset = 0\r\n function emitChunk(): void {\r\n if (destroyed || offset >= events.length) return\r\n const chunk = events.slice(offset, offset + throttleChunkSize)\r\n offset += throttleChunkSize\r\n callback(chunk)\r\n if (offset < events.length) {\r\n throttleTimer = setTimeout(emitChunk, throttleDelayMs)\r\n }\r\n }\r\n emitChunk()\r\n }\r\n\r\n return {\r\n push(events: RawWatchEvent[]): void {\r\n if (destroyed) return\r\n buffer.push(...events)\r\n if (debounceTimer !== null) {\r\n clearTimeout(debounceTimer)\r\n }\r\n debounceTimer = setTimeout(processBuffer, debounceMs)\r\n },\r\n\r\n flush(): void {\r\n if (debounceTimer !== null) {\r\n clearTimeout(debounceTimer)\r\n debounceTimer = null\r\n }\r\n processBuffer()\r\n },\r\n\r\n destroy(): void {\r\n destroyed = true\r\n if (debounceTimer !== null) clearTimeout(debounceTimer)\r\n if (throttleTimer !== null) clearTimeout(throttleTimer)\r\n buffer = []\r\n },\r\n }\r\n}\r\n","// ツリー → フラットリスト変換(仮想スクロール用)\n\nimport type { FlatNode, TreeNode } from './types'\n\n/**\n * 展開されたツリーをフラットリストに変換する。\n * react-virtuoso等の仮想スクロールに渡すためのもの。\n * matchingPaths が指定された場合、そこに含まれるノードのみ表示する。\n */\nexport function flattenTree(\n nodes: TreeNode[],\n expandedPaths: Set<string>,\n matchingPaths?: Set<string> | null,\n): FlatNode[] {\n const result: FlatNode[] = []\n\n function walk(children: TreeNode[], depth: number): void {\n for (const node of children) {\n if (matchingPaths && !matchingPaths.has(node.path)) continue\n\n result.push({ node, depth })\n if (node.isDirectory && node.children) {\n // 検索中はマッチしたディレクトリを自動展開\n if (matchingPaths || expandedPaths.has(node.path)) {\n walk(node.children, depth + 1)\n }\n }\n }\n }\n\n walk(nodes, 0)\n return result\n}\n","// 選択管理(単一/複数/範囲)\n\nimport type { FlatNode } from './types'\n\n/**\n * replace: 既存選択をクリアして新しいパスのみ選択\n * toggle: 指定パスの選択を反転(Ctrl+Click)\n * range: anchorから指定パスまでの範囲を選択(Shift+Click)\n */\nexport function computeSelection(\n currentSelected: Set<string>,\n anchorPath: string | null,\n targetPath: string,\n mode: 'replace' | 'toggle' | 'range',\n flatList: FlatNode[],\n): { selectedPaths: Set<string>; anchorPath: string } {\n switch (mode) {\n case 'replace':\n return {\n selectedPaths: new Set([targetPath]),\n anchorPath: targetPath,\n }\n\n case 'toggle': {\n const next = new Set(currentSelected)\n if (next.has(targetPath)) {\n next.delete(targetPath)\n } else {\n next.add(targetPath)\n }\n return {\n selectedPaths: next,\n anchorPath: targetPath,\n }\n }\n\n case 'range': {\n if (!anchorPath) {\n return {\n selectedPaths: new Set([targetPath]),\n anchorPath: targetPath,\n }\n }\n\n const paths = flatList.map((f) => f.node.path)\n const anchorIdx = paths.indexOf(anchorPath)\n const targetIdx = paths.indexOf(targetPath)\n\n if (anchorIdx === -1 || targetIdx === -1) {\n return {\n selectedPaths: new Set([targetPath]),\n anchorPath: targetPath,\n }\n }\n\n const start = Math.min(anchorIdx, targetIdx)\n const end = Math.max(anchorIdx, targetIdx)\n const rangePaths = new Set(paths.slice(start, end + 1))\n\n return {\n selectedPaths: rangePaths,\n anchorPath, // range選択ではanchorは変えない\n }\n }\n }\n}\n","// ソート\n\nimport type { FileEntry } from './types'\n\n/**\n * デフォルトソート: フォルダ優先 → 名前の大文字小文字無視で昇順\n */\nexport function defaultSort(a: FileEntry, b: FileEntry): number {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1\n }\n return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })\n}\n","// フィルタ\n\nimport type { FileEntry } from './types'\n\n/**\n * デフォルトフィルタ: 全表示(フィルタなし)\n */\nexport function defaultFilter(_entry: FileEntry): boolean {\n return true\n}\n","// ファイル名検索ユーティリティ\n\nimport type { FileEntry, TreeNode } from './types'\n\n/**\n * ファジーマッチ: クエリの各文字が順番に含まれているかチェック\n * スコアも返す(連続マッチ・先頭マッチ・セパレータ後マッチが高スコア)\n */\nexport function fuzzyMatch(query: string, target: string): { match: boolean; score: number } {\n const q = query.toLowerCase()\n const t = target.toLowerCase()\n\n if (q.length === 0) return { match: true, score: 0 }\n if (q.length > t.length) return { match: false, score: 0 }\n\n // 完全一致は最高スコア\n if (t === q) return { match: true, score: 100 }\n\n // 前方一致は高スコア\n if (t.startsWith(q)) return { match: true, score: 90 }\n\n // 部分一致\n if (t.includes(q)) return { match: true, score: 80 }\n\n // ファジーマッチ\n let qi = 0\n let score = 0\n let lastMatchIndex = -2\n\n for (let ti = 0; ti < t.length && qi < q.length; ti++) {\n if (t[ti] === q[qi]) {\n qi++\n // 連続マッチボーナス\n if (ti === lastMatchIndex + 1) {\n score += 5\n }\n // セパレータ直後のマッチボーナス(パス区切り、ハイフン、アンダースコア等)\n if (ti === 0 || '/\\\\-_.'.includes(t[ti - 1])) {\n score += 10\n }\n score += 1\n lastMatchIndex = ti\n }\n }\n\n if (qi < q.length) return { match: false, score: 0 }\n return { match: true, score }\n}\n\n/**\n * ツリーフィルタ用: クエリにマッチするノードとその祖先を残す\n * マッチしたパスのSetを返す\n */\nexport function findMatchingPaths(\n nodes: TreeNode[],\n query: string,\n): Set<string> {\n const matching = new Set<string>()\n\n function walk(node: TreeNode, ancestors: string[]): boolean {\n const nameMatch = fuzzyMatch(query, node.name).match\n let childMatch = false\n\n if (node.children) {\n for (const child of node.children) {\n if (walk(child, [...ancestors, node.path])) {\n childMatch = true\n }\n }\n }\n\n if (nameMatch || childMatch) {\n matching.add(node.path)\n for (const a of ancestors) {\n matching.add(a)\n }\n return true\n }\n return false\n }\n\n for (const node of nodes) {\n walk(node, [])\n }\n\n return matching\n}\n\n/**\n * ファジーファインド用: 全ファイルからスコア順に候補を返す\n */\nexport function fuzzyFind(\n files: FileEntry[],\n query: string,\n maxResults: number = 50,\n): FileEntry[] {\n if (!query) return []\n\n const scored: Array<{ entry: FileEntry; score: number }> = []\n\n for (const entry of files) {\n // ファイル名でマッチ\n const nameResult = fuzzyMatch(query, entry.name)\n // パスでもマッチ(スコアは低め)\n const pathResult = fuzzyMatch(query, entry.path)\n\n const bestScore = Math.max(nameResult.score, pathResult.score * 0.5)\n\n if (nameResult.match || pathResult.match) {\n scored.push({ entry, score: bestScore })\n }\n }\n\n scored.sort((a, b) => b.score - a.score)\n return scored.slice(0, maxResults).map((s) => s.entry)\n}\n","// createFileTree - ヘッドレスファイルツリーコントローラ\r\n\r\nimport type {\r\n FileEntry,\r\n FileSystemAdapter,\r\n FileTreeController,\r\n FileTreeOptions,\r\n FlatNode,\r\n TreeEvent,\r\n TreeNode,\r\n TreeState,\r\n WatchEvent,\r\n} from './types'\r\nimport { createEventProcessor } from './event-processor'\r\nimport { flattenTree } from './flatten'\r\nimport { computeSelection } from './selection'\r\nimport { defaultSort } from './sort'\r\nimport { defaultFilter } from './filter'\r\nimport { findMatchingPaths } from './search'\r\n\r\nfunction toTreeNode(entry: FileEntry, depth: number): TreeNode {\r\n return {\r\n ...entry,\r\n depth,\r\n children: entry.isDirectory ? undefined : undefined,\r\n childrenLoaded: false,\r\n }\r\n}\r\n\r\nfunction findNode(nodes: TreeNode[], path: string): TreeNode | undefined {\r\n for (const node of nodes) {\r\n if (node.path === path) return node\r\n if (node.children) {\r\n const found = findNode(node.children, path)\r\n if (found) return found\r\n }\r\n }\r\n return undefined\r\n}\r\n\r\nfunction findParentNodes(nodes: TreeNode[], targetPath: string): TreeNode | undefined {\r\n for (const node of nodes) {\r\n if (node.children) {\r\n for (const child of node.children) {\r\n if (child.path === targetPath) return node\r\n }\r\n const found = findParentNodes(node.children, targetPath)\r\n if (found) return found\r\n }\r\n }\r\n return undefined\r\n}\r\n\r\n/** パスの親ディレクトリを取得 */\r\nfunction dirname(path: string): string {\r\n const sep = path.includes('\\\\') ? '\\\\' : '/'\r\n const idx = path.lastIndexOf(sep)\r\n return idx === -1 ? '' : path.slice(0, idx)\r\n}\r\n\r\n/** パスの末尾(ファイル名)を取得 */\r\nfunction basename(path: string): string {\r\n const sep = path.includes('\\\\') ? '\\\\' : '/'\r\n const idx = path.lastIndexOf(sep)\r\n return idx === -1 ? path : path.slice(idx + 1)\r\n}\r\n\r\n/**\r\n * ヘッドレスファイルツリーコントローラを生成する。\r\n * フレームワーク非依存。React等で使う場合は `momoi-explorer/react` のTreeProviderを推奨。\r\n *\r\n * @param options - ツリーの初期化オプション\r\n * @returns FileTreeController インスタンス\r\n *\r\n * @example\r\n * ```ts\r\n * import { createFileTree } from 'momoi-explorer'\r\n *\r\n * const tree = createFileTree({\r\n * adapter: myAdapter,\r\n * rootPath: '/home/user/project',\r\n * onEvent: (e) => console.log(e),\r\n * })\r\n * await tree.loadRoot()\r\n * ```\r\n */\r\nexport function createFileTree(options: FileTreeOptions): FileTreeController {\r\n const { adapter, rootPath, onEvent } = options\r\n let sortFn = options.sort ?? defaultSort\r\n let filterFn = options.filter ?? defaultFilter\r\n\r\n let state: TreeState = {\r\n rootPath,\r\n rootNodes: [],\r\n expandedPaths: new Set(),\r\n selectedPaths: new Set(),\r\n anchorPath: null,\r\n renamingPath: null,\r\n creatingState: null,\r\n searchQuery: null,\r\n flatList: [],\r\n }\r\n\r\n const listeners = new Set<(state: TreeState) => void>()\r\n let expandingPaths = new Set<string>()\r\n\r\n function emit(event: TreeEvent): void {\r\n onEvent?.(event)\r\n }\r\n\r\n function notify(): void {\r\n const matchingPaths = state.searchQuery\r\n ? findMatchingPaths(state.rootNodes, state.searchQuery)\r\n : null\r\n state = { ...state, flatList: flattenTree(state.rootNodes, state.expandedPaths, matchingPaths) }\r\n for (const listener of listeners) {\r\n listener(state)\r\n }\r\n }\r\n\r\n async function loadChildren(node: TreeNode): Promise<void> {\r\n const entries = await adapter.readDir(node.path)\r\n const filtered = entries.filter(filterFn)\r\n filtered.sort(sortFn)\r\n // 既存の展開済み子ノードのchildrenを引き継ぐ\r\n const oldChildMap = node.children\r\n ? new Map(node.children.map((c) => [c.path, c]))\r\n : new Map<string, TreeNode>()\r\n node.children = filtered.map((e) => {\r\n const existing = oldChildMap.get(e.path)\r\n if (existing && existing.childrenLoaded) {\r\n return { ...toTreeNode(e, node.depth + 1), children: existing.children, childrenLoaded: true }\r\n }\r\n return toTreeNode(e, node.depth + 1)\r\n })\r\n node.childrenLoaded = true\r\n }\r\n\r\n /** 親ディレクトリをリフレッシュし、展開中のフォルダの子も再読み込みする */\r\n async function refreshParent(parentPath: string): Promise<void> {\r\n const parentNode = findNode(state.rootNodes, parentPath)\r\n if (parentNode) {\r\n await loadChildren(parentNode)\r\n state.expandedPaths = new Set(state.expandedPaths)\r\n state.expandedPaths.add(parentPath)\r\n } else if (parentPath === rootPath) {\r\n const entries = await adapter.readDir(rootPath)\r\n const filtered = entries.filter(filterFn)\r\n filtered.sort(sortFn)\r\n // 既存の展開済みノードのchildrenを引き継ぐ\r\n const oldNodeMap = new Map(state.rootNodes.map((n) => [n.path, n]))\r\n state.rootNodes = filtered.map((e) => {\r\n const existing = oldNodeMap.get(e.path)\r\n if (existing && existing.childrenLoaded) {\r\n return { ...toTreeNode(e, 0), children: existing.children, childrenLoaded: true }\r\n }\r\n return toTreeNode(e, 0)\r\n })\r\n }\r\n }\r\n\r\n function sortNodes(nodes: TreeNode[]): void {\r\n nodes.sort(sortFn)\r\n for (const node of nodes) {\r\n if (node.children) {\r\n sortNodes(node.children)\r\n }\r\n }\r\n }\r\n\r\n function filterNodes(nodes: TreeNode[]): TreeNode[] {\r\n return nodes.filter((node) => {\r\n if (!filterFn(node)) return false\r\n if (node.children) {\r\n node.children = filterNodes(node.children)\r\n }\r\n return true\r\n })\r\n }\r\n\r\n // ウォッチ関連\r\n let unwatchFn: (() => void) | null = null\r\n let eventProcessor: ReturnType<typeof createEventProcessor> | null = null\r\n\r\n function handleWatchEvents(events: WatchEvent[]): void {\r\n emit({ type: 'external-change', changes: events })\r\n\r\n // 変更されたパスの親ディレクトリを収集してリフレッシュ\r\n const dirsToRefresh = new Set<string>()\r\n for (const event of events) {\r\n const parent = dirname(event.path)\r\n // 展開中のディレクトリのみリフレッシュ\r\n if (parent === rootPath || state.expandedPaths.has(parent)) {\r\n dirsToRefresh.add(parent)\r\n }\r\n // ディレクトリ自体が変更された場合、展開中ならそれもリフレッシュ\r\n if (event.isDirectory && state.expandedPaths.has(event.path)) {\r\n dirsToRefresh.add(event.path)\r\n }\r\n }\r\n\r\n // 非同期でリフレッシュ(展開状態を保持)\r\n for (const dir of dirsToRefresh) {\r\n refreshParent(dir).then(() => notify()).catch(() => {})\r\n }\r\n }\r\n\r\n function startWatching(): void {\r\n if (!adapter.watch) return\r\n\r\n eventProcessor = createEventProcessor(handleWatchEvents, options.watchOptions)\r\n unwatchFn = adapter.watch(rootPath, (events) => {\r\n eventProcessor!.push(events)\r\n })\r\n }\r\n\r\n function stopWatching(): void {\r\n if (unwatchFn) {\r\n unwatchFn()\r\n unwatchFn = null\r\n }\r\n if (eventProcessor) {\r\n eventProcessor.destroy()\r\n eventProcessor = null\r\n }\r\n }\r\n\r\n const controller: FileTreeController = {\r\n getState(): TreeState {\r\n return state\r\n },\r\n\r\n subscribe(listener: (s: TreeState) => void): () => void {\r\n listeners.add(listener)\r\n return () => listeners.delete(listener)\r\n },\r\n\r\n async loadRoot(): Promise<void> {\r\n const entries = await adapter.readDir(rootPath)\r\n const filtered = entries.filter(filterFn)\r\n filtered.sort(sortFn)\r\n state.rootNodes = filtered.map((e) => toTreeNode(e, 0))\r\n notify()\r\n startWatching()\r\n },\r\n\r\n async expand(path: string): Promise<void> {\r\n // 再入ガード: 同じパスの展開処理が進行中なら無視\r\n if (expandingPaths.has(path)) return\r\n expandingPaths.add(path)\r\n\r\n try {\r\n const node = findNode(state.rootNodes, path)\r\n if (!node || !node.isDirectory) return\r\n\r\n if (!node.childrenLoaded) {\r\n await loadChildren(node)\r\n }\r\n\r\n // await後にstateが変わっている可能性があるので再確認\r\n state.expandedPaths = new Set(state.expandedPaths)\r\n state.expandedPaths.add(path)\r\n notify()\r\n emit({ type: 'expand', path })\r\n } finally {\r\n expandingPaths.delete(path)\r\n }\r\n },\r\n\r\n collapse(path: string): void {\r\n const sep = path.includes('\\\\') ? '\\\\' : '/'\r\n const prefix = path + sep\r\n state.expandedPaths = new Set(state.expandedPaths)\r\n state.expandedPaths.delete(path)\r\n // 子孫の展開状態もクリア\r\n for (const p of state.expandedPaths) {\r\n if (p.startsWith(prefix)) {\r\n state.expandedPaths.delete(p)\r\n }\r\n }\r\n notify()\r\n emit({ type: 'collapse', path })\r\n },\r\n\r\n async toggleExpand(path: string): Promise<void> {\r\n // expand進行中は無視(ダブルクリックで展開→即collapseを防止)\r\n if (expandingPaths.has(path)) return\r\n\r\n if (state.expandedPaths.has(path)) {\r\n controller.collapse(path)\r\n } else {\r\n await controller.expand(path)\r\n }\r\n },\r\n\r\n async expandTo(path: string): Promise<void> {\r\n // パスの各祖先を展開していく\r\n const parts: string[] = []\r\n let current = path\r\n while (current !== rootPath && current !== '') {\r\n const parent = dirname(current)\r\n if (parent === current) break\r\n parts.unshift(parent)\r\n current = parent\r\n }\r\n\r\n for (const ancestorPath of parts) {\r\n if (ancestorPath === rootPath) continue\r\n if (!state.expandedPaths.has(ancestorPath)) {\r\n await controller.expand(ancestorPath)\r\n }\r\n }\r\n },\r\n\r\n select(path: string, mode: 'replace' | 'toggle' | 'range' = 'replace'): void {\r\n const result = computeSelection(\r\n state.selectedPaths,\r\n state.anchorPath,\r\n path,\r\n mode,\r\n state.flatList,\r\n )\r\n state.selectedPaths = result.selectedPaths\r\n state.anchorPath = result.anchorPath\r\n notify()\r\n emit({ type: 'select', paths: Array.from(result.selectedPaths) })\r\n },\r\n\r\n selectAll(): void {\r\n state.selectedPaths = new Set(state.flatList.map((f) => f.node.path))\r\n notify()\r\n emit({ type: 'select', paths: Array.from(state.selectedPaths) })\r\n },\r\n\r\n clearSelection(): void {\r\n state.selectedPaths = new Set()\r\n state.anchorPath = null\r\n notify()\r\n emit({ type: 'select', paths: [] })\r\n },\r\n\r\n startRename(path: string): void {\r\n state.renamingPath = path\r\n notify()\r\n },\r\n\r\n async commitRename(newName: string): Promise<void> {\r\n if (!state.renamingPath || !adapter.rename) return\r\n\r\n const oldPath = state.renamingPath\r\n const parent = dirname(oldPath)\r\n const sep = oldPath.includes('\\\\') ? '\\\\' : '/'\r\n const newPath = parent + sep + newName\r\n\r\n await adapter.rename(oldPath, newPath)\r\n state.renamingPath = null\r\n\r\n // リネームされたノードの親をリフレッシュ\r\n if (parent === rootPath) {\r\n await controller.loadRoot()\r\n } else {\r\n const parentNode = findNode(state.rootNodes, parent)\r\n if (parentNode) {\r\n await loadChildren(parentNode)\r\n }\r\n }\r\n\r\n notify()\r\n emit({ type: 'rename', oldPath, newPath })\r\n },\r\n\r\n cancelRename(): void {\r\n state.renamingPath = null\r\n notify()\r\n },\r\n\r\n async startCreate(parentPath: string, isDirectory: boolean, insertAfterPath?: string): Promise<void> {\r\n // 親フォルダを展開してから作成モードに入る\r\n if (parentPath !== rootPath && !state.expandedPaths.has(parentPath)) {\r\n await controller.expand(parentPath)\r\n }\r\n state.creatingState = { parentPath, isDirectory, insertAfterPath }\r\n notify()\r\n },\r\n\r\n async commitCreate(name: string): Promise<void> {\r\n if (!state.creatingState) return\r\n\r\n const { parentPath, isDirectory } = state.creatingState\r\n state.creatingState = null\r\n\r\n if (isDirectory) {\r\n await controller.createDir(parentPath, name)\r\n } else {\r\n await controller.createFile(parentPath, name)\r\n }\r\n },\r\n\r\n cancelCreate(): void {\r\n state.creatingState = null\r\n notify()\r\n },\r\n\r\n async createFile(parentPath: string, name: string): Promise<void> {\r\n if (!adapter.createFile) return\r\n await adapter.createFile(parentPath, name)\r\n await refreshParent(parentPath)\r\n\r\n notify()\r\n emit({ type: 'create', parentPath, name, isDirectory: false })\r\n },\r\n\r\n async createDir(parentPath: string, name: string): Promise<void> {\r\n if (!adapter.createDir) return\r\n await adapter.createDir(parentPath, name)\r\n await refreshParent(parentPath)\r\n\r\n notify()\r\n emit({ type: 'create', parentPath, name, isDirectory: true })\r\n },\r\n\r\n async deleteSelected(): Promise<void> {\r\n if (!adapter.delete || state.selectedPaths.size === 0) return\r\n\r\n const paths = Array.from(state.selectedPaths)\r\n await adapter.delete(paths)\r\n\r\n state.selectedPaths = new Set()\r\n state.anchorPath = null\r\n\r\n // 影響を受ける親ディレクトリをリフレッシュ\r\n const parentDirs = new Set(paths.map(dirname))\r\n for (const dir of parentDirs) {\r\n await refreshParent(dir)\r\n }\r\n\r\n notify()\r\n emit({ type: 'delete', paths })\r\n },\r\n\r\n async refresh(path?: string): Promise<void> {\r\n if (!path || path === rootPath) {\r\n await refreshParent(rootPath)\r\n } else {\r\n const node = findNode(state.rootNodes, path)\r\n if (node && node.isDirectory) {\r\n await loadChildren(node)\r\n }\r\n }\r\n\r\n notify()\r\n emit({ type: 'refresh', path })\r\n },\r\n\r\n setSearchQuery(query: string | null): void {\r\n state.searchQuery = query && query.trim() ? query.trim() : null\r\n notify()\r\n },\r\n\r\n async collectAllFiles(): Promise<FileEntry[]> {\r\n const result: FileEntry[] = []\r\n\r\n async function walk(dirPath: string): Promise<void> {\r\n const entries = await adapter.readDir(dirPath)\r\n for (const entry of entries) {\r\n if (!filterFn(entry)) continue\r\n result.push(entry)\r\n if (entry.isDirectory) {\r\n await walk(entry.path)\r\n }\r\n }\r\n }\r\n\r\n await walk(rootPath)\r\n return result\r\n },\r\n\r\n setFilter(fn: ((entry: FileEntry) => boolean) | null): void {\r\n filterFn = fn ?? defaultFilter\r\n // 既に読み込み済みのノードを再フィルタ\r\n state.rootNodes = filterNodes(state.rootNodes)\r\n notify()\r\n },\r\n\r\n setSort(fn: ((a: FileEntry, b: FileEntry) => number) | null): void {\r\n sortFn = fn ?? defaultSort\r\n sortNodes(state.rootNodes)\r\n notify()\r\n },\r\n\r\n destroy(): void {\r\n stopWatching()\r\n listeners.clear()\r\n },\r\n }\r\n\r\n return controller\r\n}\r\n","// エクスプローラー用のコマンドIDとデフォルトキーバインド定義\n//\n// momoi-keybind の KeybindingEntry 互換の形式でエクスポートする。\n// momoi-keybind が無くても型として参照できるように、独自の型を定義。\n\n/** momoi-keybind の KeybindingEntry と互換の型 */\nexport interface ExplorerKeybindingEntry {\n key: string\n command: string\n when?: string\n args?: unknown\n}\n\n/** エクスプローラーのコマンドID */\nexport const ExplorerCommands = {\n DELETE: 'explorer.delete',\n RENAME: 'explorer.rename',\n NEW_FILE: 'explorer.newFile',\n NEW_FOLDER: 'explorer.newFolder',\n REFRESH: 'explorer.refresh',\n COLLAPSE_ALL: 'explorer.collapseAll',\n SELECT_ALL: 'explorer.selectAll',\n COPY_PATH: 'explorer.copyPath',\n} as const\n\nexport type ExplorerCommandId = (typeof ExplorerCommands)[keyof typeof ExplorerCommands]\n\n/** デフォルトのキーバインド定義。momoi-keybindのInputServiceに渡せる形式 */\nexport const defaultExplorerKeybindings: ExplorerKeybindingEntry[] = [\n { key: 'Delete', command: ExplorerCommands.DELETE, when: 'explorerFocus' },\n { key: 'F2', command: ExplorerCommands.RENAME, when: 'explorerFocus' },\n { key: 'Ctrl+N', command: ExplorerCommands.NEW_FILE, when: 'explorerFocus' },\n { key: 'Ctrl+Shift+N', command: ExplorerCommands.NEW_FOLDER, when: 'explorerFocus' },\n { key: 'Ctrl+R', command: ExplorerCommands.REFRESH, when: 'explorerFocus' },\n { key: 'Ctrl+Shift+E', command: ExplorerCommands.COLLAPSE_ALL, when: 'explorerFocus' },\n { key: 'Ctrl+A', command: ExplorerCommands.SELECT_ALL, when: 'explorerFocus' },\n { key: 'Ctrl+Shift+C', command: ExplorerCommands.COPY_PATH, when: 'explorerFocus' },\n]\n"],"mappings":"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","names":[]}