Your start screen becomes a desk: recent workspaces as manila folders, pinned
projects as folders with a paper sheet inside,
a rail of coding-activity
stats, and a note for every project — kept as markdown you own.
Every window you open is a project you were in the middle of. VS_Abode is a
place to put them: pinned work on the shelf, everything else filed in the
drawer, the one you're in right now on the desk in front of you — with its
branch, its uncommitted count, and the note you left yourself last time.
It reads your machine and nothing else. No account, no telemetry, no network
calls except an optional weather reading you have to ask for by latitude.
Install
Not on the marketplace yet. Build it and install the .vsix:
npm install
npm run bump # build + version + → vs-abode.vsix
code --install-extension vs-abode.vsix
The dashboard opens on startup in windows with no folder open, and offers once
to take the Welcome tab's place. Otherwise: VS_Abode: Open Dashboard, or
Ctrl+Alt+D
(Cmd+Alt+D on macOS).
On the desk right now
Whatever this window has open is lifted out of the grid into a band under the
letterhead — the second thing you read after the wordmark. Its folder down in
the shelf or the drawer stands proud of the row with a gold tab and an OPEN
index flag, so the desk and the band always agree about where you are.
The drawer
Everything you've opened, most recent first, in whichever view suits the moment
— 🗀 folders, ▦ cards, or ≣ ledger:
- Click a project to open it — in a new window by default;
Ctrl/Cmd-click does the opposite. It's stamped
OPENING… while the
window comes up.
- PIN any project to put it on the shelf as a full folder. On a fresh
install the one you've opened most is pinned for you, so the desk is never
empty — unpin it and it stays unpinned.
- Search filters live on name and path; tag chips filter the drawer.
- FILE A FOLDER adds a project you haven't opened yet, from a dialog.
- RESUME reopens the most recent workspace along with the files that were
open in it.
- Folders that have been deleted are struck through and marked missing rather
than silently failing to open.
Notes, on paper
The NOTES chip on any folder — or Ctrl/Cmd+Alt+N
— pulls that project's note onto the desk as a full sheet. It autosaves when you
pause, and Esc files it back.
A note is a plain markdown file kept beside the project it belongs to:
- A folder project writes
<project>/.abode/notes.md.
- A
.code-workspace project writes <folder>/.abode/<workspace name>.md,
so two workspaces sharing a folder don't share a sheet.
Nothing about them is a database: they travel with the repo, survive
uninstalling this extension, and can be opened, diffed and grepped like any
other file. ↗ OPEN IN EDITOR hands the real file to VS Code, and saving it
there re-reads the sheet so the two never drift.
A colour per project
Assign a project a colour and its VS Code window wears it on the title bar,
activity bar and status bar — so two windows are never mistaken for each other.
Thirteen inks ordered by hue, chosen so no two read alike at a glance, plus any
hex you care to type.
The lettering is whichever of the brand's ink or bone gives the better contrast
on the colour, never below 4.5:1. See Window tints for exactly
what gets written where.
Day desk, night desk
The header toggle swaps warm paper for graphite and rewrites the copy that goes
with it — stamped daily, in ink becomes stamped nightly, in chalk. Left on
auto, it follows your VS Code theme.
Settings
| Setting |
Default |
What it does |
vsAbode.userName |
OS user |
Name in the header greeting stamp |
vsAbode.openInNewWindow |
true |
Open projects in a new window (ctrl/cmd-click inverts) |
vsAbode.openOnStartup |
whenEmpty |
whenEmpty / always / never |
vsAbode.theme |
auto |
auto follows the VS Code color theme kind; the header toggle writes this |
vsAbode.windowTint |
true |
Let an assigned colour tint that project's window |
vsAbode.showStickyNote |
true |
Show the milestone note in the rail |
vsAbode.maxPinned |
4 |
Pins beyond this fall back into the drawer |
vsAbode.drawerLimit |
12 |
Recents shown before SHOW ALL |
vsAbode.restoreEditors |
true |
Remember open files per project for RESUME |
vsAbode.weather.latitude / .longitude |
unset |
Header weather via open-meteo; omitted when unset |
vsAbode.weather.units |
celsius |
celsius or fahrenheit |
vsAbode.scan.excludeDirs |
node_modules, .git, … |
Directories skipped when scanning |
vsAbode.scan.maxFiles |
20000 |
Upper bound on files visited per project |
vsAbode.cacheTtlMinutes |
60 |
How long scan results stay fresh |
Commands
| Command |
|
| VS_Abode: Open Dashboard |
Ctrl/Cmd+Alt+D |
| VS_Abode: Open Project Notes |
Ctrl/Cmd+Alt+N, or Ctrl/Cmd+Shift+N with the dashboard focused |
| VS_Abode: Refresh Project Data |
force a rescan, ignoring the cache |
| VS_Abode: Add a Folder to the Drawer |
|
| VS_Abode: Pin Current Workspace |
|
| VS_Abode: Edit Tags… |
|
| VS_Abode: Set Window Colour… |
|
| VS_Abode: Remove Missing Projects |
|
| VS_Abode: Clear Stored Project Data |
|
| VS_Abode: Copy Build Info |
version, commit, VS Code version and platform |
Pin/unpin, notes, window colour, open in new window, open in this window, reveal
and remove are context-menu commands on a project; they're hidden from the
palette because they need a target.
Everything is keyboard-reachable: Tab to a folder,
Enter/Space to open it.
How the data is gathered
The extension keeps its own MRU rather than reading VS Code's recently-opened
list, which is not public API: every workspace open records path, timestamp
and an open count in globalState. Saved .code-workspace files are recorded
as workspaces (and file under their name without the extension); everything else
is recorded as a folder. On first run it makes one best-effort attempt to seed
from the internal workbench.getRecentlyOpened command, then never touches it
again.
Per project, a background pass runs one filesystem walk (size, LOC, TODO/FIXME
count, language shares by byte count) and a handful of git invocations
(rev-parse, status --porcelain, log --since=126.days, rev-list --count).
Results are cached in globalState with a TTL (vsAbode.cacheTtlMinutes,
default 60); pinned projects are scanned first, the panel is updated after each
one lands, and the pass is cancelled when the panel closes. Folders that have
been deleted are marked missing and skipped rather than rescanned —
VS_Abode: Remove Missing Projects clears them out.
The rail's heatmap, streak, velocity, most-thumbed files and sticky-note
milestones are derived from the same commit data, summed across every project.
Window tints
Assigning a project a colour is VS_Abode's own record, kept in globalState
alongside the pins and tags — a spot of that colour appears on the project's
folder tab. The window only wears it once that project is open:
workbench.colorCustomizations is a window-scoped setting, so it has to live in
that project's own settings, and there is no API to colour a window that isn't
running.
- A folder project gets the keys written to
<project>/.vscode/settings.json.
A .code-workspace project gets them inside the workspace file instead —
no new file in the repo.
- VS_Abode writes through VS Code's own configuration API, so comments and
formatting in an existing settings file survive, and only the thirteen keys it
owns are touched. Anything else in
colorCustomizations is left alone, and
clearing a tint removes the setting rather than leaving {} behind.
- The write happens on open and is skipped when the window already wears the
right colour, so it doesn't dirty the file on every start.
.vscode/settings.json will show up in git status — add it to .gitignore
if you'd rather it didn't.
- Turn the whole behaviour off with
vsAbode.windowTint: false; the colours
then stay in VS_Abode and nothing is written to your projects.
Project notes, in detail
- The blotter autosaves when you pause for about ¾ of a second, and again
when the dashboard loses focus. Esc or clicking the desk behind
files it back.
- ↗ OPEN IN EDITOR writes what's on the sheet, then hands the real file to
VS Code. Opening a note that doesn't exist yet is how you start one — it's
seeded with the project's name as a heading.
- Saving that file in the editor re-reads the sheet, so the two never drift.
Typing on the sheet that hasn't been filed yet always wins over a push from
disk; nothing overwrites you mid-sentence.
- Clearing a note deletes the file rather than leaving an empty stub behind.
- The first note written into a git repo asks once whether
.abode/ should
go in .gitignore. Say no and notes are yours to commit like anything else.
Developing
npm run watch # esbuild, rebuilds on change
npm test # typecheck + bundle + unit and activation-smoke tests
npm run test:integration # drives a real VS Code (downloads it on first run)
Press F5 (Run Extension) for an Extension Development Host.
The integration runner passes the extension path to VS Code unquoted, so it
fails if the checkout lives under a directory with a space in its name. CI
paths are fine; locally, run it from a space-free copy.
To iterate on the design without an extension host, serve the repo and open
design/preview.html — it loads the real
media/dashboard.{css,js} against a mocked acquireVsCodeApi and sample data,
notes included:
npx http-server -p 8777 -c-1 . # then open /design/preview.html
Built from the design handoff in design/HANDOFF.md — the
hi-fi reference lives in the Claude Design project
VS Abode Dashboard concepts.
Layout
src/
extension.ts activation, commands, workspace + editor tracking
panel.ts the webview panel — CSP/nonce, messages, serializer
dashboardData.ts builds the render payload; owns the background scan
actions.ts open / add / tag / colour / resolve-project flows
activity.ts pure heatmap, streak and velocity math (unit-tested)
notes.ts note paths, read/write, word counts (no vscode import)
tint.ts window-tint palette and colour maths (no vscode import)
build.ts the version + commit stamp esbuild injects
store.ts globalState: MRU, pins, tags, colours, stats, UI state
format.ts bytes / LOC / relative time / sparkline / language bar
types.ts shared shapes and the message protocol
collect/
git.ts branch, dirty, commits, touched files, first commit
scan.ts one fs walk → size, LOC, TODO count, language shares
weather.ts optional open-meteo reading for the header
media/
dashboard.css the design tokens and layout, ported from the reference
dashboard.js renders the payload; no framework
fonts.css, fonts/ bundled JetBrains Mono + Newsreader (webviews are offline)
scripts/ build stamp, package guard, bump and release
test/ node:test units + a smoke test over the real bundle
test/integration/ mocha suites that run inside a real VS Code
Releasing
Every bundle is stamped with the version and commit it was built from. The
dashboard's colophon prints that imprint — 1.0.0 · 6eac61d, with a trailing
+ if the tree had uncommitted work in it — and clicking it (or VS_Abode:
Copy Build Info) puts the full line, VS Code version and platform included, on
the clipboard.
npm run bump # patch-bump, build, → vs-abode.vsix
npm run bump minor # or major, or an explicit 1.2.3
npm run package # build at the current version; refuses a dirty tree
npm run package -- --allow-dirty # …unless you mean it; stamped dirty
npm run release # patch: test, bump, commit, tag, package
npm run release minor # or major, or an explicit 1.2.3
npm run bump is the everyday one: it raises the version in package.json
and package-lock.json, rebuilds, and writes the .vsix — so every build you
install is a distinguishable version, and the colophon tells you which one is
running. It touches git not at all: nothing committed, nothing tagged, no
clean-tree check, so it works mid-edit.
npm run release runs the tests, bumps the version with npm version (which
makes the commit and the tag), then packages that exact commit. It pushes
nothing — the tag and the .vsix wait for you:
git push --follow-tags
It also warns if CHANGELOG.md still has an Unreleased heading, since a
release nobody can read later isn't much of a release.
Publishing
npx vsce login MadeByAriel # needs an Azure DevOps PAT with Marketplace scope
npm run release
npx vsce publish
The extension publishes as MadeByAriel.vs-abode. That publisher id has to
exist on the marketplace before the first publish — create it once at
marketplace.visualstudio.com/manage,
using the same Azure DevOps account the PAT belongs to.
Images never travel inside the .vsix. vsce rewrites every relative image
link in this README against the repository url — docs/brand/banner.png
becomes https://github.com/arielruff/VS-Abode/raw/HEAD/docs/brand/banner.png —
and the marketplace fetches it from there. Adding docs/ to the package would
make the download bigger and change nothing on the page. So a relative link
renders on the marketplace only once the repository is public and pushed; an
absolute one renders either way.
The originals live in docs/. The desk screenshots come from
design/preview.html; the banner is built by design/banner-build.html —
serve the repo, then screenshot #banner (it renders at 2× for a crisp PNG).
Known limitations
- The sticky note only knows about commit anniversaries, round-number commit
milestones and streaks.
- Weather is off unless you set a latitude and longitude.
- Multi-root workspaces are filed by their
.code-workspace file; stats come
from the directory containing it, not from every root. A window tint colours
the whole window, not one root.
- A window tint lands when the project is opened, not when it is assigned — a
window that's already running elsewhere keeps its old colour until it's
reopened.
filed daily, in ink
MIT © Ariel Ruff