Extending the Tree-View from Owned Packages
Pulsar’s tree-view exposes several extension points — context menus, file icons, and icon colors — all of which can be added from an owned package without touching tree-view itself.
Context menus
Context menu items are matched to right-click targets using CSS selectors. Multiple packages can target the same selector; their items are merged.
Static declaration (preferred)
Add entries to menus/<package-name>.json under "context-menu":
{
"context-menu": {
".tree-view li.directory > .header": [
{ "label": "Open all URLs…", "command": "tranquil-browser:open-all-urls-in-folder" },
{ "type": "separator" }
],
".tree-view .file .name[data-name$='.url']": [
{ "label": "Open URL…", "command": "tranquil-browser:open-url-file" },
{ "type": "separator" }
]
}
} Then register the commands in activate() inside lib/<package>.js:
this.subscriptions.add(
atom.commands.add('atom-workspace', {
'tranquil-browser:open-url-file': function (event) {
const entry = event.target.closest('.entry');
const filePath = entry && entry.getPath();
if (filePath) atom.workspace.open(filePath);
},
})
); Dynamic registration
For conditional or runtime-generated items, use atom.contextMenu.add(). It returns a Disposable so items can be removed later.
const disposable = atom.contextMenu.add({
'.tree-view li.directory > .header': [
{ label: 'My Action', command: 'my-package:my-action' }
]
});
// later: disposable.dispose(); Useful tree-view selectors
| Target | Selector |
|---|---|
| Any directory header | .tree-view li.directory > .header |
| Any file | .tree-view [is="tree-view-file"] |
| File by extension | .tree-view .file .name[data-name$='.url'] |
| Project root header | .tree-view .project-root > .header |
| Multi-select | .tree-view .multi-select |
Separators
Use { "type": "separator" } as an item to draw a divider line. Place one below your group to visually separate Tranquil items from native tree-view items.
Getting the clicked item’s path
Inside a command handler, retrieve the path of the right-clicked file or directory:
const entry = event.target.closest('.entry');
const filePath = entry && entry.getPath(); Both [is="tree-view-file"] and [is="tree-view-directory"] elements expose getPath().
File type icons
Tree-view consumes the atom.file-icons service (1.0.0). Providing this service from an owned package replaces the default icon logic entirely — the provider must handle all file types, not just the ones it cares about.
Declaring the service
In package.json:
"providedServices": {
"atom.file-icons": {
"versions": { "1.0.0": "provideFileIcons" }
}
} Implementing the provider
Delegate unknown extensions back to tree-view’s own DefaultFileIcons so only the target extension is customised:
provideFileIcons() {
let defaultFileIcons = null;
return {
iconClassForPath(filePath) {
if (typeof filePath === 'string' && filePath.endsWith('.url')) {
return 'icon-globe';
}
if (!defaultFileIcons) {
const pkg = atom.packages.getLoadedPackage('tree-view');
if (pkg) defaultFileIcons = require(path.join(pkg.path, 'lib', 'default-file-icons'));
}
return defaultFileIcons ? defaultFileIcons.iconClassForPath(filePath) : 'icon-file-text';
}
};
} Icon classes are Octicons — see static/variables/octicon-utf-codes.less for available names (icon-globe, icon-browser, icon-link, icon-file-text, etc.).
The provider lives in tranquil-theme-icons (../tranquil-theme-icons/lib/tranquil-theme-icons.js).
Icon colors and alignment
Icon glyphs are rendered via ::before pseudo-elements. Color and alignment can be set from any package’s Less stylesheet using ui-variables for theme-awareness.
@import "ui-variables";
.tree-view .name[data-name$=".url"]::before {
color: @text-color-info;
vertical-align: middle;
position: relative;
top: -1px; /* glyph-specific vertical nudge — adjust per icon */
} @text-color-info is the standard theme blue. Add rules per extension as new file types need colors. The stylesheet lives in tranquil-theme-icons (../tranquil-theme-icons/styles/tranquil-theme-icons.less).
Tree-view selection tracking (getPath)
Tree-view highlights a file entry when its pane item is active by calling getActivePaneItem().getPath() and matching the result against entries. If a custom pane item (e.g. TranquilBrowserModel) doesn’t implement getPath(), the entry is immediately deselected after opening.
Fix: implement getPath on the model
Store the source file path when the model is created from a file:
// in the opener
var model = new TranquilBrowserModel({
tranquilBrowser: _this,
url: url,
filePath: filePath, // the .url file path, not the web URL
opt: {},
}); // in the model class
getPath() {
return this.filePath;
} Return null/undefined for models not backed by a file (e.g. opened directly from a URL) — tree-view will simply deselect in that case, which is correct.
Existing examples
| Feature | Package | File |
|---|---|---|
| Context menu (URL files + folders) | tranquil-browser | menus/tranquil-browser.json |
| Context menu (markdown files) | markdown-preview | packages/markdown-preview/menus/ |
| Context menu (search in folder) | find-and-replace | packages/find-and-replace/menus/ |
| File icons + colors | tranquil-theme-icons | lib/, styles/ |
| getPath on browser model | tranquil-browser | lib/tranquil-browser-model.js |