Lossless Changelog
component

Open in Obsidian, Actually — Vault Detection and a Cleaner Deal Header

The 'Open in Obsidian' button was silently doing nothing. Turns out `open -a Obsidian /some/folder` is a no-op unless that folder *is* a vault — so we taught the app to walk up to the nearest `.obsidian/` and route through `obsidian://` URIs instead. While we were in there, the deal-workspace header that hosted that button got a long-overdue redesign.

Michael StatonMusic Editoraugmented with Claude Code on Opus 4.7

Open in Obsidian, Actually — Vault Detection and a Cleaner Deal Header

Why Care?

If you're using MemoPop as a research surface, the most valuable artifact a memo run produces is the folder full of .md files — sections, source catalogs, the cross-version best-of curation. The fastest way to live in those files is Obsidian. Until today, the "Open in Obsidian" button looked like it was working and was actually doing nothing. You clicked it, the app shrugged silently, you went on with your day mildly confused.

It now opens the firm folder inside its enclosing Obsidian vault, with the file explorer focused on the right path, the way you assumed it always did. And the row of buttons that hosts that action stopped looking like four mismatched candies arranged on a tray.

What's New?

  • Open in Obsidian actually opens the right vault. A new Rust query walks up from any path looking for a .obsidian/ directory and returns {vault_root, vault_name, rel_path}. The frontend uses that to build an obsidian://open?vault=…&file=… URI instead of open -a Obsidian <folder> (which Obsidian ignores for non-vault folders).

  • Graceful Finder fallback. When the target isn't inside any Obsidian vault, the same call falls back to revealItemInDir so the user lands in Finder rather than a confused null state.

  • Curation reveal lands on a real note. The "Open best-of-sources/" button on the curation result banner now targets Master-Sources.md inside that folder — Obsidian needs a file to open, not just a directory, and the headline note is the right one.

  • Sidecar paths come back absolute. The orchestrator's POST /actions/curate-sources was returning io/alpha-partners/... because Path('io').resolve() was missing. The Tauri side fed that relative path to /usr/bin/open which resolved it against the wrong cwd. One .resolve() call fixed it.

  • Capabilities widened for obsidian: URLs. The Tauri opener plugin rejects non-http URL schemes unless they're explicitly allowed. Added opener:allow-open-url with obsidian:, https:, http:.

  • Deal-workspace header redesigned. Single-row layout, controls all share a 32px height, the back action is a compact icon, the firm chip and version count live on one non-wrapping line, the Finder + Obsidian buttons collapsed into an icon-grouped pill with text in tooltips. The "Curate Best Sources" pill keeps its gradient prominence but no longer wraps.

The Story

We sat down to use the "Curate Best Sources" feature against the alpha-partners/ChromaDB deal. 159 sources collapsed cleanly from 392 per-section entries, the result banner appeared, we clicked the Open best-of-sources/ button — and nothing happened. No error. No window. No anything.

First instinct: the path is wrong. The error banner had been refactored to surface the exception, so we routed the open call through the wrapper that the working folder-open buttons use. That coughed up the real failure:

Couldn't open io/alpha-partners/deals/ChromaDB/exports/best-of-sources:
  Launcher "/usr/bin/open"
  "io/alpha-partners/deals/ChromaDB/exports/best-of-sources"
  "-a" "/Applications/Obsidian.app"
  failed with ExitStatus(unix_wait_status(256))

There it was — a relative path. The Python sidecar was returning io/alpha-partners/... and /usr/bin/open was resolving it against its own current working directory (which isn't the orchestrator repo root). One-line fix in the FastAPI handler:

PYTHON
return {
    ...
-   "output_dir": str(result.output_dir),
+   "output_dir": str(result.output_dir.resolve()),
}

Restart the sidecar (the Python process needs to be re-spawned — Svelte HMR doesn't bounce it). Click again. Still nothing. No error this time, just silence.

That's when the second problem revealed itself. open -a Obsidian /some/random/folder on macOS does not surface an error and does not open the folder. Obsidian only opens vaults, not arbitrary directories. If the target isn't a vault root, the launcher exits 0 and Obsidian quietly ignores you.

The fix had to acknowledge that Obsidian is a vault-shaped app, not a folder-shaped app. So:

  1. Walk up the directory tree from the target path looking for a .obsidian/ sibling (the marker of a vault root).

  2. If found, build the canonical deep-link URI: obsidian://open?vault=<vault-name>&file=<rel-path-from-vault-root>. Open that as a URL via the opener plugin.

  3. If not found, fall back to revealItemInDir so the user at least lands in Finder with the item highlighted.

The vault walker lives in Rust (apps/memopop-native/src-tauri/src/api/queries.rs) and is exposed via the same HTTP-style router pattern the rest of the local-only routes use:

RUST
pub fn find_obsidian_vault(path: &str) -> Value {
    let target = Path::new(path);
    let mut cursor = if target.is_file() {
        target.parent().map(|p| p.to_path_buf())
    } else {
        Some(target.to_path_buf())
    };
    while let Some(dir) = cursor {
        if dir.join(".obsidian").is_dir() {
            // …return {vault_root, vault_name, rel_path}…
        }
        cursor = dir.parent().map(|p| p.to_path_buf());
    }
    Value::Null
}

On the JS side, revealInVaultOrFinder(absPath, { preferFileInDir? }) handles both branches. The preferFileInDir option exists because the obsidian:// URI scheme has no "reveal folder" verb — for the best-of-sources case, we point Obsidian at Master-Sources.md inside the folder, which is the headline document anyway.

One last gotcha: Tauri's opener plugin rejects non-http(s) URL schemes unless the capabilities file explicitly allows them. The obsidian:// calls were getting silently dropped until we added:

JSON
{
  "identifier": "opener:allow-open-url",
  "allow": [
    { "url": "obsidian:" },
    { "url": "https:" },
    { "url": "http:" }
  ]
}

With all three fixes in, the button now works end to end — across the firm-open, version-open, and best-of-sources-open call sites.

And While We Were Looking at the Header…

The deal-workspace header above the content — the row with the back button, deal title, version selector, and three action buttons — had four problems at once:

  1. ← Home rendered as a labeled button taking the visual weight of a primary action when it's really an escape hatch.

  2. The deal title was 1.4rem, the firm chip sat below it on its own line, and the version count wrapped to a third line when the firm name was long.

  3. Curate Best Sources + Open in Finder + Open in Obsidian were three same-styled buttons whose long labels wrapped to 2–3 lines each in normal window widths. The result looked like a cluttered candy tray.

  4. There was no shared height rhythm between the version dropdown and the buttons — different paddings, different border radii, different colors.

The redesign:

[←]   ChromaDB                          [ChromaDB-v0.0.1-1 · 2d ago ▾]  [✨ Curate Best Sources]  [📁│📓]
      alpha-partners · 8 versions
  • One row, justify-content: space-between, flex

  • All controls share --ctl-height: 32px and --ctl-radius: 8px

  • ← Home collapses to a 32×32 icon button (label moved to title/aria-label)

  • Title drops to 1.25rem with text-overflow: ellipsis

  • Subtitle is a single non-wrapping line: firm chip + version count

  • Version select gets a custom SVG caret and a slate-rgba background that works in both light and dark

  • "Curate Best Sources" keeps its violet→pink gradient but gains a subtle violet glow shadow and a spinning ⏳ glyph during the run

  • Finder + Obsidian collapse into a bordered icon-group pill: two 32×32 icon buttons sharing one border, divider between them, text moved to tooltips. The Obsidian icon gets a subtle violet hover tint to hint at its role

  • All the old hardcoded light/dark CSS for these controls was replaced with rgba(slate) tokens that work in both modes without separate @media blocks

The dead .notebook-btn and .finder-btn styles were swept out. The result reads as one rhythm, no labels wrap, and the row stops competing with the actual content below it.

How to Test

  1. Quit MemoPop (Cmd-Q) and relaunch — the Rust crate changed, so HMR isn't enough.

  2. Open a deal that has at least one prior memo run (e.g. alpha-partners / ChromaDB).

  3. Confirm the header: single row, no wrapped labels, the back arrow is now icon-only.

  4. Click Curate Best Sources. Wait for the violet banner.

  5. Click Open best-of-sources/ on the banner — Obsidian should focus the alpha-partners vault and open Master-Sources.md.

  6. Click the 📓 icon in the header group — Obsidian should focus the same vault at the firm root.

  7. Click the 📁 icon — Finder reveals the current version's folder.

  8. (Optional) Point at a path that's not inside an Obsidian vault — both icon buttons should now fall through to Finder rather than silently doing nothing.

What's Next

  • The Trustworthy Citations rollout (Trustworthy-Citations-Source-Harvester-Rollout.md in the orchestrator's context-v/plans/) is the next push. Phase 1 — fix the self-disabling URL validator at citation_validator.py:302, swap the HEAD-only urllib probe for a real GET, add the verdict ladder (verified-accessible / verified-gated / verified-via-republish / soft-404-graceful / hard-404 / hallucinated-pattern / thin / title-swapped) plus a reputable-gated publisher allow-list. Today's curation banner deliberately admits "Validity not yet checked — soft-404s and paywall stubs may still be present"; that caveat goes away once Phase 1 lands.

  • The Obsidian-not-installed case currently surfaces the macOS handler-missing dialog if you click 📓 without Obsidian. A graceful frontend fallback to Finder when openUrl('obsidian://…') rejects would be friendlier.

  • The vault walker is wired only into the three deal-workspace open buttons today. If we add more reveal actions elsewhere (job view, outline tree, brand config viewer), they should route through the same revealInVaultOrFinder helper.