Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/syntax/inline-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,9 @@ export const MarkdownItInlineProps: MarkdownIt.PluginWithOptions<MdcInlinePropsO

if (
token.type === 'inline'
&& token.children?.length === 2
&& token.children[0].type === 'text'
&& token.children[1].type === 'mdc_inline_props'
&& token.children?.[token.children.length - 1].type === 'mdc_inline_props'
) {
const props = token.children[1].attrs
token.children.splice(1, 1)
const props = token.children.pop()?.attrs;
props?.forEach(([key, value]) => {
if (key === 'class')
prev.attrJoin('class', value)
Expand Down
4 changes: 4 additions & 0 deletions test/input/6.inline-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Hello World{class="text-green text-xl"}

`code`{.text-red}

**bold**{.text-red}

[Link](https://nuxt.com){class="nuxt"}

![Nuxt Logo](https://nuxt.com/assets/design-kit/logo/icon-green.svg){#nuxt-logo}
Expand Down
11 changes: 6 additions & 5 deletions test/output/6.inline-props.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<h1 class="text-red">Hello</h1>
<p class="text-green text-xl">Hello World</p>
<p><a href="https://nuxt.com" class="nuxt">Link</a></p>
<p>
<p class="text-red"><code>code</code></p>
<p class="text-red"><strong>bold</strong></p>
<p class="nuxt"><a href="https://nuxt.com">Link</a></p>
<p id="nuxt-logo">
<img
src="https://nuxt.com/assets/design-kit/logo/icon-green.svg"
alt="Nuxt Logo"
id="nuxt-logo"
/>
</p>
<p><code style="color: red">code</code></p>
<p><em style="color: blue">italic</em></p>
<p style="color: red"><code>code</code></p>
<p style="color: blue"><em>italic</em></p>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not so sure about this behaviour change, shouldn't the style applied to the code and em tag?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that! I was testing another (not yet submitted) issue and PR related to inline-props, and accidentally included those output changes in this PR’s tests.
I noticed the mistake and have already reverted the unintended output changes in this commit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you revert that and update the snapshot? Thanks

Copy link
Author

@kentrpg kentrpg Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not so sure about this behaviour change, shouldn't the style applied to the code and em tag?

Sorry, I was a bit nervous sending my first PR to a technical guru!

I've reviewed the logic again:
Since the attribute syntax is placed at the end of the line, the style is now applied to the block-level <p> tag, not the code or em tag.

`code`{style="color: red"}
_italic_{style="color: blue"}

If the attribute syntax needs to apply to the inline-level token instead, I found that adding an empty {} at the end works without changing any other logic. What do you think about this approach?

For example:

`code`{style="color: red"}{}
_italic_{style="color: blue"}{}
[Link](https://nuxt.com){class="nuxt"}{}
![Nuxt Logo](https://nuxt.com/assets/design-kit/logo/icon-green.svg){#nuxt-logo}{}

Copy link
Author

@kentrpg kentrpg Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just to double-check, should I only revert test(fixtures): fix unintended changes in inline-props output this commit, or do I also need to update the snapshot after reverting? Thanks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think {class="nuxt"}{} is a bit counter intutive, as I would expect

foo **bar**{style="color: red"} baz
**bar**{style="color: red"}

to behave the same, that applying the style to the b element

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d like to ask for a bit more detail about your expectation.
Are you expecting this behavior only when the attribute syntax sets a style property?

If so, I think I can adjust the condition like this:

if (
  token.type === 'inline' &&
  token.children?.[token.children.length - 1].type === 'mdc_inline_props' &&
  token.children[token.children.length - 1].attrs.length === 1 &&
  token.children[token.children.length - 1].attrs[0][0] === 'style'
) {
 // ...
}

The logic here is to check whether the last mdc_inline_props token only has a style attribute.
Other attributes can still be applied to the block-level token.for example, **bar**{.text-red} would apply the class to the p element.

Copy link
Author

@kentrpg kentrpg Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apply attributes to the parent token only when the content is not purely inline elements.
Below is my attempt at designing the filtering logic with a reverse for loop.

if (token.type === "inline" && token.children?.[token.children.length - 1].type === "mdc_inline_props") {
  const contentTokens = token.children.slice(0, -1)
  let inlineCount = 0
  let onlyInline = true
  for (let index = contentTokens.length - 1; index >= 0; index--) {
    if (contentTokens[index].type === 'text') {
      if (!contentTokens[index].content.trim()) 
        continue

      onlyInline = false
      break
    }

    // handle self-closing token
    if (contentTokens[index].nesting === 0) {
      inlineCount++
      continue
    }

    let searchIndex = index - 1
    while (searchIndex >= 0) {
      const searchToken = contentTokens[searchIndex]
      if (searchToken.nesting === 1 
        && searchToken.tag === contentTokens[index].tag 
        && searchToken.level === contentTokens[index].level
      ) {
        break
      }
      searchIndex--
    }

    if (searchIndex < 0)
      throw new Error(`No matching opening tag found for ${JSON.stringify(contentTokens[index])}`)

    index = searchIndex
    inlineCount++
  }

  if (!onlyInline || inlineCount !== 1) {
    const props = token.children.pop()?.attrs
    props?.forEach(([key, value]) => {
      if (key === "class")
        prev.attrJoin("class", value)
      else
        prev.attrSet(key, value)
    })
  } 
}

Not sure if this meets your expectation.

<p><strong style="color: blue">bold</strong>!</p>