Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transcluding nest callouts doesn't work #967

Open
CatCodeMe opened this issue Mar 6, 2024 · 4 comments
Open

Transcluding nest callouts doesn't work #967

CatCodeMe opened this issue Mar 6, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@CatCodeMe
Copy link
Contributor

Describe the bug
When I embed block of complex callout , It don't render as same as in obsidian. See Screenshots and Source .
I use latest v4 branch , pulled minutes ago.

To Reproduce

  1. use attached files
  2. transclude_1.md is source notes
  3. transclude_2.md ref blocks in transclude_1.md

transclude_1.md

transclude_2.md

Expected behavior

  1. embed single line callout, hope to have title [!NOTE] title
  2. embed multiple line or nest callout, hope to same as in obsidian.

Screenshots and Source
This is current view in local:8080,
image

image

Desktop (please complete the following information):

  • Quartz Version: [e.g. v4.2.3]
  • node Version: [e.g. v18.16]
  • npm version: [e.g. v10.1.0]
  • OS: [e.g. macos]
  • Browser [e.g. chrome, edge]
@CatCodeMe CatCodeMe added the bug Something isn't working label Mar 6, 2024
@jackyzha0
Copy link
Owner

Can you put the file contents as a code block instead of a file? Thanks

@CatCodeMe
Copy link
Contributor Author

transclude_1

> [!NOTE] Title
> - 1231231 ^2emjcu

> [!NOTE] list
> - 1
> - 2
> - 3
>  ^m9oxui

> [!NOTE] t1
> in t1
> > [!NOTE] t2
> > in t2 ^fdxers

transclude_2

![[transclude_1#^2emjcu]]

![[transclude_1#^m9oxui]]

![[transclude_1#^fdxers]]

@saberzero1
Copy link
Collaborator

Relevant code:

let blockRef = node.properties.dataBlock as string | undefined
if (blockRef?.startsWith("#^")) {
// block transclude
blockRef = blockRef.slice("#^".length)
let blockNode = page.blocks?.[blockRef]
if (blockNode) {
if (blockNode.tagName === "li") {
blockNode = {
type: "element",
tagName: "ul",
properties: {},
children: [blockNode],
}
}
node.children = [
normalizeHastElement(blockNode, slug, transcludeTarget),
{
type: "element",
tagName: "a",
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
children: [
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
],
},
]
}

if (opts.callouts) {
if (src instanceof Buffer) {
src = src.toString()
}
src = src.replace(calloutLineRegex, (value) => {
// force newline after title of callout
return value + "\n> "
})
}

if (opts.callouts) {
plugins.push(() => {
return (tree: Root, _file) => {
visit(tree, "blockquote", (node) => {
if (node.children.length === 0) {
return
}
// find first line
const firstChild = node.children[0]
if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
return
}
const text = firstChild.children[0].value
const restOfTitle = firstChild.children.slice(1)
const [firstLine, ...remainingLines] = text.split("\n")
const remainingText = remainingLines.join("\n")
const match = firstLine.match(calloutRegex)
if (match && match.input) {
const [calloutDirective, typeString, collapseChar] = match
const calloutType = canonicalizeCallout(typeString.toLowerCase())
const collapse = collapseChar === "+" || collapseChar === "-"
const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
const titleContent = match.input.slice(calloutDirective.length).trim()
const useDefaultTitle = titleContent === "" && restOfTitle.length === 0
const titleNode: Paragraph = {
type: "paragraph",
children: [
{
type: "text",
value: useDefaultTitle ? capitalize(typeString) : titleContent + " ",
},
...restOfTitle,
],
}
const title = mdastToHtml(titleNode)
const toggleIcon = `<div class="fold-callout-icon"></div>`
const titleHtml: Html = {
type: "html",
value: `<div
class="callout-title"
>
<div class="callout-icon"></div>
<div class="callout-title-inner">${title}</div>
${collapse ? toggleIcon : ""}
</div>`,
}
const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleHtml]
if (remainingText.length > 0) {
blockquoteContent.push({
type: "paragraph",
children: [
{
type: "text",
value: remainingText,
},
],
})
}
// replace first line of blockquote with title and rest of the paragraph text
node.children.splice(0, 1, ...blockquoteContent)
const classNames = ["callout", calloutType]
if (collapse) {
classNames.push("is-collapsible")
}
if (defaultState === "collapsed") {
classNames.push("is-collapsed")
}
// add properties to base blockquote
node.data = {
hProperties: {
...(node.data?.hProperties ?? {}),
className: classNames.join(" "),
"data-callout": calloutType,
"data-callout-fold": collapse,
},
}
}
})
}
})
}

if (opts.callouts) {
js.push({
script: calloutScript,
loadTime: "afterDOMReady",
contentType: "inline",
})
}

Current problem:

I suspect the parser sees a blockNode inside the callout and transcludes just that part.

This is how the provided example (#967 (comment)) looks in Obsidian:

image

The default Obsidian behavior seems to be to include the entire callout as a transcluded blockNode.

One option could be to 'lift' the reference to include the entire callout it is contained in.

By default, Obsidian handles the callout as a single block, as if a reference exists in a nested callout, it will use that one instead of creating a new one. This means if we try to insert a transcluded link to > in t1 in the below example, it will still use the existing link ^fdxers.

> [!NOTE] t1
> in t1
> > [!NOTE] t2
> > in t2 ^fdxers

Possible solution:

Check for block-refs inside callouts. If a block-ref exists inside a callout, consider the entire callout as that block-ref for the purpose of linking/transcluding.

The two most direct approaches would be either:

  • 'lift' the block-ref to the entire callout, if it is contained in one. (this would probably happen in renderPage.tsx).
  • Add extra logic to check if a block-ref is inside a callout and change parsing accordingly. (This would probably happen in ofm.ts).

@jackyzha0 Do you have a particular preference for one of the two above, of perhaps any other insights I should take into consideration when implementing a fix for this issue?

@CatCodeMe
Copy link
Contributor Author

hope to be merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants