From 74c5ea4108292d80283028f61eb5c7768e7c9bd0 Mon Sep 17 00:00:00 2001 From: Alexander Alemayhu Date: Tue, 24 Dec 2024 07:42:12 +0100 Subject: [PATCH] feat: add image support for Markdown --- src/lib/parser/DeckParser.ts | 49 ++++++----- .../handleNestedBulletPointsInMarkdown.ts | 84 +++++++++++++++++-- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/lib/parser/DeckParser.ts b/src/lib/parser/DeckParser.ts index 22ee1a73..f3de6ac5 100644 --- a/src/lib/parser/DeckParser.ts +++ b/src/lib/parser/DeckParser.ts @@ -54,6 +54,9 @@ export class DeckParser { noLimits: boolean; + workspace: Workspace; + customExporter: CustomExporter; + public get name() { return this.payload[0].name; } @@ -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); } @@ -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 @@ -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, () => { @@ -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; @@ -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, @@ -430,7 +438,7 @@ export class DeckParser { ); } const newFileName = embedFile({ - exporter, + exporter: this.customExporter, files: this.files, filePath: global.decodeURIComponent(audiofile), workspace: ws, @@ -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) { const fallback = new FallbackParser(this.files); - const exporter = this.setupExporter(this.payload, ws.location); this.payload = fallback.run(this.settings); if ( @@ -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() { diff --git a/src/lib/parser/handleNestedBulletPointsInMarkdown.ts b/src/lib/parser/handleNestedBulletPointsInMarkdown.ts index 8df61c6f..30636147 100644 --- a/src/lib/parser/handleNestedBulletPointsInMarkdown.ts +++ b/src/lib/parser/handleNestedBulletPointsInMarkdown.ts @@ -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, [], @@ -39,7 +63,30 @@ 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); + 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 = ''; @@ -55,7 +102,30 @@ export const handleNestedBulletPointsInMarkdown = ( } if (currentBack !== '' || currentFront !== '') { - deck.cards.push(new Note(currentFront, markdownToHTML(currentBack))); + const dom = cheerio.load(currentBack); + 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;