For working with the tab bar off, where a side list is the tab bar. VS Code's Open Editors view already does pinning; what it does not do is colour by project, and it is a tree rather than a flat list. - A Tabs view in the Explorer: one row per open editor, no group nodes. A TreeDataProvider whose items have no children renders as a list, since there is no separate list API. - Each row's icon is tinted by the project containing the file — the nearest .csproj/.fsproj/.vbproj, package.json, Cargo.toml, go.mod, pyproject.toml, pom.xml or build.gradle above it, configurable. The colour comes from a hash of the project name, so a project keeps its colour across sessions rather than depending on the order tabs happened to open in. Eight contributed theme colours, all overridable. - Sort by open order, project (so the colours run in blocks), or file name, with pinned tabs lifted to the top. - Close, close others (keeping pinned), pin/unpin, copy path, reveal. - The list follows the active editor. Two API limits shape this, both covered by tests: - Nothing can activate an arbitrary tab. A file-backed tab is focused by re-opening its resource in its own group, which handles text, diffs, notebooks and custom editors. Webview tabs have no resource, so they are listed and marked as unfocusable instead of pretending. - Only the *active* editor can be pinned, so pinning another row focuses it first. Commands act on live vscode.Tab objects rather than the row snapshots handed to them. The first cut compared against a snapshot's isPinned and so silently did nothing on unpin, and read the provider's cached list, which is empty until the tree has been rendered. Also worth recording, both found by the tests: VS Code moves a pinned tab to the front of its group, so `open` order shows pinned first regardless of the pinnedFirst setting; and closeAllEditors leaves pinned editors open, so test cleanup closes through the tab API instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
356 lines
9.3 KiB
JSON
356 lines
9.3 KiB
JSON
{
|
|
"name": "vertical-tabs",
|
|
"displayName": "Vertical Tabs",
|
|
"description": "Open editors as a flat vertical list, colour-coded by project, with pinning. For people who turn the tab bar off.",
|
|
"version": "0.1.0",
|
|
"publisher": "local",
|
|
"license": "MIT",
|
|
"engines": {
|
|
"vscode": "^1.85.0"
|
|
},
|
|
"categories": [
|
|
"Other"
|
|
],
|
|
"keywords": [
|
|
"tabs",
|
|
"vertical tabs",
|
|
"open editors",
|
|
"csharp",
|
|
"visual studio"
|
|
],
|
|
"activationEvents": [
|
|
"onStartupFinished"
|
|
],
|
|
"main": "./out/extension.js",
|
|
"contributes": {
|
|
"views": {
|
|
"explorer": [
|
|
{
|
|
"id": "verticalTabs.tabs",
|
|
"name": "Tabs",
|
|
"contextualTitle": "Tabs",
|
|
"visibility": "visible"
|
|
}
|
|
]
|
|
},
|
|
"viewsWelcome": [
|
|
{
|
|
"view": "verticalTabs.tabs",
|
|
"contents": "No editors are open."
|
|
}
|
|
],
|
|
"commands": [
|
|
{
|
|
"command": "verticalTabs.open",
|
|
"title": "Open Tab",
|
|
"category": "Tabs"
|
|
},
|
|
{
|
|
"command": "verticalTabs.close",
|
|
"title": "Close",
|
|
"category": "Tabs",
|
|
"icon": "$(close)"
|
|
},
|
|
{
|
|
"command": "verticalTabs.closeOthers",
|
|
"title": "Close Others",
|
|
"category": "Tabs"
|
|
},
|
|
{
|
|
"command": "verticalTabs.pin",
|
|
"title": "Pin",
|
|
"category": "Tabs",
|
|
"icon": "$(pin)"
|
|
},
|
|
{
|
|
"command": "verticalTabs.unpin",
|
|
"title": "Unpin",
|
|
"category": "Tabs",
|
|
"icon": "$(pinned)"
|
|
},
|
|
{
|
|
"command": "verticalTabs.copyPath",
|
|
"title": "Copy Path",
|
|
"category": "Tabs"
|
|
},
|
|
{
|
|
"command": "verticalTabs.revealInExplorer",
|
|
"title": "Reveal in Explorer View",
|
|
"category": "Tabs"
|
|
},
|
|
{
|
|
"command": "verticalTabs.setOrderOpen",
|
|
"title": "Sort by Open Order",
|
|
"category": "Tabs"
|
|
},
|
|
{
|
|
"command": "verticalTabs.setOrderProject",
|
|
"title": "Sort by Project",
|
|
"category": "Tabs"
|
|
},
|
|
{
|
|
"command": "verticalTabs.setOrderName",
|
|
"title": "Sort by File Name",
|
|
"category": "Tabs"
|
|
}
|
|
],
|
|
"menus": {
|
|
"view/title": [
|
|
{
|
|
"command": "verticalTabs.setOrderOpen",
|
|
"when": "view == verticalTabs.tabs",
|
|
"group": "1_order@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.setOrderProject",
|
|
"when": "view == verticalTabs.tabs",
|
|
"group": "1_order@2"
|
|
},
|
|
{
|
|
"command": "verticalTabs.setOrderName",
|
|
"when": "view == verticalTabs.tabs",
|
|
"group": "1_order@3"
|
|
}
|
|
],
|
|
"view/item/context": [
|
|
{
|
|
"command": "verticalTabs.pin",
|
|
"when": "view == verticalTabs.tabs && viewItem =~ /unpinned/",
|
|
"group": "inline@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.unpin",
|
|
"when": "view == verticalTabs.tabs && viewItem =~ /pinned/",
|
|
"group": "inline@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.close",
|
|
"when": "view == verticalTabs.tabs",
|
|
"group": "inline@2"
|
|
},
|
|
{
|
|
"command": "verticalTabs.pin",
|
|
"when": "view == verticalTabs.tabs && viewItem =~ /unpinned/",
|
|
"group": "1_state@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.unpin",
|
|
"when": "view == verticalTabs.tabs && viewItem =~ /pinned/",
|
|
"group": "1_state@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.close",
|
|
"when": "view == verticalTabs.tabs",
|
|
"group": "2_close@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.closeOthers",
|
|
"when": "view == verticalTabs.tabs",
|
|
"group": "2_close@2"
|
|
},
|
|
{
|
|
"command": "verticalTabs.copyPath",
|
|
"when": "view == verticalTabs.tabs && viewItem =~ /file/",
|
|
"group": "3_path@1"
|
|
},
|
|
{
|
|
"command": "verticalTabs.revealInExplorer",
|
|
"when": "view == verticalTabs.tabs && viewItem =~ /file/",
|
|
"group": "3_path@2"
|
|
}
|
|
],
|
|
"commandPalette": [
|
|
{
|
|
"command": "verticalTabs.open",
|
|
"when": "false"
|
|
},
|
|
{
|
|
"command": "verticalTabs.close",
|
|
"when": "false"
|
|
},
|
|
{
|
|
"command": "verticalTabs.closeOthers",
|
|
"when": "false"
|
|
},
|
|
{
|
|
"command": "verticalTabs.pin",
|
|
"when": "false"
|
|
},
|
|
{
|
|
"command": "verticalTabs.unpin",
|
|
"when": "false"
|
|
},
|
|
{
|
|
"command": "verticalTabs.copyPath",
|
|
"when": "false"
|
|
},
|
|
{
|
|
"command": "verticalTabs.revealInExplorer",
|
|
"when": "false"
|
|
}
|
|
]
|
|
},
|
|
"configuration": {
|
|
"title": "Vertical Tabs",
|
|
"properties": {
|
|
"verticalTabs.order": {
|
|
"type": "string",
|
|
"enum": [
|
|
"open",
|
|
"project",
|
|
"name"
|
|
],
|
|
"enumDescriptions": [
|
|
"Keep the order the editors are open in, matching the tab bar.",
|
|
"Group by project, so the colours run in blocks.",
|
|
"Sort by file name."
|
|
],
|
|
"default": "open",
|
|
"description": "How the tab list is ordered. Pinned tabs always come first."
|
|
},
|
|
"verticalTabs.pinnedFirst": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "List pinned tabs above the rest."
|
|
},
|
|
"verticalTabs.showProject": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Show the containing project name beside the file."
|
|
},
|
|
"verticalTabs.showDirectory": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Show the containing directory beside the file, for files with the same name."
|
|
},
|
|
"verticalTabs.colorByProject": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Colour each tab's icon by its containing project."
|
|
},
|
|
"verticalTabs.allGroups": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "List the tabs of every editor group. When off, only the active group is listed."
|
|
},
|
|
"verticalTabs.projectFiles": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"default": [
|
|
".csproj",
|
|
".fsproj",
|
|
".vbproj",
|
|
"package.json",
|
|
"Cargo.toml",
|
|
"go.mod",
|
|
"pyproject.toml",
|
|
"pom.xml",
|
|
"build.gradle"
|
|
],
|
|
"description": "File names or extensions that mark a project root. The nearest match above a file names its project."
|
|
}
|
|
}
|
|
},
|
|
"colors": [
|
|
{
|
|
"id": "verticalTabs.project1",
|
|
"description": "Project colour 1.",
|
|
"defaults": {
|
|
"dark": "#4ec9b0",
|
|
"light": "#267f99",
|
|
"highContrast": "#4ec9b0"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project2",
|
|
"description": "Project colour 2.",
|
|
"defaults": {
|
|
"dark": "#dcdcaa",
|
|
"light": "#795e26",
|
|
"highContrast": "#dcdcaa"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project3",
|
|
"description": "Project colour 3.",
|
|
"defaults": {
|
|
"dark": "#9cdcfe",
|
|
"light": "#001080",
|
|
"highContrast": "#9cdcfe"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project4",
|
|
"description": "Project colour 4.",
|
|
"defaults": {
|
|
"dark": "#c586c0",
|
|
"light": "#af00db",
|
|
"highContrast": "#c586c0"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project5",
|
|
"description": "Project colour 5.",
|
|
"defaults": {
|
|
"dark": "#ce9178",
|
|
"light": "#a31515",
|
|
"highContrast": "#ce9178"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project6",
|
|
"description": "Project colour 6.",
|
|
"defaults": {
|
|
"dark": "#b5cea8",
|
|
"light": "#098658",
|
|
"highContrast": "#b5cea8"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project7",
|
|
"description": "Project colour 7.",
|
|
"defaults": {
|
|
"dark": "#569cd6",
|
|
"light": "#0000ff",
|
|
"highContrast": "#569cd6"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.project8",
|
|
"description": "Project colour 8.",
|
|
"defaults": {
|
|
"dark": "#d7ba7d",
|
|
"light": "#b8860b",
|
|
"highContrast": "#d7ba7d"
|
|
}
|
|
},
|
|
{
|
|
"id": "verticalTabs.noProject",
|
|
"description": "Colour for files that belong to no project.",
|
|
"defaults": {
|
|
"dark": "descriptionForeground",
|
|
"light": "descriptionForeground",
|
|
"highContrast": "descriptionForeground"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"scripts": {
|
|
"vscode:prepublish": "npm run compile",
|
|
"compile": "tsc -p ./",
|
|
"watch": "tsc -watch -p ./",
|
|
"pretest": "npm run compile",
|
|
"test": "node ./out/test/runTest.js"
|
|
},
|
|
"devDependencies": {
|
|
"@types/mocha": "^10.0.6",
|
|
"@types/node": "^20.11.0",
|
|
"@types/vscode": "^1.85.0",
|
|
"@vscode/test-electron": "^2.4.1",
|
|
"mocha": "^10.4.0",
|
|
"typescript": "^5.4.0"
|
|
}
|
|
}
|