Skip to content

Commit

Permalink
feat: add image support for Markdown
Browse files Browse the repository at this point in the history
  • Loading branch information
aalemayhu committed Dec 24, 2024
1 parent 3694d18 commit a15f22f
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 32 deletions.
5 changes: 4 additions & 1 deletion src/lib/parser/DeckParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ test('Markdown nested bullet points', async () => {
expect(deck.cards[1].name).toBe('<ul>\n<li>' + 'What is the capital of Norway' + '</li>\n</ul>');
expect(deck.cards[1].back).toBe('<pre><code>Oslo</code></pre>');
expect(deck.cards[2].name).toBe('<ul>\n<li>' + 'What is the capital of Sweden'+'</li>\n</ul>');
expect(deck.cards[2].back).toBe('<pre><code>Stockholm</code></pre>');

console.log('Deck card 2 back:', deck.cards[2].back);

expect(deck.cards[2].back).toBe('<p>Stockholm</p>');
expect(deck.cards.length).toBe(3);
})
51 changes: 29 additions & 22 deletions src/lib/parser/DeckParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export class DeckParser {

noLimits: boolean;

workspace: Workspace;
customExporter: CustomExporter;

public get name() {
return this.payload[0].name;
}
Expand All @@ -65,6 +68,11 @@ export class DeckParser {
this.noLimits = input.noLimits;
this.globalTags = null;
this.payload = [];
this.workspace = new Workspace(true, 'fs');
this.customExporter = new CustomExporter(
input.name,
this.workspace.location
);
this.processFirstFile(input.name);
}

Expand All @@ -73,13 +81,16 @@ export class DeckParser {

if (this.settings.nestedBulletPoints && isMarkdownFile(name)) {
const contents = getFileContents(firstFile, false);
this.payload = handleNestedBulletPointsInMarkdown(
this.payload = handleNestedBulletPointsInMarkdown({
name,
contents?.toString(),
this.settings.deckName,
[],
this.settings
);
contents: contents?.toString(),
deckName: this.settings.deckName,
decks: [],
settings: this.settings,
exporter: this.customExporter,
workspace: this.workspace,
files: this.files,
});
} else if (isHTMLFile(name)) {
const contents = getFileContents(firstFile, true);
this.payload = contents
Expand Down Expand Up @@ -254,13 +265,6 @@ export class DeckParser {
return dom('.page-body > p > del');
}

setupExporter(decks: Deck[], workspace: string) {
for (const d of decks) {
d.style = d.cleanStyle();
}
return new CustomExporter(this.firstDeckName, workspace);
}

// https://stackoverflow.com/questions/6903823/regex-for-youtube-id
_getYouTubeID(input: string) {
return this.ensureNotNull(input, () => {
Expand Down Expand Up @@ -363,7 +367,11 @@ export class DeckParser {
}

build(ws: Workspace) {
const exporter = this.setupExporter(this.payload, ws.location);
if (ws.location !== this.workspace.location) {
console.debug('workspace location changed for build');
console.debug(ws.location);
this.customExporter = new CustomExporter(this.firstDeckName, ws.location);
}

for (const d of this.payload) {
const deck = d;
Expand Down Expand Up @@ -401,7 +409,7 @@ export class DeckParser {
const originalName = dom(elem).attr('src');
if (originalName && isImageFileEmbedable(originalName)) {
const newName = embedFile({
exporter,
exporter: this.customExporter,
files: this.files,
filePath: decodeURIComponent(originalName),
workspace: ws,
Expand Down Expand Up @@ -430,7 +438,7 @@ export class DeckParser {
);
}
const newFileName = embedFile({
exporter,
exporter: this.customExporter,
files: this.files,
filePath: global.decodeURIComponent(audiofile),
workspace: ws,
Expand Down Expand Up @@ -486,13 +494,12 @@ export class DeckParser {
}

this.payload[0].settings = this.settings;
exporter.configure(this.payload);
return exporter.save();
this.customExporter.configure(this.payload);
return this.customExporter.save();
}

tryExperimental(ws: Workspace) {
tryExperimental() {
const fallback = new FallbackParser(this.files);
const exporter = this.setupExporter(this.payload, ws.location);

this.payload = fallback.run(this.settings);
if (
Expand All @@ -504,9 +511,9 @@ export class DeckParser {
}

this.payload[0].settings = this.settings;
exporter.configure(this.payload);
this.customExporter.configure(this.payload);

return exporter.save();
return this.customExporter.save();
}

totalCardCount() {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/parser/PrepareDeck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export async function PrepareDeck(
const htmlFile = convertedFiles.find((file) => isHTMLFile(file.name));
parser.processFirstFile(htmlFile?.name ?? input.name);
} else {
const apkg = await parser.tryExperimental(input.workspace);
const apkg = await parser.tryExperimental();
return {
name: getDeckFilename(parser.name ?? input.name),
apkg,
Expand Down
88 changes: 81 additions & 7 deletions src/lib/parser/handleNestedBulletPointsInMarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,42 @@ import { getTitleFromMarkdown } from './getTitleFromMarkdown';
import get16DigitRandomId from '../../shared/helpers/get16DigitRandomId';
import Note from './Note';
import { markdownToHTML } from '../markdown';
import cheerio from 'cheerio';

import CardOption from './Settings';
import { embedFile } from './exporters/embedFile';
import { isImageFileEmbedable } from '../storage/checks';
import CustomExporter from './exporters/CustomExporter';
import Workspace from './WorkSpace';

import { File } from '../zip/zip';

const BULLET_POINT_REGEX = /^-/;

interface HandleNestedBulletPointsInMarkdownInput {
name: string;
contents: string | undefined;
deckName: string | undefined;
decks: Deck[];
settings: CardOption;
exporter: CustomExporter;
workspace: Workspace;
files: File[];
}

export const handleNestedBulletPointsInMarkdown = (
name: string,
contents: string | undefined,
deckName: string | undefined,
decks: Deck[],
settings: CardOption
input: HandleNestedBulletPointsInMarkdownInput
) => {
const {
name,
contents,
deckName,
decks,
settings,
exporter,
workspace,
files,
} = input;
const deck = new Deck(
deckName ?? getTitleFromMarkdown(contents) ?? name,
[],
Expand All @@ -39,7 +63,32 @@ export const handleNestedBulletPointsInMarkdown = (

const isEnd = lines.length - 1 == lines.indexOf(line);
if (isEnd || (BULLET_POINT_REGEX.exec(line) && isCreating)) {
deck.cards.push(new Note(currentFront, markdownToHTML(currentBack)));
const dom = cheerio.load(currentBack, {
xmlMode: true,
});
const images = dom('img');
const media: string[] = [];

images.each((_i, elem) => {
const src = dom(elem).attr('src');
if (src && isImageFileEmbedable(src)) {
const newName = embedFile({
exporter: exporter,
files: files,
filePath: src,
workspace: workspace,
});
if (newName) {
dom(elem).attr('src', newName);
media.push(newName);
}
}
});

currentBack = dom.html();
const note = new Note(currentFront, markdownToHTML(currentBack));
note.media = media;
deck.cards.push(note);
isCreating = false;
currentFront = '';
currentBack = '';
Expand All @@ -55,7 +104,32 @@ export const handleNestedBulletPointsInMarkdown = (
}

if (currentBack !== '' || currentFront !== '') {
deck.cards.push(new Note(currentFront, markdownToHTML(currentBack)));
const dom = cheerio.load(currentBack, {
xmlMode: true,
});
const images = dom('img');
const media: string[] = [];

images.each((_i, elem) => {
const src = dom(elem).attr('src');
if (src && isImageFileEmbedable(src)) {
const newName = embedFile({
exporter,
files: files,
filePath: src,
workspace,
});
if (newName) {
dom(elem).attr('src', newName);
media.push(newName);
}
}
});

currentBack = dom.html() || '';
const note = new Note(currentFront, markdownToHTML(currentBack));
note.media = media;
deck.cards.push(note);
}

return decks;
Expand Down
2 changes: 1 addition & 1 deletion src/test/fixtures/simple-deck.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

- What is the capital of Sweden

Stockholm
Stockholm

0 comments on commit a15f22f

Please sign in to comment.