Skip to content

Commit 3cf457c

Browse files
Copilotwinterdrive
andauthored
Fix: exclude hidden directories when drag-dropping folders into virtualTabs panel (#45)
* Initial plan * Fix: skip hidden files/folders when expanding directories on drag-and-drop Agent-Logs-Url: https://github.com/winterdrive/vscode-virtual-tabs/sessions/006c1e08-f3d7-4853-9d32-f692a1c9d096 Co-authored-by: winterdrive <90021888+winterdrive@users.noreply.github.com> * Fix: skip only hidden directories (not hidden files) on drag-and-drop expansion Agent-Logs-Url: https://github.com/winterdrive/vscode-virtual-tabs/sessions/73200347-ea31-4051-9259-cc7ff190fca0 Co-authored-by: winterdrive <90021888+winterdrive@users.noreply.github.com> * feat: add end-to-end tests for Scope Filter UI behavior and unit tests for scope management logic - Implemented E2E tests for Scope Filter UI behavior covering various scenarios including built-in and repo scopes. - Added unit tests for addGroup scope decision logic to ensure correct behavior based on active scopes. - Created tests for built-in group initialization conditions to verify proper handling of existing user groups. - Developed migration tests for transitioning from single to multiple active scopes. - Added tests for computing scope descriptions based on active scopes. - Implemented tests for root tree structure after applying scope filters to validate node types and order. - Updated existing tests to reflect changes in the visibility of the Add Group button in multi-root workspaces. * fix: add missing export statements in unit test files --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: winterdrive <90021888+winterdrive@users.noreply.github.com>
1 parent a003f49 commit 3cf457c

25 files changed

Lines changed: 1741 additions & 160 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ test-resources/
3535

3636
# Testing
3737
/coverage
38+
test-ui-output.txt

.vscodeignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ tsconfig.test.ui.json
88
jest.config.js
99
package-lock.json
1010
*.map
11+
test-ui-output*.txt
1112
.vscode-test/**
1213
test-resources/
1314

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to the "VirtualTabs" extension will be documented in this file.
44

5+
## [Unreleased]
6+
7+
### Changed
8+
9+
- Multi-root workspace: the panel title **Add Group** (`+`) button is now hidden. Each project scope's inline button handles group creation instead, keeping the action close to the target scope. Single-root workspaces are unaffected and still show the panel title button.
10+
11+
### Fixed
12+
13+
- Directory drag-and-drop now skips hidden directories whose names start with `.`, while still including dotfiles such as `.gitignore` and `.editorconfig`.
14+
515
## [0.5.5] - 2026-05-13
616

717
### Changed

DEVELOPMENT.md

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Under the hood, the script runs:
160160

161161
```bash
162162
tsc -p tsconfig.test.ui.json
163-
extest setup-and-run "out/test/ui/**/*.test.js" -e ".vscode-test/extensions" -r "test-resources/multi-root/virtual-tabs.code-workspace"
163+
extest setup-and-run "out/test/ui/**/*.test.js" -e ".vscode-test/extensions" -r "test-resources/multi-root/virtual-tabs.code-workspace" -c 1.96.0
164164
```
165165

166166
Notes:
@@ -170,6 +170,35 @@ Notes:
170170
* `test-resources/multi-root/` is a committed fixture workspace used by the multi-root UI/E2E tests.
171171
* The multi-root fixture verifies that VirtualTabs renders one scope section per discovered project and persists new groups only to the selected repo's `.vscode/virtualTab.json`.
172172

173+
#### VS Code Version Pinning (`-c 1.96.0`)
174+
175+
The test script is pinned to VS Code **1.96.0**. VS Code 1.100+ (verified on 1.120.0) changed its release archive to a nested hash-subfolder structure that breaks `extest`'s ChromeDriver bootstrap, causing a crash before any test runs. Do not bump this version without first verifying that the new release is compatible with `vscode-extension-tester`.
176+
177+
#### E2E Test Gotchas
178+
179+
**1. Do not rely on FileSystemWatcher for test state changes.**
180+
181+
When a test writes a fixture config file and then expects the extension to pick up the change, the FileSystemWatcher's fire timing is non-deterministic in a ChromeDriver-controlled VS Code instance (and can be blocked entirely by the `isInternalSaving` flag). Instead, **click the Refresh toolbar button** after writing the fixture — `reinitializeScopes()` reads from disk synchronously and is guaranteed to see the new content.
182+
183+
```ts
184+
// ❌ Fragile — watcher may not fire in time
185+
writeConfig(repoAConfigPath, [...]);
186+
await waitForTreeLabel('My Group'); // may time out
187+
188+
// ✅ Reliable — Refresh reads from disk immediately
189+
writeConfig(repoAConfigPath, [...]);
190+
await clickToolbarButton(sidebar, /refresh/i);
191+
await waitForTreeLabel('My Group');
192+
```
193+
194+
**2. QuickPick interactions must use Selenium `element.click()`, not `executeScript` DOM click.**
195+
196+
VS Code's `canPickMany` QuickPick widget listens for real browser events. A click injected via `driver.executeScript('arguments[0].click()', el)` bypasses VS Code's event system and leaves checkboxes in their current state. Always use `await row.click()` (Selenium WebDriver native click) when toggling QuickPick items.
197+
198+
**3. Wrap toolbar button clicks in a `driver.wait()` retry loop.**
199+
200+
After a `reinitializeScopes()` call the tree re-renders, invalidating existing Selenium element references (`StaleElementReferenceError`). `clickToolbarButton` already handles this with an internal retry loop — do not remove it.
201+
173202
### Test Source Layout
174203

175204
```text
@@ -488,6 +517,60 @@ flowchart TD
488517

489518
---
490519

520+
---
521+
522+
## 🤝 AI-Assisted Development Notes
523+
524+
When collaborating with an AI assistant (Claude Code, Copilot, Cursor, etc.) on this project:
525+
526+
**Always run commands yourself in the VS Code terminal — do not let the AI execute them in a sandbox.**
527+
528+
AI assistants may default to running shell commands (`npm run test:ui`, `git`, etc.) inside an isolated sandbox environment. Sandbox runs are invisible to the real VS Code instance, cannot interact with the extension host, and produce misleading results for UI/E2E tests. Instead, instruct the AI to output the command and have you paste it into the integrated terminal manually.
529+
530+
> Suggested instruction when starting a session:
531+
> *"Output all terminal commands for me to run in VS Code's integrated terminal. Do not execute them yourself."*
532+
533+
---
534+
535+
## ⚙️ Context Key: `virtualTabs:hasMultipleScopes`
536+
537+
This boolean context key controls whether the panel title **Add Group** (`+`) button is shown.
538+
539+
| Value | Meaning | Button |
540+
| :--- | :--- | :--- |
541+
| `true` | ≥2 repo scopes visible (or 0 — built-in only) | Hidden |
542+
| `false` | Exactly 1 repo scope visible (flat mode) | Shown |
543+
544+
**Update points**`updateScopeHeadersContext()` is called in three places in `provider.ts`:
545+
546+
1. **Constructor** — sets initial state on activation
547+
2. **`reinitializeScopes()`** — called when Refresh is clicked or workspace folders change
548+
3. **`setActiveScopeIds()`** — called when the scope filter changes
549+
550+
**Logic** (`computeHasScopeHeaders()`):
551+
552+
```ts
553+
// Filtered view: check how many repo scopes are currently visible
554+
if (this.activeScopeIds.size > 0) {
555+
const visibleScopes = this.configScopes.filter(s => this.activeScopeIds.has(s.id));
556+
return visibleScopes.length !== 1; // 0 or 2+ → show scope headers → hide button
557+
}
558+
// Unfiltered: use total scope count
559+
return this.configScopes.length > 1;
560+
```
561+
562+
`package.json` wires this to the button's `when` clause:
563+
564+
```json
565+
{
566+
"command": "virtualTabs.addGroup",
567+
"when": "view == virtualTabsView && !virtualTabs:hasMultipleScopes",
568+
"group": "navigation@3"
569+
}
570+
```
571+
572+
---
573+
491574
## 🔧 Common Development Issues
492575

493576
### Q: Compile error "Cannot find module 'vscode'"

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ In MVC/MVVM or large-scale projects, related files are often scattered across de
8787
### ⚡ Workflow Boosters
8888

8989
- **Smart Copy Menu** — Unified copy options for names, paths, and total file content.
90-
- **Directory Drag & Drop** — Drag folders to add all files recursively.
90+
- **Directory Drag & Drop** — Drag folders to add files recursively while skipping hidden directories.
9191
- **Clipboard Operations** — Full Cut/Copy/Paste support for files and groups.
9292
- **Smart Organization** — Auto-group by extension, date, or sort by various criteria.
9393

@@ -98,12 +98,12 @@ In MVC/MVVM or large-scale projects, related files are often scattered across de
9898
### 📁 Group Management
9999

100100
- **Create/Rename**: Right-click panel or groups to manage.
101-
- **Multi-root scopes**: In a multi-root workspace, each detected project appears as its own section. Use the scope header to add a group, open that scope's config, reveal its storage, or clear only that scope.
101+
- **Multi-root scopes**: In a multi-root workspace, each detected project appears as its own section. Use the inline **Add Group** button on the scope header to create groups; the panel title `+` button is intentionally hidden in multi-root mode to keep group creation scoped to the correct project. In single-root mode the panel title button is still present.
102102
- **Sub-Groups**: Right-click a group → **Add Sub-Group** (or drag one group into another).
103103
- **Auto-Sync**: The built-in "Open Editors" group automatically tracks your native tabs.
104104
- **Drag & Drop**:
105105
- **Files**: Drag files from Explorer into groups.
106-
- **Folders**: Drag folders to recursively add all files inside.
106+
- **Folders**: Drag folders to recursively add files inside; directories whose names start with `.` are skipped, while dotfiles such as `.gitignore` are still included.
107107
- **Multi-select**: Hold `Ctrl/Cmd` to drag multiple files at once.
108108

109109
![Drag and Drop Demo](docs/assets/drag_drop_demo.png)

README.zh-TW.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
### ⚡ 工作流程加速
8888

8989
- **智慧複製選單** — 統一的檔案名稱、路徑與內容複製選項。
90-
- **目錄拖放**拖曳資料夾以遞迴加入所有內部檔案
90+
- **目錄拖放**拖曳資料夾以遞迴加入內部檔案,並略過隱藏資料夾
9191
- **剪貼簿操作** — 完整的檔案與群組剪下 / 複製 / 貼上支援。
9292
- **智慧組織** — 依副檔名、日期自動分組,或自訂排序準則。
9393

@@ -103,7 +103,7 @@
103103
- **自動同步**:內建的「目前開啟的檔案」群組會自動追蹤您的分頁。
104104
- **拖放操作**
105105
- **檔案**:直接拖入群組。
106-
- **資料夾**拖入資料夾可遞迴加入所有檔案
106+
- **資料夾**拖入資料夾可遞迴加入內部檔案;會略過名稱以 `.` 開頭的資料夾,但仍會加入 `.gitignore` 等 dotfile
107107
- **多選**:按住 `Ctrl/Cmd` 選取多個檔案後一次拖入。
108108

109109
![拖放操作示範](docs/assets/drag_drop_demo.png)
@@ -161,7 +161,7 @@ VirtualTabs 透過 **Model Context Protocol (MCP)** 提供完整的 AI Agent 整
161161
檢查擴充功能是否已啟用,且 VS Code 版本為 1.75+。查看活動列是否有 VirtualTabs 圖示。
162162

163163
**Q:如何將資料夾加入群組?**
164-
直接從檔案總管將資料夾拖曳到 VirtualTabs 面板中的群組,系統會自動遞迴掃描並加入檔案。
164+
直接從檔案總管將資料夾拖曳到 VirtualTabs 面板中的群組,系統會自動遞迴掃描並加入檔案,同時略過名稱以 `.` 開頭的隱藏資料夾;`.gitignore` 等 dotfile 仍會加入
165165

166166
**Q:我可以手動調整群組順序嗎?**
167167
可以,右鍵點擊群組並使用 **Move Up/Down** 指令。

docs/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ <h2 style="text-align: center; color: #fff; margin-bottom: 50px; font-size: 2rem
315315
style="width: 100%; border-radius: 8px; margin-bottom: 20px; border: 1px solid #555;">
316316
<h3 style="color: #fff; margin-top: 0; font-size: 1.4rem;">📁 Smart Virtual Grouping</h3>
317317
<p style="font-size: 1rem; color: #bbb;">Organize files from across your codebase into meaningful
318-
groups. Drag folders to add files recursively, or cherry-pick specific modules without touching
318+
groups. Drag folders to add files recursively while skipping hidden directories, or cherry-pick specific modules without touching
319319
your disk structure.</p>
320320
</div>
321321

docs/llms-full.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ In MVC/MVVM or large-scale projects, related files are often scattered across de
9191
### ⚡ Workflow Boosters
9292

9393
- **Smart Copy Menu** — Unified copy options for names, paths, and total file content.
94-
- **Directory Drag & Drop** — Drag folders to add all files recursively.
94+
- **Directory Drag & Drop** — Drag folders to add files recursively while skipping hidden directories.
9595
- **Clipboard Operations** — Full Cut/Copy/Paste support for files and groups.
9696
- **Smart Organization** — Auto-group by extension, date, or sort by various criteria.
9797

@@ -107,7 +107,7 @@ In MVC/MVVM or large-scale projects, related files are often scattered across de
107107
- **Auto-Sync**: The built-in "Open Editors" group automatically tracks your native tabs.
108108
- **Drag & Drop**:
109109
- **Files**: Drag files from Explorer into groups.
110-
- **Folders**: Drag folders to recursively add all files inside.
110+
- **Folders**: Drag folders to recursively add files inside; directories whose names start with `.` are skipped, while dotfiles such as `.gitignore` are still included.
111111
- **Multi-select**: Hold `Ctrl/Cmd` to drag multiple files at once.
112112

113113
![Drag and Drop Demo](docs/assets/drag_drop_demo.png)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "virtual-tabs",
33
"displayName": "VirtualTabs - Virtual File Directories & AI context management",
44
"description": "%extension.description%",
5-
"version": "0.5.6",
5+
"version": "0.5.7",
66
"publisher": "winterdrive",
77
"icon": "docs/assets/virtualtabs_icon_128.png",
88
"categories": [
@@ -47,7 +47,7 @@
4747
"properties": {
4848
"virtualTabs.autoRevealActiveFile": {
4949
"type": "boolean",
50-
"default": true,
50+
"default": false,
5151
"description": "%config.autoRevealActiveFile.description%"
5252
},
5353
"virtualTabs.confirmBeforeDelete": {
@@ -365,7 +365,7 @@
365365
},
366366
{
367367
"command": "virtualTabs.addGroup",
368-
"when": "view == virtualTabsView",
368+
"when": "view == virtualTabsView && !virtualTabs:hasMultipleScopes",
369369
"group": "navigation@3"
370370
},
371371
{
@@ -685,7 +685,7 @@
685685
"test": "tsc -p ./ && jest --runInBand",
686686
"test:properties": "jest --runInBand src/test/properties",
687687
"test:ui:setup": "extest setup-tests",
688-
"test:ui": "tsc -p tsconfig.test.ui.json && extest setup-and-run \"out/test/ui/**/*.test.js\" -e \".vscode-test/extensions\" -r \"test-resources/multi-root/virtual-tabs.code-workspace\"",
688+
"test:ui": "tsc -p tsconfig.test.ui.json && extest setup-and-run \"out/test/ui/**/*.test.js\" -e \".vscode-test/extensions\" -r \"test-resources/multi-root/virtual-tabs.code-workspace\" -c 1.96.0",
689689
"vscode:prepublish": "npm run build:vt && npm run build:mcp && tsc -p ./",
690690
"build:mcp": "esbuild mcp-server/src/index.ts --bundle --platform=node --target=node18 --format=cjs --outfile=dist/mcp/index.js --external:vscode",
691691
"build:vt": "npx tsx scripts/bundle-vt.ts"

0 commit comments

Comments
 (0)