Add explorer copy path, native file actions, and Lapis app URLs #90

Closed
opened 2026-05-22 21:07:20 +00:00 by steve · 0 comments
Owner

Add daily-use file location actions to the file explorer and add a Lapis app URL
contract for opening vault files from copied links or external app launches.

Problem

The file explorer does not expose common file-location actions that users expect
in a local-first notes app: copying vault-relative paths, copying host paths,
copying durable app URLs, opening files in the OS default app, or revealing files
in the host file manager.

Lapis also does not yet have an app URL contract comparable to Obsidian URI
links. Without that, users cannot copy a stable lapis://open?... URL from the
explorer or open a file from outside the app with a link such as:

lapis://open?vault=vault&file=02 - Areas/Swift/MX/ISO20022 A Deep Dive on Pacs-008.pdf

Goal

Make explorer file actions and Lapis app URLs work across the supported
daily-use hosts, with Electron receiving full native file actions and browser/PWA
using only actions the web platform can support safely.

Scope

  • Add a Copy Path submenu to explorer item context menus.
  • Add desktop/native explorer actions for opening and revealing selected files.
  • Add host-neutral native bridge capability and commands for absolute paths,
    default-app open, and file-manager reveal.
  • Add a Lapis app URL parser/dispatcher for opening vault files.
  • Register the Electron desktop app as a handler for the lapis:// custom
    scheme and preserve the existing lapis-notes:// alias if needed.
  • Add PWA protocol-handler support only where browsers allow it.
  • Add tests and update package/rendered specs.

Non-goals

  • Do not expose raw absolute host paths in browser-local OPFS sessions.
  • Do not force unsupported PWA platforms to show native open/reveal actions.
  • Do not register Lapis as an OS handler for obsidian://.
  • Do not implement Obsidian's new, daily, unique, search,
    choose-vault, or Hook integration in this slice.
  • Do not add broad plugin URI actions beyond the protocol-handler compatibility
    needed for existing Obsidian-style plugin APIs.

Acceptance Criteria

  • Explorer context menus include Copy Path with:
    • From vault folder
    • From system root when the host can provide a system path
    • As Lapis URL for files only
  • Electron explorer context menus include native file actions between
    separators:
    • Open in default app
    • Reveal in Finder on macOS, Reveal in File Explorer on Windows, or
      Reveal in file manager elsewhere
  • PWA/browser sessions only show or enable native file actions when the active
    host advertises support for them.
  • Copy actions write the expected text to the clipboard and surface success or
    failure through the normal notice path.
  • As Lapis URL produces encoded lapis://open?vault=...&file=... links for
    files.
  • The app can parse and dispatch lapis://open?vault=<vault>&file=<path> with
    encoded paths and lenient existing links that contain spaces or slashes.
  • The app supports Obsidian-compatible open parameters where practical:
    • vault can match the current vault name or id
    • file can be a vault-relative path or markdown path without .md
    • path overrides vault and file on desktop/native sessions
    • paneType=tab|split|window maps to supported Lapis workspace behavior, with
      window desktop-only or gracefully unsupported
    • heading/block suffixes in file remain part of the open request for later
      navigation handling instead of being discarded
  • The app supports Obsidian shorthand open formats:
    • lapis://vault/<vault>/<file>
    • lapis:///absolute/path
  • Electron registers and handles lapis:// links on cold start, second
    instance launches, and macOS open-url, queueing links until the renderer is
    ready.
  • Plugin.registerObsidianProtocolHandler() registers and cleans up handlers
    against the shared URL service so Lapis/plugin actions can reuse the same
    decoded parameter shape.
  • Tests cover URL parsing, explorer menu behavior, native bridge validation, and
    supported Electron launch paths.
  • Specs document path-copy behavior, native action capabilities, app URL
    routing, PWA limitations, and Electron protocol registration.

Implementation Notes

  1. Define shared path and URL helpers.

    Add a small API/workspace helper for explorer targets:

    • vault-relative path: file.path, no leading slash
    • system path: only when the active adapter/host can resolve a real absolute
      path
    • Lapis URL: files only, generated as
      lapis://open?vault=<encoded vault id or name>&file=<encoded vault-relative path>

    Generated URLs should be strictly encoded even though the parser should
    accept lenient inputs such as unencoded spaces and / in copied examples.

  2. Update the file explorer context menu.

    In packages/workspace/src/lib/feature/file-explorer/file-explorer.svelte,
    add:

    • Copy Path submenu
    • From vault folder
    • From system root
    • As Lapis URL only for TFile

    Add desktop actions between separators:

    • separator
    • Open in default app
    • platform-specific reveal label
    • separator

    Browser File System Access does not expose stable absolute system paths or OS
    reveal/default-app hooks, so those actions should stay hidden or disabled
    unless a future web host capability explicitly advertises support.

  3. Extend the native desktop bridge.

    Add host-neutral bridge commands/capabilities for:

    • resolving a vault-relative path to an absolute host path
    • opening a path in the OS default app
    • revealing a path in the host file manager

    Electron should implement these in
    packages/desktop-electron/src-electron/main.ts using Electron
    shell.openPath() and shell.showItemInFolder().

  4. Add Lapis app URL parsing and dispatch.

    Add an API-owned URL service, such as AppUrlService, that can parse and
    dispatch:

    • lapis://open?vault=<vault>&file=<path>
    • lapis://open?path=<absolute path>
    • lapis://vault/<vault>/<file> shorthand
    • lapis:///absolute/path shorthand
    • optional paneType=tab|split|window

    Match Obsidian's important open semantics: path overrides vault and
    file; file can be a filename or vault-relative path; .md can be omitted
    for markdown; heading/block suffixes should remain part of the file
    navigation target.

  5. Register desktop custom protocols.

    Electron currently packages lapis-notes://. Add lapis:// as the primary
    scheme and keep lapis-notes:// as an alias if needed.

    Handle URLs from:

    • macOS open-url
    • Windows/Linux second-instance argv
    • cold start before renderer boot

    Queue inbound URLs until the workspace is mounted, then dispatch through the
    shared URL service.

  6. Add PWA URL handling.

    Add web manifest protocol-handler support only where browsers allow it. The
    practical web target is:

    • web+lapis://open?... for installed PWAs where supported
    • an HTTPS fallback route such as /open?url=<encoded lapis url>

    Bare lapis:// should be treated as native desktop-first.

  7. Support Obsidian-style custom action handlers.

    Plugin.registerObsidianProtocolHandler() is currently effectively a no-op.
    Implement it against the new URL service so plugins can register custom
    actions, preserving the Obsidian-compatible decoded param shape where
    obsidian://action?key=value maps to params including action.

    For Lapis, dispatch lapis://<action>?... to the same registry.

  8. Update tests and docs.

    Add focused tests for:

    • explorer menu items by runtime capability
    • copy text values for vault path, absolute path, and Lapis URL
    • Electron open/reveal IPC validation
    • URL parsing for vault/file, path, shorthand forms, encoded paths, and
      lenient unencoded spaces/slashes
    • plugin protocol handler registration and disposal

Suggested Files or Specs To Inspect

  • packages/workspace/src/lib/feature/file-explorer/file-explorer.svelte
  • packages/workspace/src/lib/feature/file-explorer/index.ts
  • packages/api/src/lib/context.svelte.ts
  • packages/api/src/lib/plugin.ts
  • packages/api/src/lib/storage/desktop-native.ts
  • packages/api/src/lib/storage/vault-state.ts
  • packages/desktop-electron/src-electron/main.ts
  • packages/desktop-electron/src-electron/preload.ts
  • packages/desktop-electron/electron-builder.config.cjs
  • packages/web/vite.config.ts
  • spec/src/20-packages/workspace/index.md
  • spec/src/20-packages/desktop-electron/index.md
  • spec/src/20-packages/web/index.md
  • spec/src/30-cross-package-contracts/storage-metadata-search.md
  • spec/src/30-cross-package-contracts/plugin-runtime.md

Validation Commands

pnpm --filter @lapis-notes/api check
pnpm --filter @lapis-notes/workspace check
pnpm --filter @lapis-notes/desktop-electron check
pnpm --filter @lapis-notes/web check
make spec-lint
mdbook build spec
pnpm test:smoke

Follow-up Tasks

  • Add richer URL actions such as new, search, or choose-vault only after
    the open path is stable.
  • Add full heading/block scroll targeting if the initial open implementation
    preserves but does not yet navigate subpaths.
  • Revisit PWA bare lapis:// support if browser protocol-handler restrictions
    change.

Implementation Summary

Implemented explorer Copy Path submenu actions, native Electron file open/reveal/path resolution IPC, Lapis app URL parsing and dispatch, desktop lapis:// and lapis-notes:// launch handling, PWA web+lapis routing, and Obsidian-style plugin protocol handler registration through the app URL service.

Add daily-use file location actions to the file explorer and add a Lapis app URL contract for opening vault files from copied links or external app launches. ## Problem The file explorer does not expose common file-location actions that users expect in a local-first notes app: copying vault-relative paths, copying host paths, copying durable app URLs, opening files in the OS default app, or revealing files in the host file manager. Lapis also does not yet have an app URL contract comparable to Obsidian URI links. Without that, users cannot copy a stable `lapis://open?...` URL from the explorer or open a file from outside the app with a link such as: ```text lapis://open?vault=vault&file=02 - Areas/Swift/MX/ISO20022 A Deep Dive on Pacs-008.pdf ``` ## Goal Make explorer file actions and Lapis app URLs work across the supported daily-use hosts, with Electron receiving full native file actions and browser/PWA using only actions the web platform can support safely. ## Scope - Add a `Copy Path` submenu to explorer item context menus. - Add desktop/native explorer actions for opening and revealing selected files. - Add host-neutral native bridge capability and commands for absolute paths, default-app open, and file-manager reveal. - Add a Lapis app URL parser/dispatcher for opening vault files. - Register the Electron desktop app as a handler for the `lapis://` custom scheme and preserve the existing `lapis-notes://` alias if needed. - Add PWA protocol-handler support only where browsers allow it. - Add tests and update package/rendered specs. ## Non-goals - Do not expose raw absolute host paths in browser-local OPFS sessions. - Do not force unsupported PWA platforms to show native open/reveal actions. - Do not register Lapis as an OS handler for `obsidian://`. - Do not implement Obsidian's `new`, `daily`, `unique`, `search`, `choose-vault`, or Hook integration in this slice. - Do not add broad plugin URI actions beyond the protocol-handler compatibility needed for existing Obsidian-style plugin APIs. ## Acceptance Criteria - Explorer context menus include `Copy Path` with: - `From vault folder` - `From system root` when the host can provide a system path - `As Lapis URL` for files only - Electron explorer context menus include native file actions between separators: - `Open in default app` - `Reveal in Finder` on macOS, `Reveal in File Explorer` on Windows, or `Reveal in file manager` elsewhere - PWA/browser sessions only show or enable native file actions when the active host advertises support for them. - Copy actions write the expected text to the clipboard and surface success or failure through the normal notice path. - `As Lapis URL` produces encoded `lapis://open?vault=...&file=...` links for files. - The app can parse and dispatch `lapis://open?vault=<vault>&file=<path>` with encoded paths and lenient existing links that contain spaces or slashes. - The app supports Obsidian-compatible `open` parameters where practical: - `vault` can match the current vault name or id - `file` can be a vault-relative path or markdown path without `.md` - `path` overrides `vault` and `file` on desktop/native sessions - `paneType=tab|split|window` maps to supported Lapis workspace behavior, with `window` desktop-only or gracefully unsupported - heading/block suffixes in `file` remain part of the open request for later navigation handling instead of being discarded - The app supports Obsidian shorthand open formats: - `lapis://vault/<vault>/<file>` - `lapis:///absolute/path` - Electron registers and handles `lapis://` links on cold start, second instance launches, and macOS `open-url`, queueing links until the renderer is ready. - `Plugin.registerObsidianProtocolHandler()` registers and cleans up handlers against the shared URL service so Lapis/plugin actions can reuse the same decoded parameter shape. - Tests cover URL parsing, explorer menu behavior, native bridge validation, and supported Electron launch paths. - Specs document path-copy behavior, native action capabilities, app URL routing, PWA limitations, and Electron protocol registration. ## Implementation Notes 1. Define shared path and URL helpers. Add a small API/workspace helper for explorer targets: - vault-relative path: `file.path`, no leading slash - system path: only when the active adapter/host can resolve a real absolute path - Lapis URL: files only, generated as `lapis://open?vault=<encoded vault id or name>&file=<encoded vault-relative path>` Generated URLs should be strictly encoded even though the parser should accept lenient inputs such as unencoded spaces and `/` in copied examples. 2. Update the file explorer context menu. In `packages/workspace/src/lib/feature/file-explorer/file-explorer.svelte`, add: - `Copy Path` submenu - `From vault folder` - `From system root` - `As Lapis URL` only for `TFile` Add desktop actions between separators: - separator - `Open in default app` - platform-specific reveal label - separator Browser File System Access does not expose stable absolute system paths or OS reveal/default-app hooks, so those actions should stay hidden or disabled unless a future web host capability explicitly advertises support. 3. Extend the native desktop bridge. Add host-neutral bridge commands/capabilities for: - resolving a vault-relative path to an absolute host path - opening a path in the OS default app - revealing a path in the host file manager Electron should implement these in `packages/desktop-electron/src-electron/main.ts` using Electron `shell.openPath()` and `shell.showItemInFolder()`. 4. Add Lapis app URL parsing and dispatch. Add an API-owned URL service, such as `AppUrlService`, that can parse and dispatch: - `lapis://open?vault=<vault>&file=<path>` - `lapis://open?path=<absolute path>` - `lapis://vault/<vault>/<file>` shorthand - `lapis:///absolute/path` shorthand - optional `paneType=tab|split|window` Match Obsidian's important `open` semantics: `path` overrides `vault` and `file`; `file` can be a filename or vault-relative path; `.md` can be omitted for markdown; heading/block suffixes should remain part of the file navigation target. 5. Register desktop custom protocols. Electron currently packages `lapis-notes://`. Add `lapis://` as the primary scheme and keep `lapis-notes://` as an alias if needed. Handle URLs from: - macOS `open-url` - Windows/Linux second-instance argv - cold start before renderer boot Queue inbound URLs until the workspace is mounted, then dispatch through the shared URL service. 6. Add PWA URL handling. Add web manifest protocol-handler support only where browsers allow it. The practical web target is: - `web+lapis://open?...` for installed PWAs where supported - an HTTPS fallback route such as `/open?url=<encoded lapis url>` Bare `lapis://` should be treated as native desktop-first. 7. Support Obsidian-style custom action handlers. `Plugin.registerObsidianProtocolHandler()` is currently effectively a no-op. Implement it against the new URL service so plugins can register custom actions, preserving the Obsidian-compatible decoded param shape where `obsidian://action?key=value` maps to params including `action`. For Lapis, dispatch `lapis://<action>?...` to the same registry. 8. Update tests and docs. Add focused tests for: - explorer menu items by runtime capability - copy text values for vault path, absolute path, and Lapis URL - Electron open/reveal IPC validation - URL parsing for `vault/file`, `path`, shorthand forms, encoded paths, and lenient unencoded spaces/slashes - plugin protocol handler registration and disposal ## Suggested Files or Specs To Inspect - `packages/workspace/src/lib/feature/file-explorer/file-explorer.svelte` - `packages/workspace/src/lib/feature/file-explorer/index.ts` - `packages/api/src/lib/context.svelte.ts` - `packages/api/src/lib/plugin.ts` - `packages/api/src/lib/storage/desktop-native.ts` - `packages/api/src/lib/storage/vault-state.ts` - `packages/desktop-electron/src-electron/main.ts` - `packages/desktop-electron/src-electron/preload.ts` - `packages/desktop-electron/electron-builder.config.cjs` - `packages/web/vite.config.ts` - `spec/src/20-packages/workspace/index.md` - `spec/src/20-packages/desktop-electron/index.md` - `spec/src/20-packages/web/index.md` - `spec/src/30-cross-package-contracts/storage-metadata-search.md` - `spec/src/30-cross-package-contracts/plugin-runtime.md` ## Validation Commands ```bash pnpm --filter @lapis-notes/api check pnpm --filter @lapis-notes/workspace check pnpm --filter @lapis-notes/desktop-electron check pnpm --filter @lapis-notes/web check make spec-lint mdbook build spec pnpm test:smoke ``` ## Follow-up Tasks - Add richer URL actions such as `new`, `search`, or `choose-vault` only after the `open` path is stable. - Add full heading/block scroll targeting if the initial open implementation preserves but does not yet navigate subpaths. - Revisit PWA bare `lapis://` support if browser protocol-handler restrictions change. ## Implementation Summary Implemented explorer Copy Path submenu actions, native Electron file open/reveal/path resolution IPC, Lapis app URL parsing and dispatch, desktop lapis:// and lapis-notes:// launch handling, PWA web+lapis routing, and Obsidian-style plugin protocol handler registration through the app URL service. <!-- backlog:task_id=ROADMAP-008 source_spec=spec/src/50-roadmap/complete-daily-use-app.md -->
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lapis-notes/lapis#90
No description provided.