Skip to content

Original-file link modes break when an item is renamed in Eagle #4

Description

@ChristianBili

Versions: CMDS Eagle 1.8.4 · Obsidian [1.14.4] · Eagle [4.0.0] · macOS [26.6.2]
Link mode: cmds-eagle-photo-link (same cause for cmds-eagle and cmds-eagle-photo-only)

What happens

  1. Embed an item with an original-file mode. The note contains
    [![Old name](file:///…/images/<ID>.info/Old%20name.png)](eagle://item/<ID>).
  2. Rename the item in Eagle.
  3. Eagle renames the file on disk to New name.png; the file:// URL in the note now points to a missing file and the image no longer renders.

The alt text (and any caption plugin that reads it, e.g. Image Captions) also keeps the old name.

Why it matters

The README states that Eagle is the source of truth for these modes. Renaming is a routine operation in Eagle, so today it silently breaks notes.

Suggested fix (render-time, note untouched)

The <ID>.info folder in the path is stable. At render time the plugin can read the current name from <ID>.info/metadata.json and repoint the image, without rewriting the note and without needing Eagle to be running.

I patched my local main.js along these lines and it works on a 2,500-item library:

// Called from the Markdown post-processor and from observeRenderedImages()
// (outside the cross-platform guard, so it runs for every user).
private resolveEagleImage(img: HTMLImageElement): void {
  const src = img.getAttribute('src');
  if (!src || img.dataset.eagleResolvedSrc === src) return;
  const path = extractPathFromImageSrc(src);
  const match = path?.match(/^(.*[\\/]images[\\/][^\\/]+\.info)[\\/][^\\/]+$/);
  if (path && match) void this.applyEagleImage(img, src, path, match[1]);
}

private async applyEagleImage(img: HTMLImageElement, src: string, path: string, dir: string) {
  let meta: { name?: string; ext?: string; isDeleted?: boolean };
  try {
    meta = JSON.parse(await fsp.readFile(`${dir}/metadata.json`, 'utf8'));
  } catch { return; }
  if (!meta.name || meta.isDeleted) return;

  let file = `${meta.name}.${meta.ext}`;
  try {
    await fsp.access(`${dir}/${file}`);
  } catch {
    const files = (await fsp.readdir(dir).catch(() => []))
      .filter(f => f !== 'metadata.json' && !f.endsWith('_thumbnail.png'));
    if (!files.length) return;
    file = files[0];
  }
  if (img.getAttribute('src') !== src) return; // replaced meanwhile

  const current = `${dir}/${file}`;
  if (current.normalize('NFC') !== path.normalize('NFC')) {
    img.setAttribute('src', pathToResourceUrl(current, Platform.resourcePathPrefix));
  }
  img.dataset.eagleResolvedSrc = img.getAttribute('src') ?? '';
  img.setAttribute('alt', meta.name);
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions