Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions src/transform/plugins/images/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const collect = (input: string, options: Options) => {
const children = token.children || [];

children.forEach((childToken) => {
if (childToken.type !== 'image') {
if (childToken.type !== 'image' && childToken.type !== 'image_with_caption') {
return;
}

Expand All @@ -43,7 +43,7 @@ const collect = (input: string, options: Options) => {
if (singlePage && !path.includes('_includes/')) {
const newSrc = relative(root, resolveRelativePath(path, src));

result = result.replace(src, newSrc);
result = result.replace(new RegExp(src, 'g'), newSrc);
}

copyFile(targetPath, targetDestPath);
Expand Down
96 changes: 94 additions & 2 deletions src/transform/plugins/images/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,52 @@ type Opts = SVGOpts & ImageOpts;
const index: MarkdownItPluginCb<Opts> = (md, opts) => {
md.assets = [];

md.inline.ruler.after('image', 'image_caption', (state, silent) => {
const pos = state.pos;
const max = state.posMax;

if (state.tokens.length === 0 || state.tokens[state.tokens.length - 1].type !== 'image') {
return false;
}
if (state.src.charCodeAt(pos) !== 0x7b /* { */) {
return false;
}

let found = false;
let curPos = pos + 1;
let captionText = '';

while (curPos < max) {
if (state.src.charCodeAt(curPos) === 0x7d /* } */) {
const content = state.src.slice(pos + 1, curPos).trim();
const captionMatch = content.match(/^caption(?:="([^"]*)")?$/);
if (captionMatch) {
found = true;
captionText = captionMatch[1] || '';
break;
}
}
curPos++;
}

if (!found) {
return false;
}

if (!silent) {
const token = state.tokens[state.tokens.length - 1];
token.type = 'image_with_caption';
if (captionText) {
token.attrSet('caption', captionText);
}
state.pos = curPos + 1;
return true;
}

state.pos = curPos + 1;
return true;
});

const plugin = (state: StateCore) => {
const tokens = state.tokens;
let i = 0;
Expand All @@ -106,11 +152,15 @@ const index: MarkdownItPluginCb<Opts> = (md, opts) => {
let j = 0;

while (j < childrenTokens.length) {
if (childrenTokens[j].type === 'image') {
if (
childrenTokens[j].type === 'image' ||
childrenTokens[j].type === 'image_with_caption'
) {
const didPatch = childrenTokens[j].attrGet('yfm_patched') || false;

if (didPatch) {
return;
j++;
continue;
}

const imgSrc = childrenTokens[j].attrGet('src') || '';
Expand All @@ -124,10 +174,47 @@ const index: MarkdownItPluginCb<Opts> = (md, opts) => {

childrenTokens[j].attrSet('yfm_patched', '1');
}
j++;
}

j = 0;
const newTokens: Token[] = [];

while (j < childrenTokens.length) {
Copy link
Member

Choose a reason for hiding this comment

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

Replace default token image with image_with_caption can be wery destructive for external plugins, which searches for image token only.

I propose to implement this in other way:

  • Check what image token has caption attr.
  • Then generate new token image_caption after this one.
  • Add renderer for image_caption which will generate all required a11y compatible html.
  • Convert caption attr on image to some class attr image.attrSet('class', image.attrGet('class') + ' yfm-image-with-caption')
  • Add some styles to main css.

Copy link
Author

Choose a reason for hiding this comment

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

Makes sense, I got rid of image_with_caption token

Copy link
Author

Choose a reason for hiding this comment

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

Wdyt, does the current solution work or potentially could have a negative impact on other image-related plugins?
What about the CSS? Do I need to align it to the left?
Screenshot 2024-12-18 at 19 01 38

if (childrenTokens[j].type === 'image_with_caption') {
const explicitCaption = childrenTokens[j].attrGet('caption');
const title = childrenTokens[j].attrGet('title');
const captionText = explicitCaption || title || '';

const figureOpen = new state.Token('figure_open', 'figure', 1);
const figureClose = new state.Token('figure_close', 'figure', -1);

childrenTokens[j].type = 'image';

if (captionText) {
const captionOpen = new state.Token('figcaption_open', 'figcaption', 1);
const captionContent = new state.Token('text', '', 0);
captionContent.content = captionText;
const captionClose = new state.Token('figcaption_close', 'figcaption', -1);

newTokens.push(
figureOpen,
childrenTokens[j],
captionOpen,
captionContent,
captionClose,
figureClose,
);
} else {
newTokens.push(figureOpen, childrenTokens[j], figureClose);
}
} else {
newTokens.push(childrenTokens[j]);
}
j++;
}

tokens[i].children = newTokens;
i++;
}
};
Expand All @@ -143,6 +230,11 @@ const index: MarkdownItPluginCb<Opts> = (md, opts) => {

return token.attrGet('content') || '';
};

md.renderer.rules.figure_open = () => '<figure>';
md.renderer.rules.figure_close = () => '</figure>';
md.renderer.rules.figcaption_open = () => '<figcaption>';
md.renderer.rules.figcaption_close = () => '</figcaption>';
};

export = index;
Loading