```dataviewjs
// ==========================================
// 1. 설정: 타겟 폴더 경로
// ==========================================
const targetPath = "Project";

// ==========================================
// 2. 실행 로직 (바로가기 폴더 지원)
// ==========================================
try {
    // 1. 컨테이너 초기화
    dv.container.innerHTML = "";

    // 2. 설정 키 (저장용)
    const KEY_TREE_PX = "dv_layout_tree_px_v10"; 
    const KEY_HEIGHT_PX = "dv_layout_height_px_v10";
    const KEY_SORT_STATE = "dv_sort_state_v10";
    const KEY_COL_NAME_PX = "dv_col_name_px_v2";
    const KEY_COL_DATE_PX = "dv_col_date_px_v2";

    // ★ 3. 바로가기 폴더 해결 함수
    async function resolveSymlinkPath(path) {
        try {
            const folder = app.vault.getAbstractFileByPath(path);
            if (!folder) return null;
            
            // 실제 파일시스템 경로 가져오기 (symlink 해결)
            const adapter = app.vault.adapter;
            if (adapter && adapter.basePath) {
                const fullPath = adapter.basePath + "/" + path;
                // Node.js fs를 사용할 수 없으므로 Obsidian API 활용
                return path; // 일단 원본 경로 반환
            }
            return path;
        } catch (e) {
            return path;
        }
    }

    // ★ 4. 향상된 파일 수집 함수 (바로가기 폴더 포함)
    async function getAllFilesIncludingSymlinks(basePath) {
        const allFiles = [];
        const adapter = app.vault.adapter;
        
        // 방법 1: Obsidian의 getAllLoadedFiles 사용 (모든 로드된 파일)
        const loadedFiles = app.vault.getAllLoadedFiles();
        
        for (const file of loadedFiles) {
            if (file.path && file.path.startsWith(basePath)) {
                if (file.children === undefined) { // 파일만 (폴더 제외)
                    allFiles.push(file);
                }
            }
        }
        
        // 방법 2: adapter를 통한 직접 읽기 시도 (symlink 대응)
        if (adapter && adapter.list) {
            try {
                const listing = await adapter.list(basePath);
                if (listing && listing.files) {
                    for (const filePath of listing.files) {
                        const existingFile = app.vault.getAbstractFileByPath(filePath);
                        if (existingFile && !allFiles.find(f => f.path === filePath)) {
                            allFiles.push(existingFile);
                        }
                    }
                }
            } catch (e) {
                console.warn("Symlink 폴더 읽기 실패:", e);
            }
        }
        
        return allFiles;
    }

    // ★ 5. 폴더 구조 수집 함수 (바로가기 폴더 포함)
    async function getAllFoldersIncludingSymlinks(basePath) {
        const folders = new Set();
        folders.add(basePath);
        
        const adapter = app.vault.adapter;
        
        // 방법 1: 로드된 폴더들
        const loadedFiles = app.vault.getAllLoadedFiles();
        for (const item of loadedFiles) {
            if (item.path && item.path.startsWith(basePath)) {
                if (item.children !== undefined) { // 폴더
                    folders.add(item.path);
                }
                // 파일의 부모 폴더들도 추가
                if (item.path.includes("/")) {
                    const parts = item.path.split("/");
                    for (let i = 0; i < parts.length - 1; i++) {
                        const folderPath = parts.slice(0, i + 1).join("/");
                        if (folderPath.startsWith(basePath)) {
                            folders.add(folderPath);
                        }
                    }
                }
            }
        }
        
        // 방법 2: adapter를 통한 재귀 탐색
        async function scanDir(dirPath) {
            if (!adapter || !adapter.list) return;
            try {
                const listing = await adapter.list(dirPath);
                if (listing && listing.folders) {
                    for (const folderPath of listing.folders) {
                        if (folderPath.startsWith(basePath)) {
                            folders.add(folderPath);
                            await scanDir(folderPath); // 재귀
                        }
                    }
                }
            } catch (e) {
                // symlink 접근 실패 무시
            }
        }
        
        await scanDir(basePath);
        
        return folders;
    }

    // 6. 파일 및 폴더 데이터 가져오기
    let allFiles = await getAllFilesIncludingSymlinks(targetPath);
    let folderPaths = await getAllFoldersIncludingSymlinks(targetPath);
    
    allFiles.sort((a, b) => a.name.localeCompare(b.name));

    if (allFiles.length === 0) {
        dv.container.innerHTML = `
            <div style="padding:20px; background:#330000; color:red; border:1px solid red; border-radius:5px;">
                <strong>⚠️ 파일을 찾을 수 없습니다.</strong><br>
                가능한 원인:<br>
                1. 바로가기 폴더가 Obsidian에 제대로 로드되지 않음<br>
                2. 폴더 경로 오류: <code>${targetPath}</code><br>
                3. 바로가기 대상 폴더에 파일이 없음<br><br>
                <strong>해결 방법:</strong><br>
                - Obsidian 재시작 후 다시 시도<br>
                - 설정 → 파일 및 링크 → "감지된 새 링크" 확인<br>
                - targetPath 경로를 실제 폴더 경로로 변경
            </div>`;
    } else {
        // ==========================================
        // 7. 렌더링 함수들
        // ==========================================
        function getIcon(ext) {
            const icons = {'pdf':'📕', 'xlsx':'📊', 'xls':'📊', 'png':'🖼️', 'jpg':'🖼️', 'md':'📝', 'zip':'📦', 'pptx':'📢', 'gtx':'⚙️', 'g3':'⚙️'};
            return icons[ext.toLowerCase()] || '📄';
        }

        function formatDate(ms) {
            if (!ms) return "-";
            const d = new Date(ms);
            const yy = String(d.getFullYear()).slice(2);
            const mm = String(d.getMonth() + 1).padStart(2, '0');
            const dd = String(d.getDate()).padStart(2, '0');
            return `${yy}-${mm}-${dd}`;
        }

        // 트리 빌드
        function buildFolderTree(paths) {
            let tree = {};
            Array.from(paths).sort().forEach(path => {
                if (path === targetPath) return;
                let relPath = path.substring(targetPath.length);
                if (relPath.startsWith("/")) relPath = relPath.substring(1);
                if (relPath === "") return;
                let parts = relPath.split("/");
                let currentLevel = tree;
                parts.forEach((part, index) => {
                    if (!currentLevel[part]) {
                        let fullPath = targetPath + "/" + parts.slice(0, index + 1).join("/");
                        currentLevel[part] = { type: 'folder', name: part, children: {}, fullPath: fullPath };
                    }
                    currentLevel = currentLevel[part].children;
                });
            });
            return tree;
        }

        const folderTree = buildFolderTree(folderPaths);

        // 트리 HTML
        function renderTreeHTML(treeNodes, depth) {
            let html = "";
            let style = `padding-left: ${depth === 0 ? 0 : 12}px; border-left: ${depth > 0 ? '1px solid var(--background-modifier-border)' : 'none'};`;
            Object.keys(treeNodes).sort().forEach(key => {
                let node = treeNodes[key];
                let hasChild = Object.keys(node.children).length > 0;
                if (hasChild) {
                    html += `<details open style="${style}"><summary class="tree-item" data-path="${node.fullPath}" style="cursor:pointer; padding:2px 4px; border-radius:4px; list-style:none;">📁 ${node.name}</summary><div class="tree-children">${renderTreeHTML(node.children, depth + 1)}</div></details>`;
                } else {
                    html += `<div class="tree-item" data-path="${node.fullPath}" style="${style} cursor:pointer; padding:2px 4px; border-radius:4px;">📁 ${node.name}</div>`;
                }
            });
            return html;
        }

        let treeHTML = renderTreeHTML(folderTree, 0);

        // 정렬 함수
        function sortItems(items, key, order) {
            return items.sort((a, b) => {
                let valA, valB;
                if (key === 'name') {
                    valA = (a.type === 'folder') ? a.name : a.file.name;
                    valB = (b.type === 'folder') ? b.name : b.file.name;
                } else if (key === 'date') {
                    valA = (a.type === 'folder') ? 0 : a.file.stat.mtime;
                    valB = (b.type === 'folder') ? 0 : b.file.stat.mtime;
                } else if (key === 'memo') {
                    let keyA = (a.type === 'folder') ? `memo_fol_${a.path}` : `memo_fil_${a.file.path}`;
                    let keyB = (b.type === 'folder') ? `memo_fol_${b.path}` : `memo_fil_${b.file.path}`;
                    valA = localStorage.getItem(keyA) || "";
                    valB = localStorage.getItem(keyB) || "";
                }
                if (valA < valB) return order === 'asc' ? -1 : 1;
                if (valA > valB) return order === 'asc' ? 1 : -1;
                return 0;
            });
        }

        // 리스트 HTML 생성
        function renderListHTML(path, sortKey, sortOrder) {
            let rawFolders = Array.from(folderPaths).filter(p => {
                if (p === path) return false;
                let parent = p.substring(0, p.lastIndexOf("/"));
                return parent === path;
            }).map(p => ({ type: 'folder', path: p, name: p.split('/').pop() }));
            
            let rawFiles = allFiles.filter(f => {
                let parent = f.path.substring(0, f.path.lastIndexOf("/"));
                return parent === path;
            }).map(f => ({ type: 'file', file: f }));

            let folderSortKey = (sortKey === 'date') ? 'name' : sortKey;
            let sortedFolders = sortItems(rawFolders, folderSortKey, sortOrder); 
            let sortedFiles = sortItems(rawFiles, sortKey, sortOrder);

            let memoKeySync = `memo_fol_${path}`;
            let memoDesc = localStorage.getItem(memoKeySync) || '';
            let align = localStorage.getItem('memo_align_pref') || 'left';
            
            let nameW = localStorage.getItem(KEY_COL_NAME_PX) || '300';
            let dateW = localStorage.getItem(KEY_COL_DATE_PX) || '120';

            let html = `<div style="padding:10px; border-bottom:1px solid var(--background-modifier-border); background:var(--background-secondary); border-radius:4px;">
                <div style="font-size:0.8em; color:var(--text-muted); margin-bottom:4px;">📝 폴더 메모</div>
                <textarea class="folder-memo" data-key="${memoKeySync}" placeholder="이 폴더에 대한 설명..." style="width:100%; height:50px; resize:vertical; border:1px solid var(--background-modifier-border); text-align:${align}; font-family:var(--font-text);">${memoDesc}</textarea>
            </div>`;
            
            if (sortedFolders.length === 0 && sortedFiles.length === 0) {
                return html + `<div style="padding:20px; text-align:center; color:var(--text-muted);">📭 폴더가 비어있습니다.</div>`;
            }

            const sortIcon = (k) => {
                if (sortKey !== k) return `<span style="color:var(--text-faint); font-size:0.8em; margin-left:4px; opacity:0.3;">⇅</span>`;
                return sortOrder === 'asc' ? ' ▲' : ' ▼';
            };

            html += `<div class="list-header" style="display:flex; font-weight:bold; padding:6px 0; border-bottom:2px solid var(--background-modifier-border); align-items:center; user-select:none;">
                
                <div class="header-cell col-name-header" data-sort="name" style="flex:0 0 ${nameW}px; display:flex; justify-content:center; align-items:center; cursor:pointer;" title="이름 정렬">
                    이름 ${sortIcon('name')}
                </div>
                
                <div class="col-resizer-handle resizer-name" style="width:10px; cursor:col-resize; display:flex; justify-content:center; align-items:center; background:var(--background-secondary); height:20px; margin:0 2px; border-radius:2px;">
                    <div style="width:2px; height:12px; background:var(--text-muted); opacity:0.5; pointer-events:none;"></div>
                </div>
                
                <div class="header-cell col-date-header" data-sort="date" style="flex:0 0 ${dateW}px; display:flex; justify-content:center; align-items:center; cursor:pointer;" title="날짜 정렬">
                    수정일 ${sortIcon('date')}
                </div>

                <div class="col-resizer-handle resizer-date" style="width:10px; cursor:col-resize; display:flex; justify-content:center; align-items:center; background:var(--background-secondary); height:20px; margin:0 2px; border-radius:2px;">
                    <div style="width:2px; height:12px; background:var(--text-muted); opacity:0.5; pointer-events:none;"></div>
                </div>

                <div class="header-cell col-memo-header" data-sort="memo" style="flex:1; min-width:50px; display:flex; justify-content:center; align-items:center; cursor:pointer;" title="메모 정렬">
                    <span>메모 ${sortIcon('memo')}</span>
                </div>
                
                <div class="align-btn-group" style="position:absolute; right:15px; display:flex; gap:1px; opacity:0.6;">
                     <button class="btn-align" data-align="left" style="padding:0 3px; font-size:9px;">L</button>
                     <button class="btn-align" data-align="center" style="padding:0 3px; font-size:9px;">C</button>
                     <button class="btn-align" data-align="right" style="padding:0 3px; font-size:9px;">R</button>
                </div>
            </div>`;
            
            const rowStyle = "display:flex; padding:4px 0; border-bottom:1px solid var(--background-modifier-border); align-items:center;";
            const nameStyle = `flex:0 0 ${nameW}px; padding-left:10px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;`;
            const dateStyle = `flex:0 0 ${dateW}px; text-align:center; font-size:0.85em; color:var(--text-muted); justify-content:center; display:flex;`;
            const memoStyle = `flex:1; min-width:50px; border:none; background:transparent; text-align:${align};`;

            sortedFolders.forEach(item => {
                let mKey = `memo_fol_${item.path}`;
                let mVal = localStorage.getItem(mKey) || '';
                html += `<div class="list-row folder-row" data-path="${item.path}" style="${rowStyle} cursor:pointer;">
                    <div class="row-name col-name-cell" style="${nameStyle}">📁 ${item.name}</div>
                    <div class="col-spacer" style="width:14px;"></div>
                    <div class="row-date col-date-cell" style="${dateStyle}">-</div>
                    <div class="col-spacer" style="width:14px;"></div>
                    <input class="row-memo col-memo-cell" data-key="${mKey}" value="${mVal}" placeholder="..." style="${memoStyle}">
                </div>`;
            });
            
            sortedFiles.forEach(item => {
                let f = item.file;
                let mKey = `memo_fil_${f.path}`;
                let mVal = localStorage.getItem(mKey) || '';
                let mTime = formatDate(f.stat.mtime);
                
                html += `<div class="list-row file-row" data-name="${f.name}" style="${rowStyle}">
                    <div class="col-name-cell" style="${nameStyle}">${getIcon(f.extension)} <a class="internal-link" href="${f.path}" target="_blank">${f.name}</a></div>
                    <div class="col-spacer" style="width:14px;"></div>
                    <div class="row-date col-date-cell" style="${dateStyle}">${mTime}</div>
                    <div class="col-spacer" style="width:14px;"></div>
                    <input class="row-memo col-memo-cell" data-key="${mKey}" value="${mVal}" placeholder="..." style="${memoStyle}">
                </div>`;
            });
            return html;
        }

        // 8. UI 조립
        let initTreePx = localStorage.getItem(KEY_TREE_PX) || '250';
        let initHeightPx = localStorage.getItem(KEY_HEIGHT_PX) || '600';
        
        const css = `
            <style>
                .dv-container { display:flex; flex-direction:column; gap:0; font-size:13px; }
                .dv-top { border:1px solid var(--background-modifier-border); padding:10px; border-radius:8px; background:var(--background-primary); margin-bottom: 5px; }
                .dv-btn-group button { cursor: pointer; transition: 0.1s; padding: 4px 8px; font-size: 12px; }
                .dv-btn-group button.active { background-color: var(--interactive-accent); color: var(--text-on-accent); border: none; }
                .header-cell:hover { background-color: var(--background-modifier-hover); color: var(--interactive-accent); }
                .row-resizer { height: 12px; cursor: row-resize; background: var(--background-secondary); border: 1px solid var(--background-modifier-border); border-top: none; display: flex; justify-content: center; align-items: center; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; }
                .row-resizer:hover { background: var(--interactive-accent); opacity: 0.8; }
                .row-resizer div { width: 30px; height: 3px; background: var(--text-muted); border-radius: 2px; }
                .dv-split { display:flex; overflow:hidden; border:1px solid var(--background-modifier-border); border-top-left-radius:8px; border-top-right-radius:8px; border-bottom: none; }
                .tree-item:hover, .list-row:hover { background-color: var(--background-modifier-hover); border-radius: 4px; }
                .folder-row:hover .row-name { text-decoration: underline; color: var(--interactive-accent); }
                .nav-btn { cursor:pointer; padding: 4px 8px; font-size: 12px; border: 1px solid var(--background-modifier-border); background: var(--background-primary); border-radius: 4px; }
                .nav-btn:hover { background: var(--background-modifier-hover); }
                .nav-btn:disabled { opacity: 0.5; cursor: not-allowed; }
                .col-resizer-handle:hover { background: var(--interactive-accent) !important; opacity: 1 !important; border-color: var(--interactive-accent) !important; }
            </style>
        `;

        const mainHTML = `
            ${css}
            <div class="dv-container">
                <div class="dv-top">
                    <div style="display:flex; justify-content:space-between; margin-bottom:8px; align-items:center;">
                        <h3 style="margin:0;">🔍 통합 검색 <span style="font-size:0.7em; color:var(--text-muted);">(바로가기 폴더 지원)</span></h3>
                        <div class="dv-btn-group" style="display:flex; gap:10px; align-items:center;">
                            <div style="display:flex; gap:2px; border-right:2px solid var(--background-modifier-border); padding-right:10px;">
                                <button class="btn-filter active" data-type="all">전체</button>
                                <button class="btn-filter" data-type="folder">폴더</button>
                                <button class="btn-filter" data-type="file">파일</button>
                                <button class="btn-filter" data-type="memo">메모</button>
                            </div>
                            <div style="display:flex; gap:4px;">
                                <button class="btn-clear">🗑️</button>
                                <button class="btn-exp">➕</button>
                                <button class="btn-col">➖</button>
                            </div>
                        </div>
                    </div>
                    <input class="search-input" type="text" placeholder="검색..." style="width:100%; padding:6px;">
                    <details class="search-results" open style="margin-top:5px;">
                        <summary style="cursor:pointer; font-weight:bold; padding:4px;">📋 검색 결과 (${allFiles.length}개 파일 로드됨)</summary>
                        <div class="result-content" style="border:1px solid var(--background-modifier-border); max-height:200px; overflow-y:auto; padding:5px; background:var(--background-secondary);">검색어를 입력하세요.</div>
                    </details>
                </div>

                <div class="dv-split" style="flex: none; height: ${initHeightPx}px;">
                    <div class="left-pane" style="flex:0 0 ${initTreePx}px; min-width:150px; border-right:1px solid var(--background-modifier-border); display:flex; flex-direction:column; background:var(--background-primary-alt);">
                        <div style="padding:8px; border-bottom:1px solid var(--background-modifier-border); display:flex; justify-content:space-between; align-items:center;">
                            <b>🌿 폴더</b>
                            <div class="dv-btn-group" style="display:flex; gap:2px;">
                                <button class="btn-tree-exp" style="padding:2px 6px;">➕</button>
                                <button class="btn-tree-col" style="padding:2px 6px;">➖</button>
                            </div>
                        </div>
                        <div class="tree-content" style="flex:1; overflow-y:auto; padding:5px;">${treeHTML}</div>
                    </div>

                    <div class="resizer" style="width:6px; cursor:col-resize; background:var(--background-modifier-border); display:flex; align-items:center; justify-content:center;">⋮</div>

                    <div class="right-pane" style="flex:1; display:flex; flex-direction:column; min-width:300px; background:var(--background-primary);">
                        <div style="padding:8px; border-bottom:1px solid var(--background-modifier-border); display:flex; justify-content:space-between; align-items:center; background:var(--background-secondary);">
                            <h3 style="margin:0;">🗂️ 파일 리스트</h3>
                            <div style="display:flex; gap:5px; align-items:center;">
                                <span class="current-path" style="font-size:0.8em; color:var(--text-muted); margin-right:10px; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; max-width:200px;">${targetPath}</span>
                                <button class="nav-btn btn-open">📂 열기</button>
                                <button class="nav-btn btn-nav-back" disabled>◀</button>
                                <button class="nav-btn btn-nav-fwd" disabled>▶</button>
                            </div>
                        </div>
                        <div class="file-list-content" style="flex:1; overflow-y:auto;"></div>
                    </div>
                </div>

                <div class="row-resizer" title="높이 조절"><div></div></div>
            </div>
        `;

        dv.container.innerHTML = mainHTML;

        // 9. 이벤트 핸들러 (기존과 동일)
        setTimeout(() => {
            const root = dv.container;
            const q = (s) => root.querySelector(s);
            const qa = (s) => root.querySelectorAll(s);

            let savedSort = JSON.parse(localStorage.getItem(KEY_SORT_STATE)) || { key: 'name', order: 'asc' };

            let state = {
                path: targetPath,
                history: [targetPath],
                histIdx: 0,
                filters: new Set(['all']),
                sort: savedSort,
                resizingMode: null,
                startX: 0,
                startW: 0
            };

            const updateView = (newPath, pushHist = true) => {
                state.path = newPath;
                if (pushHist && state.history[state.histIdx] !== newPath) {
                    state.history = state.history.slice(0, state.histIdx + 1);
                    state.history.push(newPath);
                    state.histIdx++;
                }
                
                q('.file-list-content').innerHTML = renderListHTML(newPath, state.sort.key, state.sort.order);
                q('.current-path').innerText = `📍 ${newPath}`;
                
                qa('.tree-item').forEach(el => {
                    el.style.backgroundColor = 'transparent';
                    el.style.color = 'var(--text-normal)';
                });
                const activeItem = q(`.tree-item[data-path="${newPath}"]`);
                if (activeItem) {
                    activeItem.style.backgroundColor = 'var(--interactive-accent)';
                    activeItem.style.color = 'var(--text-on-accent)';
                    let p = activeItem.parentElement; 
                    if (p.tagName === 'DETAILS') {
                        let grandParent = p.parentElement; 
                        while(grandParent) {
                            if (grandParent.tagName === 'DETAILS') grandParent.open = true;
                            if (grandParent.classList.contains('tree-content')) break;
                            grandParent = grandParent.parentElement;
                        }
                    }
                }

                const bBack = q('.btn-nav-back');
                const bFwd = q('.btn-nav-fwd');
                if(bBack) { bBack.disabled = state.histIdx === 0; bBack.style.opacity = state.histIdx === 0 ? 0.5 : 1; }
                if(bFwd) { bFwd.disabled = state.histIdx === state.history.length - 1; bFwd.style.opacity = state.histIdx === state.history.length - 1 ? 0.5 : 1; }
            };

            updateView(targetPath, false);

            root.addEventListener('click', (e) => {
                const t = e.target;
                const btn = t.closest('button');
                
                const header = t.closest('.header-cell');
                if (header) {
                    const key = header.getAttribute('data-sort');
                    if (key) {
                        if (state.sort.key === key) {
                            state.sort.order = (state.sort.order === 'asc') ? 'desc' : 'asc';
                        } else {
                            state.sort.key = key;
                            state.sort.order = 'asc';
                        }
                        localStorage.setItem(KEY_SORT_STATE, JSON.stringify(state.sort));
                        updateView(state.path, false);
                    }
                    return;
                }

                if (t.classList.contains('tree-item')) return updateView(t.getAttribute('data-path'));
                if (t.classList.contains('row-name')) {
                    const row = t.closest('.folder-row');
                    if (row) return updateView(row.getAttribute('data-path'));
                }
                const searchItem = t.closest('.search-item');
                if (searchItem) return updateView(searchItem.getAttribute('data-path'));

                if (btn) {
                    if (btn.classList.contains('btn-align')) {
                        const align = btn.getAttribute('data-align');
                        localStorage.setItem('memo_align_pref', align);
                        qa('.row-memo, .folder-memo').forEach(el => el.style.textAlign = align);
                    }
                    if (btn.classList.contains('btn-filter')) {
                        const type = btn.getAttribute('data-type');
                        if (type === 'all') { state.filters.clear(); state.filters.add('all'); }
                        else {
                            state.filters.delete('all');
                            if (state.filters.has(type)) state.filters.delete(type); else state.filters.add(type);
                            if (state.filters.size === 0) state.filters.add('all');
                        }
                        qa('.btn-filter').forEach(b => {
                            if (state.filters.has(b.getAttribute('data-type'))) b.classList.add('active'); else b.classList.remove('active');
                        });
                        q('.search-input').dispatchEvent(new Event('input'));
                    }
                    else if (btn.classList.contains('btn-clear')) { q('.search-input').value = ''; q('.search-input').dispatchEvent(new Event('input')); }
                    else if (btn.classList.contains('btn-exp')) q('.search-results').open = true;
                    else if (btn.classList.contains('btn-col')) q('.search-results').open = false;
                    else if (btn.classList.contains('btn-tree-exp')) qa('.tree-content details').forEach(d => d.open = true);
                    else if (btn.classList.contains('btn-tree-col')) qa('.tree-content details').forEach(d => d.open = false);
                    else if (btn.classList.contains('btn-nav-back') && state.histIdx > 0) updateView(state.history[--state.histIdx], false);
                    else if (btn.classList.contains('btn-nav-fwd') && state.histIdx < state.history.length - 1) updateView(state.history[++state.histIdx], false);
                    else if (btn.classList.contains('btn-open')) {
                        let f = app.vault.getAbstractFileByPath(state.path);
                        if(f) app.workspace.getLeaf().openFile(f);
                    }
                }
            });

            root.addEventListener('input', (e) => {
                const t = e.target;
                if (t.classList.contains('search-input')) {
                    const term = t.value.toLowerCase();
                    const box = q('.result-content');
                    if (!term) { box.innerHTML = '검색어를 입력하세요.'; return; }
                    
                    let res = [];
                    if (state.filters.has('all') || state.filters.has('folder')) Array.from(folderPaths).filter(p=>p.toLowerCase().includes(term)).forEach(p=>res.push({t:'📁', p:p, n:p.split('/').pop(), m:p}));
                    if (state.filters.has('all') || state.filters.has('file')) allFiles.filter(f=>f.name.toLowerCase().includes(term)).forEach(f=>res.push({t:'📄', p:f.path.substring(0,f.path.lastIndexOf("/")), n:f.name, m:f.name}));
                    if (state.filters.has('all') || state.filters.has('memo')) {
                        for(let i=0; i<localStorage.length; i++) {
                            let k=localStorage.key(i);
                            if(k.includes('memo_') && localStorage.getItem(k).toLowerCase().includes(term)) {
                                let v=localStorage.getItem(k), p=k.replace('memo_fil_','').replace('memo_fol_','').replace('memo_desc_','');
                                let isFile=k.includes('_fil_'), pathForView=isFile?p.substring(0,p.lastIndexOf("/")):p;
                                res.push({t:'📝', p:pathForView, n:p.split('/').pop(), m:v});
                            }
                        }
                    }
                    if (res.length===0) box.innerHTML = '검색 결과 없음';
                    else {
                        box.innerHTML = res.map(r => `<div class="search-item" data-path="${r.p}" style="padding:4px; border-bottom:1px solid var(--background-modifier-border); cursor:pointer;"><span style="font-size:0.8em; color:var(--text-muted);">${r.t} ${r.n}</span><br><b>${r.m}</b></div>`).join('');
                        qa('.search-item').forEach(el=>el.addEventListener('click',()=>updateView(el.getAttribute('data-path'))));
                    }
                }
                if (t.classList.contains('folder-memo') || t.classList.contains('row-memo')) {
                    localStorage.setItem(t.getAttribute('data-key'), t.value);
                }
            });

            root.addEventListener('mousedown', (e) => {
                let mode = null;
                if (e.target.classList.contains('resizer')) mode = 'split';
                else if (e.target.classList.contains('resizer-name')) mode = 'name';
                else if (e.target.classList.contains('resizer-date')) mode = 'date';
                else if (e.target.classList.contains('row-resizer') || e.target.parentElement.classList.contains('row-resizer')) mode = 'row';

                if (mode) {
                    e.preventDefault();
                    state.resizingMode = mode;
                    state.startX = (mode === 'row') ? e.clientY : e.clientX;
                    
                    if (mode === 'split') state.startW = parseInt(localStorage.getItem(KEY_TREE_PX) || '250');
                    else if (mode === 'name') state.startW = parseInt(localStorage.getItem(KEY_COL_NAME_PX) || '300');
                    else if (mode === 'date') state.startW = parseInt(localStorage.getItem(KEY_COL_DATE_PX) || '120');
                    else if (mode === 'row') state.startW = parseInt(localStorage.getItem(KEY_HEIGHT_PX) || '600');

                    document.body.style.cursor = (mode === 'row') ? 'row-resize' : 'col-resize';

                    const onMove = (em) => {
                        if (!state.resizingMode) return;
                        let currentPos = (state.resizingMode === 'row') ? em.clientY : em.clientX;
                        let delta = currentPos - state.startX;
                        let newW = state.startW;

                        if (state.resizingMode === 'split') {
                            newW = state.startW + delta;
                            if(newW < 150) newW = 150;
                            q('.left-pane').style.flex = `0 0 ${newW}px`;
                            localStorage.setItem(KEY_TREE_PX, newW);
                        } 
                        else if (state.resizingMode === 'name') {
                            newW = state.startW + delta; 
                            if(newW < 100) newW = 100;
                            q('.col-name-header').style.flex = `0 0 ${newW}px`;
                            qa('.col-name-cell').forEach(el => el.style.flex = `0 0 ${newW}px`);
                            localStorage.setItem(KEY_COL_NAME_PX, newW);
                        }
                        else if (state.resizingMode === 'date') {
                            newW = state.startW + delta;
                            if(newW < 50) newW = 50;
                            q('.col-date-header').style.flex = `0 0 ${newW}px`;
                            qa('.col-date-cell').forEach(el => el.style.flex = `0 0 ${newW}px`);
                            localStorage.setItem(KEY_COL_DATE_PX, newW);
                        }
                        else if (state.resizingMode === 'row') {
                            newW = state.startW + delta;
                            if(newW < 100) newW = 100;
                            q('.dv-split').style.height = `${newW}px`;
                            localStorage.setItem(KEY_HEIGHT_PX, newW);
                        }
                    };

                    const onUp = () => {
                        state.resizingMode = null;
                        document.body.style.cursor = 'default';
                        document.removeEventListener('mousemove', onMove);
                        document.removeEventListener('mouseup', onUp);
                    };
                    document.addEventListener('mousemove', onMove);
                    document.addEventListener('mouseup', onUp);
                }
            });

        }, 100);
    }
} catch (e) {
    dv.container.innerHTML = `
        <div style="padding:20px; border:2px solid red; color:red; background:#330000;">
            <h3>🛑 치명적 오류</h3>
            <p>${e.message}</p>
            <pre style="font-size:0.8em; background:#220000; padding:10px; overflow:auto;">${e.stack}</pre>
        </div>`;
}
```