From 66824850a20467deca03436307336d00950c3c07 Mon Sep 17 00:00:00 2001 From: Ethan Graf Date: Tue, 28 Jul 2026 21:15:23 -0400 Subject: [PATCH] Resolve internal links from entryId, not the document handle id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `resolveDocumentLink` was being handed `handle.id` as the source path. That is the provider's own document identity, which only coincides with a filesystem path for local vaults: `SyncedVaultDocumentsProvider` sets `id: session.uri`, an `at://` URI. Taking `dirname` of that produces a target no provider can open, so file-to-file link navigation was broken in synced vaults. `request.entryId` is the absolute path in both providers, and is already what the wikilink resolution beside it uses — having the two disagree within the same component was the confusing part. Documents the constraint on `LinkSource.path` rather than only fixing the call site, so the next caller does not reach for the handle again. Co-Authored-By: Claude Opus 5 --- src/documents/resolveDocumentLink.ts | 10 +++++++++- src/editors/DocumentSlotView.tsx | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) 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, ), ) -- 2.51.2