Skip to content

Commit

Permalink
feat: another implementation of image capture
Browse files Browse the repository at this point in the history
    Don't use separate image_capture tag
    that potentially can break other image-related plugings
  • Loading branch information
osulyanov committed Dec 18, 2024
1 parent 1174cd9 commit c19de0f
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 83 deletions.
2 changes: 1 addition & 1 deletion 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' && childToken.type !== 'image_with_caption') {
if (childToken.type !== 'image') {
return;
}

Expand Down
134 changes: 52 additions & 82 deletions src/transform/plugins/images/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,56 +87,66 @@ function convertSvg(
}
}

type Opts = SVGOpts & ImageOpts;
function imageCaption(state: StateCore) {
const tokens = state.tokens;

const index: MarkdownItPluginCb<Opts> = (md, opts) => {
md.assets = [];
for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type !== 'inline') {
continue;
}

md.inline.ruler.after('image', 'image_caption', (state, silent) => {
const pos = state.pos;
const max = state.posMax;
const childrenTokens = tokens[i].children || [];
const newTokens: Token[] = [];

if (state.tokens.length === 0 || state.tokens[state.tokens.length - 1].type !== 'image') {
return false;
}
if (state.src.charCodeAt(pos) !== 0x7b /* { */) {
return false;
}
for (let j = 0; j < childrenTokens.length; j++) {
const token = childrenTokens[j];

if (token.type === 'image') {
const attrs = token.attrs || [];
const hasCaptionAttr = attrs.some(([key]) => key === 'caption');

if (hasCaptionAttr) {
const captionAttr = attrs.find(([key]) => key === 'caption');
const explicitCaption = captionAttr ? captionAttr[1] : '';
const title = attrs.find(([key]) => key === 'title');
const captionText = explicitCaption || (title ? title[1] : '');

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;
const figureOpen = new state.Token('figure_open', 'figure', 1);
const figureClose = new state.Token('figure_close', 'figure', -1);

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,
token,
captionOpen,
captionContent,
captionClose,
figureClose,
);
} else {
newTokens.push(figureOpen, token, figureClose);
}
} else {
newTokens.push(token);
}
} else {
newTokens.push(token);
}
curPos++;
}

if (!found) {
return false;
}
tokens[i].children = newTokens;
}
}

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;
}
type Opts = SVGOpts & ImageOpts;

state.pos = curPos + 1;
return true;
});
const index: MarkdownItPluginCb<Opts> = (md, opts) => {
md.assets = [];

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

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

if (didPatch) {
Expand All @@ -177,44 +184,6 @@ const index: MarkdownItPluginCb<Opts> = (md, opts) => {
j++;
}

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

while (j < childrenTokens.length) {
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 @@ -225,6 +194,7 @@ const index: MarkdownItPluginCb<Opts> = (md, opts) => {
md.core.ruler.push('images', plugin);
}

md.core.ruler.push('image_caption', imageCaption);
md.renderer.rules.image_svg = (tokens, index) => {
const token = tokens[index];

Expand Down

0 comments on commit c19de0f

Please sign in to comment.