Skip to content

[Discussion] Spaces are a powerful idea hidden behind a "folders with superpowers" framing — proposing a Concepts doc + a few surgical UX fixes #547

Description

@kundeng

Type: Architecture / Documentation / UX
Severity: Conceptual (suspected root cause of many usability reports)

TL;DR

After several hours configuring Make.md from scratch in a real working vault (5,000+ notes, several symlinked external folders) and then reading the source carefully, I came away convinced:

A Space is a queryable subset of a vault — defined by folder, by tag, by the whole vault, or by an arbitrary saved query — rendered through views (table/board/calendar/gallery), with user-defined typed columns (contexts) and optional custom frames.

In other words: Notion-style databases where the "database" is any slice of your vault.

That is a genuinely ambitious and genuinely useful idea — Dataview + Bases + Kanban + a frame engine, unified. But the README markets it as "folders with extra powers," and the in-product UI reinforces the smaller framing. New users build the wrong mental model on day one and never recover.

This issue argues that the gap is conceptual scaffolding and a handful of small UX affordances, not a rewrite.

The model I reverse-engineered from source

Reading src/shared/types/PathState.ts, src/shared/types/spaceInfo.ts, src/shared/types/spaceDef.ts, src/core/types/space.ts, and src/core/spaceManager/filesystemAdapter/, the actual model is:

Entity Definition How it surfaces
PathState Any addressable node — file, folder, tag, virtual entity — plus its memberships (spaces, linkedSpaces, liveSpaces) The "rows" in a Space
SpaceInfo / FilesystemSpaceInfo A Space's static on-disk location: path, defPath, notePath, and (on the FS subtype) folderPath, dbPath, framePath, commandsPath 6+ artifacts on disk per Space
SpaceDefinition A Space's behavior: contexts, sort, joins, links, tags, template, ... Folder-note YAML (_contexts, _joins, _links, _sort, _template)
SpaceType 'folder' | 'tag' | 'vault' | 'default' | 'unknown' Five fundamentally different containers, same UI chrome
waypoints.json (Focus[]) List of pinned top-level Spaces — each a { name, sticker, paths: string[] } The Spaces sidebar's pinned rail
PathState.spaces vs linkedSpaces vs liveSpaces Three membership-like arrays; their distinct semantics are undocumented in the type Indistinguishable in the UI

A single working folder-Space therefore depends on the consistency of:

  1. A real folder on disk (for folder-typed Spaces)
  2. A folder note (<name>.md) carrying SpaceDefinition in frontmatter
  3. A .space/ subfolder with views.mdb / context.mdb (SQLite, via sql.js)
  4. A waypoints.json entry (if pinned)
  5. A PathState entry in .makemd/superstate.mdc
  6. The .makemd/fileCache.mdc index being current
  7. Inverse references (linkedSpaces) on every member path

That's seven artifacts. Any one can drift silently. When any drift, the Space "looks empty" with no diagnostic.

Confusions we hit, mapped to causes

Symptom Root cause in the model
Created a Space via UI named n8n docs → it's empty The paths value was a display-style label, not a resolvable folder path
Symlinked folders never appear in the Spaces panel even though they appear in Obsidian's native file explorer Make.md inherits whatever Obsidian's vault API returns; symlinks that Obsidian's adapter excludes are invisible to Make.md too, with no warning or alternative path
Vault changes made while Obsidian is closed aren't reflected on next launch .makemd/fileCache.mdc and .makemd/superstate.mdc are persisted and trusted on startup; in-session events are handled, but there's no reconciliation pass on load
No command palette entry to reindex / rescan None implemented, even though a superstateReindex event already exists internally (dispatched from superstate.ts)
Adding a file to a Space is non-obvious for new users The command mk-pin-active ("Pin Active File to Space") exists and works — but its name doesn't surface the "add to Space" concept that new users are searching for
waypoints.json schema bundles all pinned entries without distinguishing their role The paths: string[] field carries the pinned target(s) flatly, without separating "the Space's home" from any reference scoping
The vault-root Spaces/ folder feels magical It's just a default container, but new users assume all Spaces must live inside it

Proposed changes — small, surgical, high-leverage

Each of the following closes a documented class of confusion.

1. A Concepts doc, linked prominently from the README

One page. The framing sentence at the top:

A Space is a queryable subset of your vault — defined by folder, by tag, by the whole vault, or by an arbitrary query — rendered as a view, with optional typed columns.

Then the table above, plus three diagrams: (a) the seven artifacts of a folder-Space, (b) Path→Space membership via the three relationship types, (c) which file stores what. This single page would do more for adoption than any code change.

2. Make.md: Reindex vault command

The single highest-leverage UX win, and almost free to implement: the superstateReindex event is already wired internally and dispatched from superstate.ts. A command palette entry that clears fileCache.mdc + superstate.mdc, triggers a re-walk, and emits the existing reindex event (plus a Notice with progress) would be a thin wrapper over machinery that already exists.

Many "everything is empty after I did X" reports would resolve themselves the first time a user runs this — especially for users (like me) whose vault changed between Obsidian sessions.

3. Treat symlinks as a first-class question

Make.md's filesystem adapter doesn't independently resolve symlinks — it delegates to Obsidian's vault API. For users whose vault contains symlinked external project trees, this means whole folder subtrees can be invisible to Spaces while still visible in Obsidian's own file explorer (which traverses them just fine), with no diagnostic.

Two reasonable directions:

  • Document the dependency explicitly: "Make.md indexes only what Obsidian's adapter exposes; symlinks behave as Obsidian configures them."
  • Or add an optional followSymlinks setting that bypasses the Obsidian adapter for known mount points (with a depth cap to prevent loops) and feeds those paths into the index.

Even just a warning chip in the Spaces panel for "this Space's folder appears to be a symlink — make sure your vault adapter is configured to follow it" would close a real gap.

4. Disambiguate spaces / linkedSpaces / liveSpaces in PathState

These three arrays carry distinct membership semantics in the implementation, but the type itself documents none of them — and they all look the same in the UI. Either rename for clarity:

type PathState = {
  containedInSpaces: string[];   // physical parent
  linkedFromSpaces: string[];    // someone _links to me
  queriedIntoSpaces: string[];   // I match a Space's joins/filters
  ...
}

…or unify into one field with a per-edge type tag:

spaceMembership: { spacePath: string; via: 'contained' | 'linked' | 'query' }[]

The latter would also let the UI become honest: hovering a row in a Space view could show why that row is there. For a Notion-style database where membership rules can be subtle, this is critical — and for the source, it would replace a load-bearing tribal-knowledge split with a documented one.

5. Surface SpaceType in the UI

Five fundamentally different containers (folder, tag, vault, default, unknown) appear to share the same sticker, menu, and property pane. A small chip or distinct icon per type would let users immediately recognize what kind of container they are looking at — and would also signal the system's full power to users who currently only see "fancy folders."

6. Promote Pin Active File to Space as the "Add to Space" affordance

The command exists and does the right thing — it just isn't named in the language users search for. Two cheap improvements:

  • Rename or alias the command to Make.md: Add active file to Space (keeping the old name as an alias for compatibility).
  • Surface it from the file context menu and from a Space's "+" button as "Add file to this Space…" rather than only as a draggable affordance.

The existence of mk-pin-active is one of the clearest demonstrations that the model is sound and the discoverability gap is the real issue.

7. Make.md: Diagnose this Space command

Walks the seven artifacts and reports which are present / missing / stale. Even a simple Notice saying "Space 'X' is missing its folder note" or "Space 'X' has no .space/views.mdb — recreate?" would close a large class of "it looks empty" reports.

Why one issue instead of seven

These problems get reported individually as bugs, but they share a single root cause: users don't know the data model exists. A Concepts page, a Reindex command (which is mostly wrapping an existing event), a Diagnose command, and a rename of Pin Active File to Space would, I suspect, dramatically reduce the rate of new confusion reports — and would let new users perceive the actual ambition of the system instead of mistaking it for a folder skin.

I'd be happy to take a first pass at the Reindex vault command and the Concepts doc as a PR if maintainers think these directions make sense. Before writing code I'd love to know:

  1. Is the spaces / linkedSpaces / liveSpaces split intentional and load-bearing, or accreted? Would unification break the views / contexts pipeline?
  2. Is the README's "folders with superpowers" framing a deliberate audience choice (non-technical), or just unfinished? Happy to write the Concepts page in whichever register fits.
  3. Is there an existing internal design doc I missed?

Thanks for the work on this plugin. The data-model power is real and impressive — it just needs the conceptual scaffolding to land for users.


Related forum discussion: Make.md Spaces, Add files to Space in Make.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions