Skip to content

Nested custom entries that reuse existing entries have prefixing side effects #37

Description

@kgar

Given this Obsidian Callout custom entry:

export type ObsidianCalloutEntry = {
  callout: {
    type: string;
    title?: InlineTypes[] | InlineTypes;
    content: MarkdownEntry | MarkdownEntry[];
    initialState?: 'expanded' | 'collapsed';
  };
};

export const calloutRenderer: MarkdownRenderer = (
  entry: ObsidianCalloutEntry,
  options: RenderOptions
) => {
  let titleContent = getTitleContent(entry);

  let content = Array.isArray(entry.callout.content)
    ? entry.callout.content
    : [entry.callout.content];

  let initialState =
    entry.callout.initialState === 'collapsed'
      ? '-'
      : entry.callout.initialState === 'expanded'
      ? '+'
      : '';

  const blockquote: BlockquoteEntry = {
    blockquote: [
      { p: [`[!${entry.callout.type}]${initialState}`, ...titleContent] },
      ...content,
    ],
  };

  return {
    markdown: renderEntries([blockquote], { ...options),
    blockLevel: true,
  };
};

function getTitleContent(entry: ObsidianCalloutEntry): InlineTypes[] {
  if (!entry.callout.title) {
    return [];
  }

  const title = entry.callout.title;
  let normalizedTitle = Array.isArray(title) ? title : [title];

  return [' ', ...normalizedTitle];
}

The expected format of nested callouts is to append one additional layer of blockquote. So, a callout within a callout should look like this:

> [!info]+ Some Title Here
> 
> > [!quote]+ 
> > 
> > Here's a quote.

Instead, it looks like this:

> [!info]+ Some Title Here
> 
> > > [!quote]+ 
> > > 
> > > Here's a quote.

In general, this is because the callout implementation is calling renderEnties(), which is the established example in the docs.

The workaround is to update the callout renderer as follows:

  return {
    markdown: renderEntries([blockquote], { ...options, prefix: '' }),
    blockLevel: true,
  };

This works, but it feels awkward to do, and it's not obvious to an implementer of custom renderers which reuse existing renderers.

I'm not sure yet on a solution for this that will prevent headaches for custom entry authors, but I'm putting the issue here for now so it can be explored later.

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