diff --git a/src/documents/resolveDocumentLink.ts b/src/documents/resolveDocumentLink.ts index 615aa72..ab4fb27 100644 --- a/src/documents/resolveDocumentLink.ts +++ b/src/documents/resolveDocumentLink.ts @@ -5,7 +5,15 @@ import type { OpenDocRequest } from '../workspaces/workspace'; export type LinkSource = { /** Provider the current document belongs to. */ providerId: string; - /** The current document's id/path (links resolve relative to its folder). */ + /** + * The current document's filesystem path; links resolve relative to its + * folder. + * + * Must come from `OpenDocRequest.entryId`, NOT from `DocumentHandle.id`. + * They coincide for a local vault, but a synced vault's handle id is an + * `at://` URI (`syncedVaultDocumentsProvider` sets `id: session.uri`), and + * taking `dirname` of that yields a target no provider can open. + */ path: string; }; diff --git a/src/editors/DocumentSlotView.tsx b/src/editors/DocumentSlotView.tsx index a09c007..df4509e 100644 --- a/src/editors/DocumentSlotView.tsx +++ b/src/editors/DocumentSlotView.tsx @@ -57,12 +57,16 @@ export function DocumentSlotView({ // Resolve internal-link targets against this document, then route to the // workspace's open handler. Kept here (not in each workspace) so every // workspace gets file-to-file navigation for free. + // Resolve from `request.entryId`, NOT `handle.id`: the handle's id is the + // provider's own document identity, which for a synced vault is an `at://` + // URI rather than a path (`syncedVaultDocumentsProvider` sets `id: + // session.uri`). `entryId` is always the absolute path in both providers. const { request, handle } = state; const onOpenLink = onOpenEntry ? (target: string) => onOpenEntry( resolveDocumentLink( - { providerId: request.providerId, path: handle.id }, + { providerId: request.providerId, path: request.entryId }, target, ), )