From 4bdd94cbf061790e1477d10eece4bf347b85739f Mon Sep 17 00:00:00 2001 From: Puetsua Date: Sat, 17 Jan 2026 19:43:09 +0800 Subject: [PATCH] feat(marked-options): Refactor renderer methods and add hooks for image sizing syntax --- src/app/ui/marked-options-factory.ts | 55 +++++++++------------------- src/main.ts | 7 +++- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/app/ui/marked-options-factory.ts b/src/app/ui/marked-options-factory.ts index 51bcccf985..51eadeabdd 100644 --- a/src/app/ui/marked-options-factory.ts +++ b/src/app/ui/marked-options-factory.ts @@ -1,4 +1,5 @@ import { MarkedOptions, MarkedRenderer } from 'ngx-markdown'; +import { Hooks } from 'marked'; /** * Parses image sizing syntax from title attribute. @@ -45,25 +46,26 @@ const preprocessMarkdown = (markdown: string): string => { export const markedOptionsFactory = (): MarkedOptions => { const renderer = new MarkedRenderer(); - renderer.checkbox = (checked: boolean) => + renderer.checkbox = ({ checked }) => `${checked ? 'check_box' : 'check_box_outline_blank'}`; - renderer.listitem = (text: string, task: boolean, checked: boolean) => { + renderer.listitem = (item) => { // Handle task list items - if (task) { - const isChecked = checked === true; - return `
  • ${text}
  • `; + if (item.task) { + const isChecked = item.checked === true; + const checkboxIcon = isChecked ? 'check_box' : 'check_box_outline_blank'; + return `
  • ${checkboxIcon}${item.text}
  • `; } - return `
  • ${text}
  • `; + return `
  • ${item.text}
  • `; }; - renderer.link = (href, title, text) => + renderer.link = ({ href, title, text }) => `${text}`; // Custom image renderer with support for sizing syntax // Note: indexeddb:// URLs are pre-resolved to blob: URLs before markdown rendering // The sizing dimensions are passed via the title attribute in "width|height" format - renderer.image = (href, title, text) => { + renderer.image = ({ href, title, text }) => { const { width, height } = parseImageDimensionsFromTitle(title); // Build width and height attributes (not style, as Angular sanitizer strips inline styles) @@ -78,34 +80,16 @@ export const markedOptionsFactory = (): MarkedOptions => { return `${text}`; }; - renderer.paragraph = (text) => { - const split = text.split('\n'); - return split.reduce((acc, p, i) => { - const result = /h(\d)\./.exec(p); - if (result !== null) { - const h = `h${result[1]}`; - return acc + `<${h}>${p.replace(result[0], '')}`; - } - - if (split.length === 1) { - return `

    ` + p + `

    `; - } - - return acc ? (split.length - 1 === i ? acc + p + `

    ` : acc + p) : `

    ` + p; - }, ''); - }; - // parse all RFC3986 URIs const urlPattern = /\b((([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/?#]*))([^?#]*)(\?([^#]*))?(#(.*))?)\b/gi; const rendererTxtOld = renderer.text; - renderer.text = (text) => { - return rendererTxtOld( - text.replace(urlPattern, (url) => { - return `${url}`; - }), - ); + renderer.text = (token) => { + const textWithLinks = token.text.replace(urlPattern, (url) => { + return `${url}`; + }); + return rendererTxtOld({ ...token, text: textWithLinks }); }; const options: MarkedOptions = { @@ -116,12 +100,9 @@ export const markedOptionsFactory = (): MarkedOptions => { }; // Add preprocessing hook to handle image sizing syntax - // We need to provide all required hooks functions as stubs - (options as any).hooks = { - preprocess: preprocessMarkdown, - postprocess: (html: string) => html, - processAllTokens: (tokens: any[]) => tokens, - }; + const hooks = new Hooks(); + hooks.preprocess = preprocessMarkdown; + options.hooks = hooks; return options; }; diff --git a/src/main.ts b/src/main.ts index a1a3137504..5e5f5a1944 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,7 +19,7 @@ import { App as CapacitorApp } from '@capacitor/app'; import { GlobalErrorHandler } from './app/core/error-handler/global-error-handler.class'; import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { MarkdownModule, MARKED_OPTIONS, provideMarkdown } from 'ngx-markdown'; +import { MarkdownModule, MARKED_OPTIONS, SANITIZE, provideMarkdown } from 'ngx-markdown'; import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; import { FeatureStoresModule } from './app/root-store/feature-stores.module'; import { MATERIAL_ANIMATIONS, MatNativeDateModule } from '@angular/material/core'; @@ -90,7 +90,10 @@ bootstrapApplication(AppComponent, { }, // Don't sanitize in Electron - we trust local file:// URLs for clipboard images // In web, use HTML sanitization for security - sanitize: IS_ELECTRON ? SecurityContext.NONE : SecurityContext.HTML, + sanitize: { + provide: SANITIZE, + useValue: IS_ELECTRON ? SecurityContext.NONE : SecurityContext.HTML, + }, }), MaterialCssVarsModule.forRoot(), MatSidenavModule,