From 08263a86fb4955b06668b4f745d44a3f4a184fec Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 8 Mar 2025 15:53:51 +0330 Subject: [PATCH 01/14] 1st commit --- .../MarkdownViewer/BitMarkdownViewer.razor | 10 + .../MarkdownViewer/BitMarkdownViewer.razor.cs | 27 + .../MarkdownViewer/BitMarkdownViewer.scss | 30 + .../MarkdownViewer/BitMarkdownViewer.ts | 41 + .../BitMarkdownViewerJsRuntimeExtensions.cs | 9 + .../Components/MarkdownViewer/marked.d.ts | 714 ++++++++++++++++++ .../wwwroot/marked/marked-15.0.7.js | 6 + 7 files changed, 837 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/marked.d.ts create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/marked/marked-15.0.7.js diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor new file mode 100644 index 0000000000..5370fe1d71 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor @@ -0,0 +1,10 @@ +@namespace Bit.BlazorUI +@inherits BitComponentBase + +
+
\ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs new file mode 100644 index 0000000000..4d55f49318 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -0,0 +1,27 @@ +namespace Bit.BlazorUI; + +/// +/// BitMarkdownViewer is a Blazor wrapper around the famous markedjs library. +/// +/// +public partial class BitMarkdownViewer : BitComponentBase +{ + [Inject] private IJSRuntime _js { get; set; } = default!; + + + + protected override string RootElementClass => "bit-pdr"; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (firstRender is false) return; + + string[] scripts = [ + "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js" + ]; + + await _js.BitPdfReaderInit(scripts); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss new file mode 100644 index 0000000000..a1cd67f816 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss @@ -0,0 +1,30 @@ +@import '../../../Bit.BlazorUI/Styles/functions.scss'; + +.bit-erb { + width: 100%; + height: 100%; + display: flex; + gap: spacing(2); + align-items: center; + box-sizing: border-box; + flex-direction: column; + justify-content: center; + background-color: $clr-bg-pri; +} + +.bit-erb-exp { + width: 90%; + overflow: auto; + white-space: pre; + text-align: start; + margin: spacing(3); +} + +.bit-erb-ftr { + width: 100%; + display: flex; + gap: spacing(2); + align-items: center; + box-sizing: border-box; + justify-content: center; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts new file mode 100644 index 0000000000..dbe4daf89f --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -0,0 +1,41 @@ +namespace BitBlazorUI { + export class MarkdownViewer { + private static _initPromise?: Promise; + + public static async init(scripts: string[]) { + if (MarkdownViewer._initPromise) { + await MarkdownViewer._initPromise; + } + + const allScripts = Array.from(document.scripts).map(s => s.src); + const notAppenedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); + + if (notAppenedScripts.length == 0) return Promise.resolve(); + + const promise = new Promise(async (resolve: any, reject: any) => { + try { + for (let url of notAppenedScripts) await addScript(url); + resolve(); + } catch (e: any) { + reject(e); + } + }); + MarkdownViewer._initPromise = promise; + return promise; + + async function addScript(url: string) { + return new Promise((res, rej) => { + const script = document.createElement('script'); + script.src = url; + script.onload = res; + script.onerror = rej; + document.body.appendChild(script); + }) + } + } + + public static parse(md: string) { + return marked.parse(md, { async: true }); + } + } +} \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs new file mode 100644 index 0000000000..583b8ba970 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -0,0 +1,9 @@ +namespace Bit.BlazorUI; + +internal static class BitMarkdownViewerJsRuntimeExtensions +{ + public static ValueTask BitMarkdownViewerInit(this IJSRuntime jsRuntime, IEnumerable scripts) + { + return jsRuntime.InvokeVoid("BitBlazorUI.MarkdownViewer.init", scripts); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/marked.d.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/marked.d.ts new file mode 100644 index 0000000000..777b5522a0 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/marked.d.ts @@ -0,0 +1,714 @@ +// Generated by dts-bundle-generator v9.5.1 + +declare type MarkedToken = (Tokens.Blockquote | Tokens.Br | Tokens.Code | Tokens.Codespan | Tokens.Def | Tokens.Del | Tokens.Em | Tokens.Escape | Tokens.Heading | Tokens.Hr | Tokens.HTML | Tokens.Image | Tokens.Link | Tokens.List | Tokens.ListItem | Tokens.Paragraph | Tokens.Space | Tokens.Strong | Tokens.Table | Tokens.Tag | Tokens.Text); +declare type Token = (MarkedToken | Tokens.Generic); +declare namespace Tokens { + interface Blockquote { + type: "blockquote"; + raw: string; + text: string; + tokens: Token[]; + } + interface Br { + type: "br"; + raw: string; + } + interface Checkbox { + checked: boolean; + } + interface Code { + type: "code"; + raw: string; + codeBlockStyle?: "indented"; + lang?: string; + text: string; + escaped?: boolean; + } + interface Codespan { + type: "codespan"; + raw: string; + text: string; + } + interface Def { + type: "def"; + raw: string; + tag: string; + href: string; + title: string; + } + interface Del { + type: "del"; + raw: string; + text: string; + tokens: Token[]; + } + interface Em { + type: "em"; + raw: string; + text: string; + tokens: Token[]; + } + interface Escape { + type: "escape"; + raw: string; + text: string; + } + interface Generic { + [index: string]: any; + type: string; + raw: string; + tokens?: Token[]; + } + interface Heading { + type: "heading"; + raw: string; + depth: number; + text: string; + tokens: Token[]; + } + interface Hr { + type: "hr"; + raw: string; + } + interface HTML { + type: "html"; + raw: string; + pre: boolean; + text: string; + block: boolean; + } + interface Image { + type: "image"; + raw: string; + href: string; + title: string | null; + text: string; + } + interface Link { + type: "link"; + raw: string; + href: string; + title?: string | null; + text: string; + tokens: Token[]; + } + interface List { + type: "list"; + raw: string; + ordered: boolean; + start: number | ""; + loose: boolean; + items: ListItem[]; + } + interface ListItem { + type: "list_item"; + raw: string; + task: boolean; + checked?: boolean; + loose: boolean; + text: string; + tokens: Token[]; + } + interface Paragraph { + type: "paragraph"; + raw: string; + pre?: boolean; + text: string; + tokens: Token[]; + } + interface Space { + type: "space"; + raw: string; + } + interface Strong { + type: "strong"; + raw: string; + text: string; + tokens: Token[]; + } + interface Table { + type: "table"; + raw: string; + align: Array<"center" | "left" | "right" | null>; + header: TableCell[]; + rows: TableCell[][]; + } + interface TableCell { + text: string; + tokens: Token[]; + header: boolean; + align: "center" | "left" | "right" | null; + } + interface TableRow { + text: string; + } + interface Tag { + type: "html"; + raw: string; + inLink: boolean; + inRawBlock: boolean; + text: string; + block: boolean; + } + interface Text { + type: "text"; + raw: string; + text: string; + tokens?: Token[]; + escaped?: boolean; + } +} +declare type Links = Record>; +declare type TokensList = Token[] & { + links: Links; +}; +/** + * Renderer + */ +declare class _Renderer { + options: MarkedOptions; + parser: _Parser; + constructor(options?: MarkedOptions); + space(token: Tokens.Space): string; + code({ text, lang, escaped }: Tokens.Code): string; + blockquote({ tokens }: Tokens.Blockquote): string; + html({ text }: Tokens.HTML | Tokens.Tag): string; + heading({ tokens, depth }: Tokens.Heading): string; + hr(token: Tokens.Hr): string; + list(token: Tokens.List): string; + listitem(item: Tokens.ListItem): string; + checkbox({ checked }: Tokens.Checkbox): string; + paragraph({ tokens }: Tokens.Paragraph): string; + table(token: Tokens.Table): string; + tablerow({ text }: Tokens.TableRow): string; + tablecell(token: Tokens.TableCell): string; + /** + * span level renderer + */ + strong({ tokens }: Tokens.Strong): string; + em({ tokens }: Tokens.Em): string; + codespan({ text }: Tokens.Codespan): string; + br(token: Tokens.Br): string; + del({ tokens }: Tokens.Del): string; + link({ href, title, tokens }: Tokens.Link): string; + image({ href, title, text }: Tokens.Image): string; + text(token: Tokens.Text | Tokens.Escape): string; +} +/** + * TextRenderer + * returns only the textual part of the token + */ +declare class _TextRenderer { + strong({ text }: Tokens.Strong): string; + em({ text }: Tokens.Em): string; + codespan({ text }: Tokens.Codespan): string; + del({ text }: Tokens.Del): string; + html({ text }: Tokens.HTML | Tokens.Tag): string; + text({ text }: Tokens.Text | Tokens.Escape | Tokens.Tag): string; + link({ text }: Tokens.Link): string; + image({ text }: Tokens.Image): string; + br(): string; +} +/** + * Parsing & Compiling + */ +declare class _Parser { + options: MarkedOptions; + renderer: _Renderer; + textRenderer: _TextRenderer; + constructor(options?: MarkedOptions); + /** + * Static Parse Method + */ + static parse(tokens: Token[], options?: MarkedOptions): string; + /** + * Static Parse Inline Method + */ + static parseInline(tokens: Token[], options?: MarkedOptions): string; + /** + * Parse Loop + */ + parse(tokens: Token[], top?: boolean): string; + /** + * Parse Inline Tokens + */ + parseInline(tokens: Token[], renderer?: _Renderer | _TextRenderer): string; +} +declare const other: { + codeRemoveIndent: RegExp; + outputLinkReplace: RegExp; + indentCodeCompensation: RegExp; + beginningSpace: RegExp; + endingHash: RegExp; + startingSpaceChar: RegExp; + endingSpaceChar: RegExp; + nonSpaceChar: RegExp; + newLineCharGlobal: RegExp; + tabCharGlobal: RegExp; + multipleSpaceGlobal: RegExp; + blankLine: RegExp; + doubleBlankLine: RegExp; + blockquoteStart: RegExp; + blockquoteSetextReplace: RegExp; + blockquoteSetextReplace2: RegExp; + listReplaceTabs: RegExp; + listReplaceNesting: RegExp; + listIsTask: RegExp; + listReplaceTask: RegExp; + anyLine: RegExp; + hrefBrackets: RegExp; + tableDelimiter: RegExp; + tableAlignChars: RegExp; + tableRowBlankLine: RegExp; + tableAlignRight: RegExp; + tableAlignCenter: RegExp; + tableAlignLeft: RegExp; + startATag: RegExp; + endATag: RegExp; + startPreScriptTag: RegExp; + endPreScriptTag: RegExp; + startAngleBracket: RegExp; + endAngleBracket: RegExp; + pedanticHrefTitle: RegExp; + unicodeAlphaNumeric: RegExp; + escapeTest: RegExp; + escapeReplace: RegExp; + escapeTestNoEncode: RegExp; + escapeReplaceNoEncode: RegExp; + unescapeTest: RegExp; + caret: RegExp; + percentDecode: RegExp; + findPipe: RegExp; + splitPipe: RegExp; + slashPipe: RegExp; + carriageReturn: RegExp; + spaceLine: RegExp; + notSpaceStart: RegExp; + endingNewline: RegExp; + listItemRegex: (bull: string) => RegExp; + nextBulletRegex: (indent: number) => RegExp; + hrRegex: (indent: number) => RegExp; + fencesBeginRegex: (indent: number) => RegExp; + headingBeginRegex: (indent: number) => RegExp; + htmlBeginRegex: (indent: number) => RegExp; +}; +declare const blockNormal: { + blockquote: RegExp; + code: RegExp; + def: RegExp; + fences: RegExp; + heading: RegExp; + hr: RegExp; + html: RegExp; + lheading: RegExp; + list: RegExp; + newline: RegExp; + paragraph: RegExp; + table: RegExp; + text: RegExp; +}; +declare type BlockKeys = keyof typeof blockNormal; +declare const inlineNormal: { + _backpedal: RegExp; + anyPunctuation: RegExp; + autolink: RegExp; + blockSkip: RegExp; + br: RegExp; + code: RegExp; + del: RegExp; + emStrongLDelim: RegExp; + emStrongRDelimAst: RegExp; + emStrongRDelimUnd: RegExp; + escape: RegExp; + link: RegExp; + nolink: RegExp; + punctuation: RegExp; + reflink: RegExp; + reflinkSearch: RegExp; + tag: RegExp; + text: RegExp; + url: RegExp; +}; +declare type InlineKeys = keyof typeof inlineNormal; +declare interface Rules { + other: typeof other; + block: Record; + inline: Record; +} +/** + * Tokenizer + */ +declare class _Tokenizer { + options: MarkedOptions; + rules: Rules; + lexer: _Lexer; + constructor(options?: MarkedOptions); + space(src: string): Tokens.Space | undefined; + code(src: string): Tokens.Code | undefined; + fences(src: string): Tokens.Code | undefined; + heading(src: string): Tokens.Heading | undefined; + hr(src: string): Tokens.Hr | undefined; + blockquote(src: string): Tokens.Blockquote | undefined; + list(src: string): Tokens.List | undefined; + html(src: string): Tokens.HTML | undefined; + def(src: string): Tokens.Def | undefined; + table(src: string): Tokens.Table | undefined; + lheading(src: string): Tokens.Heading | undefined; + paragraph(src: string): Tokens.Paragraph | undefined; + text(src: string): Tokens.Text | undefined; + escape(src: string): Tokens.Escape | undefined; + tag(src: string): Tokens.Tag | undefined; + link(src: string): Tokens.Link | Tokens.Image | undefined; + reflink(src: string, links: Links): Tokens.Link | Tokens.Image | Tokens.Text | undefined; + emStrong(src: string, maskedSrc: string, prevChar?: string): Tokens.Em | Tokens.Strong | undefined; + codespan(src: string): Tokens.Codespan | undefined; + br(src: string): Tokens.Br | undefined; + del(src: string): Tokens.Del | undefined; + autolink(src: string): Tokens.Link | undefined; + url(src: string): Tokens.Link | undefined; + inlineText(src: string): Tokens.Text | undefined; +} +declare class _Hooks { + options: MarkedOptions; + block?: boolean; + constructor(options?: MarkedOptions); + static passThroughHooks: Set; + /** + * Process markdown before marked + */ + preprocess(markdown: string): string; + /** + * Process HTML after marked is finished + */ + postprocess(html: string): string; + /** + * Process all tokens before walk tokens + */ + processAllTokens(tokens: Token[] | TokensList): Token[] | TokensList; + /** + * Provide function to tokenize markdown + */ + provideLexer(): typeof _Lexer.lexInline; + /** + * Provide function to parse tokens + */ + provideParser(): typeof _Parser.parse; +} +declare interface TokenizerThis { + lexer: _Lexer; +} +declare type TokenizerExtensionFunction = (this: TokenizerThis, src: string, tokens: Token[] | TokensList) => Tokens.Generic | undefined; +declare type TokenizerStartFunction = (this: TokenizerThis, src: string) => number | void; +declare interface TokenizerExtension { + name: string; + level: "block" | "inline"; + start?: TokenizerStartFunction; + tokenizer: TokenizerExtensionFunction; + childTokens?: string[]; +} +declare interface RendererThis { + parser: _Parser; +} +declare type RendererExtensionFunction = (this: RendererThis, token: Tokens.Generic) => string | false | undefined; +declare interface RendererExtension { + name: string; + renderer: RendererExtensionFunction; +} +declare type TokenizerAndRendererExtension = TokenizerExtension | RendererExtension | (TokenizerExtension & RendererExtension); +declare type HooksApi = Omit<_Hooks, "constructor" | "options" | "block">; +declare type HooksObject = { + [K in keyof HooksApi]?: (this: _Hooks, ...args: Parameters) => ReturnType | Promise>; +}; +declare type RendererApi = Omit<_Renderer, "constructor" | "options" | "parser">; +declare type RendererObject = { + [K in keyof RendererApi]?: (this: _Renderer, ...args: Parameters) => ReturnType | false; +}; +declare type TokenizerApi = Omit<_Tokenizer, "constructor" | "options" | "rules" | "lexer">; +declare type TokenizerObject = { + [K in keyof TokenizerApi]?: (this: _Tokenizer, ...args: Parameters) => ReturnType | false; +}; +declare interface MarkedExtension { + /** + * True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string. + */ + async?: boolean; + /** + * Enable GFM line breaks. This option requires the gfm option to be true. + */ + breaks?: boolean; + /** + * Add tokenizers and renderers to marked + */ + extensions?: TokenizerAndRendererExtension[] | null; + /** + * Enable GitHub flavored markdown. + */ + gfm?: boolean; + /** + * Hooks are methods that hook into some part of marked. + * preprocess is called to process markdown before sending it to marked. + * processAllTokens is called with the TokensList before walkTokens. + * postprocess is called to process html after marked has finished parsing. + * provideLexer is called to provide a function to tokenize markdown. + * provideParser is called to provide a function to parse tokens. + */ + hooks?: HooksObject | null; + /** + * Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior. + */ + pedantic?: boolean; + /** + * Type: object Default: new Renderer() + * + * An object containing functions to render tokens to HTML. + */ + renderer?: RendererObject | null; + /** + * Shows an HTML error message when rendering fails. + */ + silent?: boolean; + /** + * The tokenizer defines how to turn markdown text into tokens. + */ + tokenizer?: TokenizerObject | null; + /** + * The walkTokens function gets called with every token. + * Child tokens are called before moving on to sibling tokens. + * Each token is passed by reference so updates are persisted when passed to the parser. + * The return value of the function is ignored. + */ + walkTokens?: ((token: Token) => void | Promise) | null; +} +declare interface MarkedOptions extends Omit { + /** + * Hooks are methods that hook into some part of marked. + */ + hooks?: _Hooks | null; + /** + * Type: object Default: new Renderer() + * + * An object containing functions to render tokens to HTML. + */ + renderer?: _Renderer | null; + /** + * The tokenizer defines how to turn markdown text into tokens. + */ + tokenizer?: _Tokenizer | null; + /** + * Custom extensions + */ + extensions?: null | { + renderers: { + [name: string]: RendererExtensionFunction; + }; + childTokens: { + [name: string]: string[]; + }; + inline?: TokenizerExtensionFunction[]; + block?: TokenizerExtensionFunction[]; + startInline?: TokenizerStartFunction[]; + startBlock?: TokenizerStartFunction[]; + }; + /** + * walkTokens function returns array of values for Promise.all + */ + walkTokens?: null | ((token: Token) => void | Promise | (void | Promise)[]); +} +/** + * Block Lexer + */ +declare class _Lexer { + tokens: TokensList; + options: MarkedOptions; + state: { + inLink: boolean; + inRawBlock: boolean; + top: boolean; + }; + private tokenizer; + private inlineQueue; + constructor(options?: MarkedOptions); + /** + * Expose Rules + */ + static get rules(): { + block: { + normal: { + blockquote: RegExp; + code: RegExp; + def: RegExp; + fences: RegExp; + heading: RegExp; + hr: RegExp; + html: RegExp; + lheading: RegExp; + list: RegExp; + newline: RegExp; + paragraph: RegExp; + table: RegExp; + text: RegExp; + }; + gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "def" | "heading" | "list" | "paragraph" | "fences" | "lheading" | "newline", RegExp>; + pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "def" | "heading" | "list" | "paragraph" | "fences" | "lheading" | "newline", RegExp>; + }; + inline: { + normal: { + _backpedal: RegExp; + anyPunctuation: RegExp; + autolink: RegExp; + blockSkip: RegExp; + br: RegExp; + code: RegExp; + del: RegExp; + emStrongLDelim: RegExp; + emStrongRDelimAst: RegExp; + emStrongRDelimUnd: RegExp; + escape: RegExp; + link: RegExp; + nolink: RegExp; + punctuation: RegExp; + reflink: RegExp; + reflinkSearch: RegExp; + tag: RegExp; + text: RegExp; + url: RegExp; + }; + gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>; + breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>; + pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>; + }; + }; + /** + * Static Lex Method + */ + static lex(src: string, options?: MarkedOptions): TokensList; + /** + * Static Lex Inline Method + */ + static lexInline(src: string, options?: MarkedOptions): Token[]; + /** + * Preprocessing + */ + lex(src: string): TokensList; + /** + * Lexing + */ + blockTokens(src: string, tokens?: Token[], lastParagraphClipped?: boolean): Token[]; + blockTokens(src: string, tokens?: TokensList, lastParagraphClipped?: boolean): TokensList; + inline(src: string, tokens?: Token[]): Token[]; + /** + * Lexing/Compiling + */ + inlineTokens(src: string, tokens?: Token[]): Token[]; +} +/** + * Gets the original marked default options. + */ +declare function _getDefaults(): MarkedOptions; +declare let _defaults: MarkedOptions; +declare type MaybePromise = void | Promise; +declare class Marked { + defaults: MarkedOptions; + options: (opt: MarkedOptions) => this; + parse: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + parseInline: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + Parser: typeof _Parser; + Renderer: typeof _Renderer; + TextRenderer: typeof _TextRenderer; + Lexer: typeof _Lexer; + Tokenizer: typeof _Tokenizer; + Hooks: typeof _Hooks; + constructor(...args: MarkedExtension[]); + /** + * Run callback for every token + */ + walkTokens(tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]): MaybePromise[]; + use(...args: MarkedExtension[]): this; + setOptions(opt: MarkedOptions): this; + lexer(src: string, options?: MarkedOptions): TokensList; + parser(tokens: Token[], options?: MarkedOptions): string; + private parseMarkdown; + private onError; +} +/** + * Compiles markdown to HTML asynchronously. + * + * @param src String of markdown source to be compiled + * @param options Hash of options, having async: true + * @return Promise of string of compiled HTML + */ +declare function marked(src: string, options: MarkedOptions & { + async: true; +}): Promise; +/** + * Compiles markdown to HTML. + * + * @param src String of markdown source to be compiled + * @param options Optional hash of options + * @return String of compiled HTML. Will be a Promise of string if async is set to true by any extensions. + */ +declare function marked(src: string, options: MarkedOptions & { + async: false; +}): string; +declare function marked(src: string, options: MarkedOptions & { + async: true; +}): Promise; +declare function marked(src: string, options?: MarkedOptions | null): string | Promise; +declare namespace marked { + var options: (options: MarkedOptions) => typeof marked; + var setOptions: (options: MarkedOptions) => typeof marked; + var getDefaults: typeof _getDefaults; + var defaults: MarkedOptions; + var use: (...args: MarkedExtension[]) => typeof marked; + var walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[]; + var parseInline: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + var Parser: typeof _Parser; + var parser: typeof _Parser.parse; + var Renderer: typeof _Renderer; + var TextRenderer: typeof _TextRenderer; + var Lexer: typeof _Lexer; + var lexer: typeof _Lexer.lex; + var Tokenizer: typeof _Tokenizer; + var Hooks: typeof _Hooks; + var parse: typeof marked; +} +declare const options: (options: MarkedOptions) => typeof marked; +declare const setOptions: (options: MarkedOptions) => typeof marked; +declare const use: (...args: MarkedExtension[]) => typeof marked; +declare const walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[]; +declare const parseInline: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; +}; +declare const parse: typeof marked; +declare const parser: typeof _Parser.parse; +declare const lexer: typeof _Lexer.lex; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/marked/marked-15.0.7.js b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/marked/marked-15.0.7.js new file mode 100644 index 0000000000..4052d1b49e --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/wwwroot/marked/marked-15.0.7.js @@ -0,0 +1,6 @@ +/** + * marked v15.0.7 - a markdown parser + * Copyright (c) 2011-2025, Christopher Jeffrey. (MIT Licensed) + * https://github.com/markedjs/marked + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s={exec:()=>null};function r(e,t=""){let n="string"==typeof e?e:e.source;const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(i.caret,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}const i={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[\t ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},l=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,o=/(?:[*+-]|\d{1,9}[.)])/,a=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,c=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),h=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),p=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,u=/(?!\s*\])(?:\\.|[^\[\]\\])+/,g=r(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",u).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),k=r(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,o).getRegex(),d="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",f=/|$))/,x=r("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",f).replace("tag",d).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),b=r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),w={blockquote:r(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",b).getRegex(),code:/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,def:g,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:l,html:x,lheading:c,list:k,newline:/^(?:[ \t]*(?:\n|$))+/,paragraph:b,table:s,text:/^[^\n]+/},m=r("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),y={...w,lheading:h,table:m,paragraph:r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",m).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex()},$={...w,html:r("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",f).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:s,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:r(p).replace("hr",l).replace("heading"," *#{1,6} *[^\n]").replace("lheading",c).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},R=/^( {2,}|\\)\n(?!\s*$)/,S=/[\p{P}\p{S}]/u,T=/[\s\p{P}\p{S}]/u,z=/[^\s\p{P}\p{S}]/u,A=r(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,T).getRegex(),_=/(?!~)[\p{P}\p{S}]/u,P=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,I=r(P,"u").replace(/punct/g,S).getRegex(),L=r(P,"u").replace(/punct/g,_).getRegex(),B="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",C=r(B,"gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),q=r(B,"gu").replace(/notPunctSpace/g,/(?:[^\s\p{P}\p{S}]|~)/u).replace(/punctSpace/g,/(?!~)[\s\p{P}\p{S}]/u).replace(/punct/g,_).getRegex(),E=r("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),Z=r(/\\(punct)/,"gu").replace(/punct/g,S).getRegex(),v=r(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),D=r(f).replace("(?:--\x3e|$)","--\x3e").getRegex(),M=r("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",D).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),O=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Q=r(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",O).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),j=r(/^!?\[(label)\]\[(ref)\]/).replace("label",O).replace("ref",u).getRegex(),N=r(/^!?\[(ref)\](?:\[\])?/).replace("ref",u).getRegex(),G={_backpedal:s,anyPunctuation:Z,autolink:v,blockSkip:/\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g,br:R,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:s,emStrongLDelim:I,emStrongRDelimAst:C,emStrongRDelimUnd:E,escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,link:Q,nolink:N,punctuation:A,reflink:j,reflinkSearch:r("reflink|nolink(?!\\()","g").replace("reflink",j).replace("nolink",N).getRegex(),tag:M,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},V=e=>K[e];function W(e,t){if(t){if(i.escapeTest.test(e))return e.replace(i.escapeReplace,V)}else if(i.escapeTestNoEncode.test(e))return e.replace(i.escapeReplaceNoEncode,V);return e}function Y(e){try{e=encodeURI(e).replace(i.percentDecode,"%")}catch{return null}return e}function ee(e,t){const n=e.replace(i.findPipe,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(i.splitPipe);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:te(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t,n){const s=e.match(n.other.indentCodeCompensation);if(null===s)return t;const r=s[1];return t.split("\n").map((e=>{const t=e.match(n.other.beginningSpace);if(null===t)return e;const[s]=t;return s.length>=r.length?e.slice(r.length):e})).join("\n")}(e,t[3]||"",this.rules);return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){const t=te(e,"#");this.options.pedantic?e=t.trim():t&&!this.rules.other.endingSpaceChar.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:te(t[0],"\n")}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=te(t[0],"\n").split("\n"),n="",s="";const r=[];for(;e.length>0;){let t=!1;const i=[];let l;for(l=0;l1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=this.rules.other.listItemRegex(n);let l=!1;for(;e;){let n=!1,s="",o="";if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;s=t[0],e=e.substring(s.length);let a=t[2].split("\n",1)[0].replace(this.rules.other.listReplaceTabs,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=!a.trim(),p=0;if(this.options.pedantic?(p=2,o=a.trimStart()):h?p=t[1].length+1:(p=t[2].search(this.rules.other.nonSpaceChar),p=p>4?1:p,o=a.slice(p),p+=t[1].length),h&&this.rules.other.blankLine.test(c)&&(s+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=this.rules.other.nextBulletRegex(p),n=this.rules.other.hrRegex(p),r=this.rules.other.fencesBeginRegex(p),i=this.rules.other.headingBeginRegex(p),l=this.rules.other.htmlBeginRegex(p);for(;e;){const u=e.split("\n",1)[0];let g;if(c=u,this.options.pedantic?(c=c.replace(this.rules.other.listReplaceNesting," "),g=c):g=c.replace(this.rules.other.tabCharGlobal," "),r.test(c))break;if(i.test(c))break;if(l.test(c))break;if(t.test(c))break;if(n.test(c))break;if(g.search(this.rules.other.nonSpaceChar)>=p||!c.trim())o+="\n"+g.slice(p);else{if(h)break;if(a.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4)break;if(r.test(a))break;if(i.test(a))break;if(n.test(a))break;o+="\n"+c}h||c.trim()||(h=!0),s+=u+"\n",e=e.substring(u.length+1),a=g.slice(p)}}r.loose||(l?r.loose=!0:this.rules.other.doubleBlankLine.test(s)&&(l=!0));let u,g=null;this.options.gfm&&(g=this.rules.other.listIsTask.exec(o),g&&(u="[ ] "!==g[0],o=o.replace(this.rules.other.listReplaceTask,""))),r.items.push({type:"list_item",raw:s,task:!!g,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=s}const o=r.items.at(-1);if(!o)return;o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e"space"===e.type)),n=t.length>0&&t.some((e=>this.rules.other.anyLine.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e({text:e,tokens:this.lexer.inline(e),header:!1,align:i.align[t]}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(e)){if(!this.rules.other.endAngleBracket.test(e))return;const t=te(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=this.rules.other.pedanticHrefTitle.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),this.rules.other.startAngleBracket.test(n)&&(n=this.options.pedantic&&!this.rules.other.endAngleBracket.test(e)?n.slice(1):n.slice(1,-1)),ne(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return ne(n,e,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal," ");const n=this.rules.other.nonSpaceChar.test(e),s=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&s&&(e=e.substring(1,e.length-1)),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=t[1],n="mailto:"+e):(e=t[1],n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=t[0],n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=t[0],n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){const e=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:e}}}}class re{tokens;options;state;tokenizer;inlineQueue;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||e.defaults,this.options.tokenizer=this.options.tokenizer||new se,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={other:i,block:U.normal,inline:J.normal};this.options.pedantic?(n.block=U.pedantic,n.inline=J.pedantic):this.options.gfm&&(n.block=U.gfm,this.options.breaks?n.inline=J.breaks:n.inline=J.gfm),this.tokenizer.rules=n}static get rules(){return{block:U,inline:J}}static lex(e,t){return new re(t).lex(e)}static lexInline(e,t){return new re(t).inlineTokens(e)}lex(e){e=e.replace(i.carriageReturn,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);const n=t.at(-1);1===s.raw.length&&void 0!==n?n.raw+="\n":t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.at(-1).src=n.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title});continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let r=e;if(this.options.extensions?.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(s=this.tokenizer.paragraph(r))){const i=t.at(-1);n&&"paragraph"===i?.type?(i.raw+="\n"+s.raw,i.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=i.text):t.push(s),n=r.length!==e.length,e=e.substring(s.raw.length)}else if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,s=null;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(s=this.tokenizer.rules.inline.reflinkSearch.exec(n));)e.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(s=this.tokenizer.rules.inline.blockSkip.exec(n));)n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(s=this.tokenizer.rules.inline.anyPunctuation.exec(n));)n=n.slice(0,s.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let r=!1,i="";for(;e;){let s;if(r||(i=""),r=!1,this.options.extensions?.inline?.some((n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.escape(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.tag(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.link(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===s.type&&"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s);continue}if(s=this.tokenizer.emStrong(e,n,i)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.codespan(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.br(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.del(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.autolink(e)){e=e.substring(s.raw.length),t.push(s);continue}if(!this.state.inLink&&(s=this.tokenizer.url(e))){e=e.substring(s.raw.length),t.push(s);continue}let l=e;if(this.options.extensions?.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(l=e.substring(0,t+1))}if(s=this.tokenizer.inlineText(l)){e=e.substring(s.raw.length),"_"!==s.raw.slice(-1)&&(i=s.raw.slice(-1)),r=!0;const n=t.at(-1);"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return t}}class ie{options;parser;constructor(t){this.options=t||e.defaults}space(e){return""}code({text:e,lang:t,escaped:n}){const s=(t||"").match(i.notSpaceStart)?.[0],r=e.replace(i.endingNewline,"")+"\n";return s?'
'+(n?r:W(r,!0))+"
\n":"
"+(n?r:W(r,!0))+"
\n"}blockquote({tokens:e}){return`
\n${this.parser.parse(e)}
\n`}html({text:e}){return e}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return"
\n"}list(e){const t=e.ordered,n=e.start;let s="";for(let t=0;t\n"+s+"\n"}listitem(e){let t="";if(e.task){const n=this.checkbox({checked:!!e.checked});e.loose?"paragraph"===e.tokens[0]?.type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+W(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`
  • ${t}
  • \n`}checkbox({checked:e}){return"'}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t="",n="";for(let t=0;t${s}`),"\n\n"+t+"\n"+s+"
    \n"}tablerow({text:e}){return`\n${e}\n`}tablecell(e){const t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${W(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){const s=this.parser.parseInline(n),r=Y(e);if(null===r)return s;let i='
    ",i}image({href:e,title:t,text:n}){const s=Y(e);if(null===s)return W(n);let r=`${n}{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new ie(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new se(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new ae;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;const s=n,r=e.hooks[s],i=t[s];ae.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return re.lex(e,t??this.defaults)}parser(e,t){return oe.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{const s={...n},r={...this.defaults,...s},i=this.onError(!!r.silent,!!r.async);if(!0===this.defaults.async&&!1===s.async)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(null==t)return i(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));r.hooks&&(r.hooks.options=r,r.hooks.block=e);const l=r.hooks?r.hooks.provideLexer():e?re.lex:re.lexInline,o=r.hooks?r.hooks.provideParser():e?oe.parse:oe.parseInline;if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(t):t).then((e=>l(e,r))).then((e=>r.hooks?r.hooks.processAllTokens(e):e)).then((e=>r.walkTokens?Promise.all(this.walkTokens(e,r.walkTokens)).then((()=>e)):e)).then((e=>o(e,r))).then((e=>r.hooks?r.hooks.postprocess(e):e)).catch(i);try{r.hooks&&(t=r.hooks.preprocess(t));let e=l(t,r);r.hooks&&(e=r.hooks.processAllTokens(e)),r.walkTokens&&this.walkTokens(e,r.walkTokens);let n=o(e,r);return r.hooks&&(n=r.hooks.postprocess(n)),n}catch(e){return i(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="

    An error occurred:

    "+W(n.message+"",!0)+"
    ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const he=new ce;function pe(e,t){return he.parse(e,t)}pe.options=pe.setOptions=function(e){return he.setOptions(e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.getDefaults=t,pe.defaults=e.defaults,pe.use=function(...e){return he.use(...e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.walkTokens=function(e,t){return he.walkTokens(e,t)},pe.parseInline=he.parseInline,pe.Parser=oe,pe.parser=oe.parse,pe.Renderer=ie,pe.TextRenderer=le,pe.Lexer=re,pe.lexer=re.lex,pe.Tokenizer=se,pe.Hooks=ae,pe.parse=pe;const ue=pe.options,ge=pe.setOptions,ke=pe.use,de=pe.walkTokens,fe=pe.parseInline,xe=pe,be=oe.parse,we=re.lex;e.Hooks=ae,e.Lexer=re,e.Marked=ce,e.Parser=oe,e.Renderer=ie,e.TextRenderer=le,e.Tokenizer=se,e.getDefaults=t,e.lexer=we,e.marked=pe,e.options=ue,e.parse=xe,e.parseInline=fe,e.parser=be,e.setOptions=ge,e.use=ke,e.walkTokens=de})); From 8863bfd0a6d2ed77f574745a23ef5dc372e0f614 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 8 Mar 2025 20:24:46 +0330 Subject: [PATCH 02/14] finish implementations --- .../MarkdownViewer/BitMarkdownViewer.razor | 1 + .../MarkdownViewer/BitMarkdownViewer.razor.cs | 41 +++- .../MarkdownViewer/BitMarkdownViewer.scss | 31 +-- .../MarkdownViewer/BitMarkdownViewer.ts | 5 +- .../BitMarkdownViewerJsRuntimeExtensions.cs | 5 + .../Styles/bit.blazorui.extras.scss | 4 +- ...{components.scss => extra-components.scss} | 1 + .../{general.scss => extra-general.scss} | 0 .../BitMarkdownViewerDemo.razor | 22 +++ .../BitMarkdownViewerDemo.razor.cs | 181 ++++++++++++++++++ .../BitMarkdownViewerDemo.razor.scss | 7 + .../Pages/Home/ComponentsSection.razor | 3 + .../Shared/MainLayout.razor.NavItems.cs | 1 + .../compilerconfig.json | 6 + 14 files changed, 272 insertions(+), 36 deletions(-) rename src/BlazorUI/Bit.BlazorUI.Extras/Styles/{components.scss => extra-components.scss} (89%) rename src/BlazorUI/Bit.BlazorUI.Extras/Styles/{general.scss => extra-general.scss} (100%) create mode 100644 src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor create mode 100644 src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs create mode 100644 src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.scss diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor index 5370fe1d71..7ce97e2877 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor @@ -7,4 +7,5 @@ style="@StyleBuilder.Value" class="@ClassBuilder.Value" dir="@Dir?.ToString().ToLower()"> + @((MarkupString)(_html ?? "")) \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 4d55f49318..785e8e95ed 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -6,22 +6,51 @@ /// public partial class BitMarkdownViewer : BitComponentBase { + private string? _html; + + + [Inject] private IJSRuntime _js { get; set; } = default!; - protected override string RootElementClass => "bit-pdr"; + /// + /// The Markdown string value to render as an html element. + /// + [Parameter, CallOnSet(nameof(OnMarkdownSet))] + public string? Markdown { get; set; } + + + + protected override string RootElementClass => "bit-mdv"; protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); - if (firstRender is false) return; + if (firstRender) + { + await _js.BitPdfReaderInit(["_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"]); + + await ParseAndRender(); + } + } + + + + private async Task OnMarkdownSet() + { + if (IsRendered is false) return; + + await ParseAndRender(); + } + + private async Task ParseAndRender() + { + if (Markdown.HasNoValue()) return; - string[] scripts = [ - "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js" - ]; + _html = await _js.BitMarkdownViewerParse(Markdown!); - await _js.BitPdfReaderInit(scripts); + StateHasChanged(); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss index a1cd67f816..40a3af3a40 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss @@ -1,30 +1,9 @@ @import '../../../Bit.BlazorUI/Styles/functions.scss'; -.bit-erb { - width: 100%; - height: 100%; - display: flex; - gap: spacing(2); - align-items: center; - box-sizing: border-box; - flex-direction: column; - justify-content: center; - background-color: $clr-bg-pri; -} - -.bit-erb-exp { - width: 90%; - overflow: auto; - white-space: pre; - text-align: start; - margin: spacing(3); -} +.bit-mdv { + all: revert; -.bit-erb-ftr { - width: 100%; - display: flex; - gap: spacing(2); - align-items: center; - box-sizing: border-box; - justify-content: center; + * { + all: revert; + } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts index dbe4daf89f..ef44bb2f4d 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -34,8 +34,9 @@ namespace BitBlazorUI { } } - public static parse(md: string) { - return marked.parse(md, { async: true }); + public static async parse(md: string) { + const html = await marked.parse(md, { async: true }); + return html; } } } \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs index 583b8ba970..98de2bfc1e 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -6,4 +6,9 @@ public static ValueTask BitMarkdownViewerInit(this IJSRuntime jsRuntime, IEnumer { return jsRuntime.InvokeVoid("BitBlazorUI.MarkdownViewer.init", scripts); } + + public static ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) + { + return jsRuntime.Invoke("BitBlazorUI.MarkdownViewer.parse", markdown); + } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss index eb02b8b603..2e953e1248 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/bit.blazorui.extras.scss @@ -1,3 +1,3 @@ -@import "general.scss"; -@import "components.scss"; +@import "extra-general.scss"; +@import "extra-components.scss"; @import "fabric.mdl2.bit.blazoui.extras.scss"; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss similarity index 89% rename from src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss rename to src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss index 2b8912a623..a58e1aa4f7 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/components.scss +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss @@ -3,6 +3,7 @@ @import "../Components/DataGrid/Pagination/BitDataGridPaginator.scss"; @import "../Components/ErrorBoundary/BitErrorBoundary.scss"; @import "../Components/InfiniteScrolling/BitInfiniteScrolling.scss"; +@import "../Components/MarkdownViewer/BitMarkdownViewer.scss"; @import "../Components/MessageBox/BitMessageBox.scss"; @import "../Components/NavPanel/BitNavPanel.scss"; @import "../Components/PdfReader/BitPdfReader.scss"; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/general.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-general.scss similarity index 100% rename from src/BlazorUI/Bit.BlazorUI.Extras/Styles/general.scss rename to src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-general.scss diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor new file mode 100644 index 0000000000..c75e7dc389 --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor @@ -0,0 +1,22 @@ +@page "/components/markdownviewer" + + + + + + + + + + + +
    + +
    +
    +
    \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs new file mode 100644 index 0000000000..1b7ae4782f --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs @@ -0,0 +1,181 @@ +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Extras.MarkdownViewer; + +public partial class BitMarkdownViewerDemo +{ + private readonly List componentParameters = + [ + new() + { + Name = "Markdown", + Type = "string?", + DefaultValue = "null", + Description = "The Markdown string value to render as an html element.", + }, + ]; + + + private string advancedMarkdown = @"![Header](https://user-images.githubusercontent.com/6169846/251658486-b16e1db8-5481-46c4-9fc1-c9b279a4364a.png) + +
    + +![License](https://img.shields.io/github/license/bitfoundation/bitplatform.svg) +![CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.yml/badge.svg) +![NuGet version](https://img.shields.io/nuget/v/bit.blazorui.svg?logo=nuget) +[![Nuget downloads](https://img.shields.io/badge/packages_download-6.0M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform ""Average time to resolve an issue"") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform ""Percentage of issues still open"") + +
    + +# 🧾 Introduction + +At **bitplatform**, we've curated a comprehensive toolkit to empower you in crafting the finest projects using Blazor. Diverging from others merely offering UI Toolkits, ***bit BlazorUI components*** distinguishes itself with over 56 components, with a compact size of under 400 KB. These components boast both Dark and Light Themes, delivering unparalleled High Performance 🚀 + +Yet, bitplatform doesn't stop there. Our platform introduces exclusive tools that elevate your development experience: + +***Bswup***: This unique tool harnesses the power of Progressive Web Apps (PWA) within the innovative new structure of dotnet 8. By amalgamating pre-rendering techniques reminiscent of renowned platforms like GitHub, Reddit, and Facebook, Bswup ensures an exceptional user experience 😍 + +***Butil***: Embracing Blazor because of your love for C#? Butil enables you to stay true to that sentiment by providing essential Browser APIs in C#, eliminating the need to revert to JavaScript for any functionality 👌 + +***Besql***: Dreaming of an offline web application capable of saving data and syncing later? Enter Besql, your solution to incorporating ef core & sqlite in your browser. It's a crucial aid for achieving this objective seamlessly 🕺 + +***Bit Boilerplate Project Template***: If the aforementioned features have piqued your interest, dive into the Bit Boilerplate project template. Experience everything mentioned above along with additional features such as ASP.NET Core Identity integration, multilingualism, and other cool features that empowers you to develop unified Web, Android, iOS, Windows, and macOS apps from a single codebase, while providing seamless integration with native platform features and third-party Java, Kotlin, Swift, Objective-C, and JavaScript libraries 💯 + +For more details, visit us at [bitplatform.dev](https://bitplatform.dev/). + +
    + +**Note**: This project is tested with [BrowserStack](https://www.browserstack.com/). + +
    + +# 🎁 OSS Showcases + +The following apps are our open-source projects powered by the bit platform showcasing the different capabilities of our toolchain: + +| |    Web    |    iOS    | Android | Windows | macOS | +|:-:|:--:|:--:|:--:|:--:|:--:| +| bitplatform | [![PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://bitplatform.dev)| *N/A* | *N/A* | *N/A* | *N/A* | +| Sales | [![PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://sales.bitplatform.dev) | *Soon!* | *Soon!* | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-sales.bitplatform.dev/SalesModule.Client.Windows-win-Setup.exe) | *Soon!* | +| bit BlazorUI | [![Prerendered PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://blazorui.bitplatform.dev) | [![iOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381842-e72976ce-fd20-431d-a677-ca1ed625b83b.png)](https://apps.apple.com/us/app/bit-blazor-ui/id6450401404) | [![Android app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381958-24931682-87f6-44fc-a1c7-eecf46387005.png)](https://play.google.com/store/apps/details?id=com.bitplatform.BlazorUI.Demo) | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-components.bitplatform.dev/Bit.BlazorUI.Demo.Client.Windows-win-Setup.exe) | [![macOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382211-0d58f9ba-1a1f-4481-a0ca-b23a393cca9f.png)](https://apps.apple.com/nl/app/bit-blazor-ui/id6450401404) +| AdminPanel | [![Prerendered PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://adminpanel.bitplatform.dev) | [![iOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381842-e72976ce-fd20-431d-a677-ca1ed625b83b.png)](https://apps.apple.com/us/app/bit-adminpanel/id6450611349) | [![Android app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381958-24931682-87f6-44fc-a1c7-eecf46387005.png)](https://play.google.com/store/apps/details?id=com.bitplatform.AdminPanel.Template) | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-admin.bitplatform.dev/AdminPanel.Client.Windows-win-Setup.exe) | [![macOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382211-0d58f9ba-1a1f-4481-a0ca-b23a393cca9f.png)](https://apps.apple.com/nl/app/bit-adminpanel/id6450611349) | +| Todo | [![Prerendered PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://todo.bitplatform.dev) | [![iOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381842-e72976ce-fd20-431d-a677-ca1ed625b83b.png)](https://apps.apple.com/us/app/bit-todotemplate/id6450611072) | [![Android app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381958-24931682-87f6-44fc-a1c7-eecf46387005.png)](https://play.google.com/store/apps/details?id=com.bitplatform.Todo.Template) | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-todo.bitplatform.dev/TodoSample.Client.Windows-win-Setup.exe) | [![macOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382211-0d58f9ba-1a1f-4481-a0ca-b23a393cca9f.png)](https://apps.apple.com/nl/app/bit-todotemplate/id6450611072) + +1. [bitplatform.dev](https://bitplatform.dev): .NET 9 Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +2. [sales.bitplatform.dev](https://sales.bitplatform.dev): .NET 9 Sales Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +3. [blazorui.bitplatform.dev](https://blazorui.bitplatform.dev): .NET 9 Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +4. [adminpanel.bitplatform.dev](https://adminpanel.bitplatform.dev): .NET 9 PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +5. [todo.bitplatform.dev](https://todo.bitplatform.dev): .NET 8 Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +6. [adminpanel.bitplatform.cc](https://adminpanel.bitplatform.cc): .NET 9 PWA with Blazor WebAssembly Standalone (Azure static web app) +7. [todo-aot.bitplatform.cc](https://todo-aot.bitplatform.cc): .NET 9 AOT Compiled PWA with Blazor WebAssembly Standalone (Azure static web app) +8. [todo-small.bitplatform.cc](https://todo-small.bitplatform.cc): .NET 9 Todo demo app with smaller download footprint (Azure static web app) +9. [todo-offline.bitplatform.cc](https://todo-offline.bitplatform.cc): .NET 9 Todo demo app with ef-core & sqlite (Azure static web app) + +[Todo](https://todo.bitplatform.dev) & [Adminpanel](https://adminpanel.bitplatform.dev) web apps will launch their respective Android and iOS applications if you have already installed them, mirroring the behavior of apps like YouTube and Instagram. + +Prerendering combined with PWA functionality delivers an experience akin to that of GitHub and Reddit. The bitplatform solution, seamlessly integrated with the innovative new .NET 8 project structure, stands as the exclusive remedy for such a scenario within the realm of Blazor. + +# How to contribute? + +We welcome contributions! Many people all over the world have helped make this project better. + +* [Contributing](CONTRIBUTING.md) explains what kinds of contributions we welcome. +* [Build Instructions](docs/how-to-build.md) explains how to build and test. +* [Get Up and Running on bit platform](docs/up-and-running.md) explains how to get the latest builds and their libraries to test them in your own projects. + +
    + +# **Contributions** + +![Alt](https://repobeats.axiom.co/api/embed/66dc1fc04ed967094b98ac118e8f18fa38b19f6a.svg ""bit platform open source contributions report"")"; + + + + private readonly string example1RazorCode = @" +"; + + private readonly string example2RazorCode = @" + +"; + private readonly string example2CsharpCode = @" +private string advancedMarkdown = @""![Header](https://user-images.githubusercontent.com/6169846/251658486-b16e1db8-5481-46c4-9fc1-c9b279a4364a.png) + +
    + +![License](https://img.shields.io/github/license/bitfoundation/bitplatform.svg) +![CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.yml/badge.svg) +![NuGet version](https://img.shields.io/nuget/v/bit.blazorui.svg?logo=nuget) +[![Nuget downloads](https://img.shields.io/badge/packages_download-6.0M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform """"Average time to resolve an issue"""") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform """"Percentage of issues still open"""") + +
    + +# 🧾 Introduction + +At **bitplatform**, we've curated a comprehensive toolkit to empower you in crafting the finest projects using Blazor. Diverging from others merely offering UI Toolkits, ***bit BlazorUI components*** distinguishes itself with over 56 components, with a compact size of under 400 KB. These components boast both Dark and Light Themes, delivering unparalleled High Performance 🚀 + +Yet, bitplatform doesn't stop there. Our platform introduces exclusive tools that elevate your development experience: + +***Bswup***: This unique tool harnesses the power of Progressive Web Apps (PWA) within the innovative new structure of dotnet 8. By amalgamating pre-rendering techniques reminiscent of renowned platforms like GitHub, Reddit, and Facebook, Bswup ensures an exceptional user experience 😍 + +***Butil***: Embracing Blazor because of your love for C#? Butil enables you to stay true to that sentiment by providing essential Browser APIs in C#, eliminating the need to revert to JavaScript for any functionality 👌 + +***Besql***: Dreaming of an offline web application capable of saving data and syncing later? Enter Besql, your solution to incorporating ef core & sqlite in your browser. It's a crucial aid for achieving this objective seamlessly 🕺 + +***Bit Boilerplate Project Template***: If the aforementioned features have piqued your interest, dive into the Bit Boilerplate project template. Experience everything mentioned above along with additional features such as ASP.NET Core Identity integration, multilingualism, and other cool features that empowers you to develop unified Web, Android, iOS, Windows, and macOS apps from a single codebase, while providing seamless integration with native platform features and third-party Java, Kotlin, Swift, Objective-C, and JavaScript libraries 💯 + +For more details, visit us at [bitplatform.dev](https://bitplatform.dev/). + +
    + +**Note**: This project is tested with [BrowserStack](https://www.browserstack.com/). + +
    + +# 🎁 OSS Showcases + +The following apps are our open-source projects powered by the bit platform showcasing the different capabilities of our toolchain: + +| |    Web    |    iOS    | Android | Windows | macOS | +|:-:|:--:|:--:|:--:|:--:|:--:| +| bitplatform | [![PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://bitplatform.dev)| *N/A* | *N/A* | *N/A* | *N/A* | +| Sales | [![PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://sales.bitplatform.dev) | *Soon!* | *Soon!* | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-sales.bitplatform.dev/SalesModule.Client.Windows-win-Setup.exe) | *Soon!* | +| bit BlazorUI | [![Prerendered PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://blazorui.bitplatform.dev) | [![iOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381842-e72976ce-fd20-431d-a677-ca1ed625b83b.png)](https://apps.apple.com/us/app/bit-blazor-ui/id6450401404) | [![Android app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381958-24931682-87f6-44fc-a1c7-eecf46387005.png)](https://play.google.com/store/apps/details?id=com.bitplatform.BlazorUI.Demo) | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-components.bitplatform.dev/Bit.BlazorUI.Demo.Client.Windows-win-Setup.exe) | [![macOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382211-0d58f9ba-1a1f-4481-a0ca-b23a393cca9f.png)](https://apps.apple.com/nl/app/bit-blazor-ui/id6450401404) +| AdminPanel | [![Prerendered PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://adminpanel.bitplatform.dev) | [![iOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381842-e72976ce-fd20-431d-a677-ca1ed625b83b.png)](https://apps.apple.com/us/app/bit-adminpanel/id6450611349) | [![Android app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381958-24931682-87f6-44fc-a1c7-eecf46387005.png)](https://play.google.com/store/apps/details?id=com.bitplatform.AdminPanel.Template) | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-admin.bitplatform.dev/AdminPanel.Client.Windows-win-Setup.exe) | [![macOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382211-0d58f9ba-1a1f-4481-a0ca-b23a393cca9f.png)](https://apps.apple.com/nl/app/bit-adminpanel/id6450611349) | +| Todo | [![Prerendered PWA](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381583-8b8eb895-80c9-4811-9641-57a5a08db163.png)](https://todo.bitplatform.dev) | [![iOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381842-e72976ce-fd20-431d-a677-ca1ed625b83b.png)](https://apps.apple.com/us/app/bit-todotemplate/id6450611072) | [![Android app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251381958-24931682-87f6-44fc-a1c7-eecf46387005.png)](https://play.google.com/store/apps/details?id=com.bitplatform.Todo.Template) | [![Windows app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382080-9ae97fea-934c-4097-aca4-124a2aed1595.png)](https://windows-todo.bitplatform.dev/TodoSample.Client.Windows-win-Setup.exe) | [![macOS app](https://github-production-user-asset-6210df.s3.amazonaws.com/6169846/251382211-0d58f9ba-1a1f-4481-a0ca-b23a393cca9f.png)](https://apps.apple.com/nl/app/bit-todotemplate/id6450611072) + +1. [bitplatform.dev](https://bitplatform.dev): .NET 9 Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +2. [sales.bitplatform.dev](https://sales.bitplatform.dev): .NET 9 Sales Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +3. [blazorui.bitplatform.dev](https://blazorui.bitplatform.dev): .NET 9 Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +4. [adminpanel.bitplatform.dev](https://adminpanel.bitplatform.dev): .NET 9 PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +5. [todo.bitplatform.dev](https://todo.bitplatform.dev): .NET 8 Pre-rendered PWA with Blazor WebAssembly (Azure Web App + Cloudflare CDN) +6. [adminpanel.bitplatform.cc](https://adminpanel.bitplatform.cc): .NET 9 PWA with Blazor WebAssembly Standalone (Azure static web app) +7. [todo-aot.bitplatform.cc](https://todo-aot.bitplatform.cc): .NET 9 AOT Compiled PWA with Blazor WebAssembly Standalone (Azure static web app) +8. [todo-small.bitplatform.cc](https://todo-small.bitplatform.cc): .NET 9 Todo demo app with smaller download footprint (Azure static web app) +9. [todo-offline.bitplatform.cc](https://todo-offline.bitplatform.cc): .NET 9 Todo demo app with ef-core & sqlite (Azure static web app) + +[Todo](https://todo.bitplatform.dev) & [Adminpanel](https://adminpanel.bitplatform.dev) web apps will launch their respective Android and iOS applications if you have already installed them, mirroring the behavior of apps like YouTube and Instagram. + +Prerendering combined with PWA functionality delivers an experience akin to that of GitHub and Reddit. The bitplatform solution, seamlessly integrated with the innovative new .NET 8 project structure, stands as the exclusive remedy for such a scenario within the realm of Blazor. + +# How to contribute? + +We welcome contributions! Many people all over the world have helped make this project better. + +* [Contributing](CONTRIBUTING.md) explains what kinds of contributions we welcome. +* [Build Instructions](docs/how-to-build.md) explains how to build and test. +* [Get Up and Running on bit platform](docs/up-and-running.md) explains how to get the latest builds and their libraries to test them in your own projects. + +
    + +# **Contributions** + +![Alt](https://repobeats.axiom.co/api/embed/66dc1fc04ed967094b98ac118e8f18fa38b19f6a.svg """"bit platform open source contributions report"""")"";"; +} diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.scss new file mode 100644 index 0000000000..954a678855 --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.scss @@ -0,0 +1,7 @@ +::deep { + .advanced { + img { + max-width: 100%; + } + } +} diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor index 34724f09e1..8ffbba07ce 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor @@ -262,6 +262,9 @@ InfiniteScrolling + + MarkdownViewer + MessageBox diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs index baba7ffa6f..709b26429f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs @@ -156,6 +156,7 @@ public partial class MainLayout new() { Text = "DataGrid", Url = "/components/datagrid", AdditionalUrls = ["/components/data-grid"] }, new() { Text = "ErrorBoundary", Url = "/components/errorboundary" }, new() { Text = "InfiniteScrolling", Url = "/components/infinitescrolling" }, + new() { Text = "MarkdownViewer", Url = "/components/markdownviewer" }, new() { Text = "MessageBox", Url = "/components/messagebox" }, new() { Text = "NavPanel", Url = "/components/navpanel" }, new() { Text = "PdfReader", Url = "/components/pdfreader" }, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json index 5ee2e0e469..94fe095a3d 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json @@ -77,6 +77,12 @@ "minify": { "enabled": false }, "options": { "sourceMap": false } }, + { + "outputFile": "Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.css", + "inputFile": "Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.scss", + "minify": { "enabled": false }, + "options": { "sourceMap": false } + }, { "outputFile": "Pages/Components/Extras/MessageBox/BitMessageBoxDemo.razor.css", "inputFile": "Pages/Components/Extras/MessageBox/BitMessageBoxDemo.razor.scss", From b5d3293fdbff315f07b8ca6c5e9214e13312e27b Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 8 Mar 2025 20:27:32 +0330 Subject: [PATCH 03/14] remove redundant line --- .../Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor | 1 - 1 file changed, 1 deletion(-) diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor index c75e7dc389..8ec3a925d7 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor @@ -13,7 +13,6 @@ -
    From 4b5a5b87e9853f39efc05fdd27eb944bb91347ed Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 9 Mar 2025 14:01:10 +0330 Subject: [PATCH 04/14] add Jint --- .../Bit.BlazorUI.Extras.csproj | 3 + .../Components/Chart/BitChart.razor.cs | 2 +- .../Components/Chart/BitChart.ts | 33 ----- .../Chart/JsInterop/BitChartJsInterop.cs | 5 - .../MarkdownViewer/BitMarkdownViewer.razor.cs | 78 +++++++++- .../MarkdownViewer/BitMarkdownViewer.ts | 35 +---- .../BitMarkdownViewerJsRuntimeExtensions.cs | 7 +- .../PdfReader/BitPdfReader.razor.cs | 2 +- .../Components/PdfReader/BitPdfReader.ts | 34 ----- .../BitPdfReaderJsRuntimeExtensions.cs | 5 - .../JsInterop/ExtrasJsRuntimeExtensions.cs | 5 + .../Bit.BlazorUI.Extras/Scripts/Extras.ts | 43 ++++++ .../JsInterop/IJSRuntimeExtensions.cs | 18 ++- .../JsInterop/IJSRuntimeFastExtensions.cs | 136 ++++++++++++++++++ .../Bit.BlazorUI.Demo.Server.csproj | 3 +- .../Components/App.razor | 15 +- .../BitMarkdownViewerDemo.razor | 2 +- .../BitMarkdownViewerDemo.razor.cs | 1 + 18 files changed, 296 insertions(+), 131 deletions(-) create mode 100644 src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj b/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj index b6ed4c0749..95f2283f42 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj @@ -20,6 +20,9 @@ all + + all + diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs index a02bc9d68f..b20ff0303a 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs @@ -142,7 +142,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) scripts.AddRange(DateAdapterScripts); } - await _js.BitChartJsInitChartJs(scripts); + await _js.BitExtrasInitScripts(scripts); if (Config is not null) { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts index 68ed3b8d89..f4eb9ac8e9 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts @@ -22,7 +22,6 @@ namespace BitBlazorUI { } export class BitChart { - private static _initPromise?: Promise; private static _bitCharts = new Map(); public static getChartJs(canvasId: string) { @@ -31,38 +30,6 @@ namespace BitBlazorUI { return BitChart._bitCharts.get(canvasId)!; } - public static async initChartJs(scripts: string[]) { - if (BitChart._initPromise) { - await BitChart._initPromise; - } - - const allScripts = Array.from(document.scripts).map(s => s.src); - const notAppenedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); - - if (notAppenedScripts.length == 0) return Promise.resolve(); - - const promise = new Promise(async (resolve: any, reject: any) => { - try { - for (let url of notAppenedScripts) await addScript(url); - resolve(); - } catch (e: any) { - reject(e); - } - }); - BitChart._initPromise = promise; - return promise; - - async function addScript(url: string) { - return new Promise((res, rej) => { - const script = document.createElement('script'); - script.src = url; - script.onload = res; - script.onerror = rej; - document.body.appendChild(script); - }) - } - } - public static removeChart(canvasId: string) { if (!BitChart._bitCharts.has(canvasId)) return; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs index a3601570e0..391b21674f 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs @@ -20,11 +20,6 @@ internal static class BitChartJsInterop Converters = { new IsoDateTimeConverter() } }; - public static ValueTask BitChartJsInitChartJs(this IJSRuntime jsRuntime, IEnumerable scripts) - { - return jsRuntime.InvokeVoid("BitBlazorUI.BitChart.initChartJs", scripts); - } - public static ValueTask BitChartJsRemoveChart(this IJSRuntime jsRuntime, string? canvasId) { return jsRuntime.InvokeVoid("BitBlazorUI.BitChart.removeChart", canvasId); diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 785e8e95ed..208e69bf73 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -1,4 +1,6 @@ -namespace Bit.BlazorUI; +using Jint; + +namespace Bit.BlazorUI; /// /// BitMarkdownViewer is a Blazor wrapper around the famous markedjs library. @@ -6,7 +8,12 @@ /// public partial class BitMarkdownViewer : BitComponentBase { + private static string _ScriptPath = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"; + + + private string? _html; + private CancellationTokenSource _cts = new(); @@ -24,19 +31,70 @@ public partial class BitMarkdownViewer : BitComponentBase protected override string RootElementClass => "bit-mdv"; + protected override async Task OnInitializedAsync() + { + if (_js.IsRuntimeInvalid()) // prerendering + { + try + { + await RunJint(); + } + catch (Exception ex) + { + System.Console.Error.WriteLine(ex.ToString()); + } + } + else if (OperatingSystem.IsBrowser()) + { + if (await _js.BitMarkdownViewerCheckScript(_ScriptPath)) + { + await ParseAndRender(); + } + } + + await base.OnInitializedAsync(); + } + protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (firstRender) { - await _js.BitPdfReaderInit(["_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"]); - - await ParseAndRender(); + if (await _js.BitMarkdownViewerCheckScript(_ScriptPath) is false) + { + await _js.BitExtrasInitScripts([_ScriptPath]); + } + + if (_html.HasNoValue()) + { + await ParseAndRender(); + } } } + private async Task RunJint() + { + if (Markdown.HasNoValue()) return; + + await Task.Run(async () => + { + var scriptPath = Path.Combine(Environment.CurrentDirectory, "..", "..", "Bit.BlazorUI.Extras", "wwwroot", "marked", "marked-15.0.7.js"); + var script = await File.ReadAllTextAsync(scriptPath); + + var engine = new Engine(options => + { + options.CancellationToken(_cts.Token); + }).Execute(script); + + var fn = engine.Evaluate("marked.parse").AsFunctionInstance(); + + _html = fn.Call(Markdown).AsString(); + + await InvokeAsync(StateHasChanged); + }, _cts.Token); + } private async Task OnMarkdownSet() { @@ -53,4 +111,16 @@ private async Task ParseAndRender() StateHasChanged(); } + + + + protected override async ValueTask DisposeAsync(bool disposing) + { + if (IsDisposed || disposing is false) return; + + _cts.Cancel(); + _cts.Dispose(); + + await base.DisposeAsync(disposing); + } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts index ef44bb2f4d..4e5b5bae78 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -2,40 +2,13 @@ namespace BitBlazorUI { export class MarkdownViewer { private static _initPromise?: Promise; - public static async init(scripts: string[]) { - if (MarkdownViewer._initPromise) { - await MarkdownViewer._initPromise; - } - + public static checkScript(script: string) { const allScripts = Array.from(document.scripts).map(s => s.src); - const notAppenedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); - - if (notAppenedScripts.length == 0) return Promise.resolve(); - - const promise = new Promise(async (resolve: any, reject: any) => { - try { - for (let url of notAppenedScripts) await addScript(url); - resolve(); - } catch (e: any) { - reject(e); - } - }); - MarkdownViewer._initPromise = promise; - return promise; - - async function addScript(url: string) { - return new Promise((res, rej) => { - const script = document.createElement('script'); - script.src = url; - script.onload = res; - script.onerror = rej; - document.body.appendChild(script); - }) - } + return !!allScripts.find(as => as.includes(script)); } - public static async parse(md: string) { - const html = await marked.parse(md, { async: true }); + public static parse(md: string) { + const html = marked.parse(md, { async: false }); return html; } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs index 98de2bfc1e..283c221663 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -1,14 +1,15 @@ namespace Bit.BlazorUI; +[System.Diagnostics.CodeAnalysis.SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] internal static class BitMarkdownViewerJsRuntimeExtensions { - public static ValueTask BitMarkdownViewerInit(this IJSRuntime jsRuntime, IEnumerable scripts) + public static ValueTask BitMarkdownViewerCheckScript(this IJSRuntime jsRuntime, string script) { - return jsRuntime.InvokeVoid("BitBlazorUI.MarkdownViewer.init", scripts); + return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.checkScript", script); } public static ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) { - return jsRuntime.Invoke("BitBlazorUI.MarkdownViewer.parse", markdown); + return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.parse", markdown); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs index 1c655a52e1..dbb34b22fa 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.razor.cs @@ -174,7 +174,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) "_content/Bit.BlazorUI.Extras/pdf.js/pdfjs-4.7.76-worker.js" ]; - await _js.BitPdfReaderInit(scripts); + await _js.BitExtrasInitScripts(scripts, true); _numberOfPages = await _js.BitPdfReaderSetup(Config); diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts index 9d78cee62a..5fe6adafb0 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReader.ts @@ -1,41 +1,7 @@ namespace BitBlazorUI { export class PdfReader { - private static _initPromise?: Promise; private static _bitPdfReaders = new Map(); - public static async init(scripts: string[]) { - if (PdfReader._initPromise) { - await PdfReader._initPromise; - } - - const allScripts = Array.from(document.scripts).map(s => s.src); - const notAppenedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); - - if (notAppenedScripts.length == 0) return Promise.resolve(); - - const promise = new Promise(async (resolve: any, reject: any) => { - try { - for (let url of notAppenedScripts) await addScript(url); - resolve(); - } catch (e: any) { - reject(e); - } - }); - PdfReader._initPromise = promise; - return promise; - - async function addScript(url: string) { - return new Promise((res, rej) => { - const script = document.createElement('script'); - script.src = url; - script.type = 'module'; - script.onload = res; - script.onerror = rej; - document.body.appendChild(script); - }) - } - } - public static async setup(config: BitPdfReaderConfig) { const { pdfjsLib } = globalThis as unknown as { pdfjsLib: PdfJsLib }; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs index 2039be382b..f1e7c1eff2 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs @@ -2,11 +2,6 @@ internal static class BitPdfReaderJsRuntimeExtensions { - public static ValueTask BitPdfReaderInit(this IJSRuntime jsRuntime, IEnumerable scripts) - { - return jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.init", scripts); - } - public static ValueTask BitPdfReaderSetup(this IJSRuntime jsRuntime, BitPdfReaderConfig config) { return jsRuntime.Invoke("BitBlazorUI.PdfReader.setup", config); diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs index 7fb1869f11..42a18342c9 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs @@ -16,4 +16,9 @@ internal static ValueTask BitExtrasScrollBy(this IJSRuntime jsRuntime, ElementRe { return jsRuntime.InvokeVoid("BitBlazorUI.Extras.scrollBy", element, x, y); } + + public static ValueTask BitExtrasInitScripts(this IJSRuntime jsRuntime, IEnumerable scripts, bool isModule = false) + { + return jsRuntime.InvokeVoid("BitBlazorUI.Extras.initScript", scripts, isModule); + } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts index e438769f3f..b137e343bb 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts @@ -18,5 +18,48 @@ namespace BitBlazorUI { element.scrollBy(x, y); } + + private static _initScriptPromises: { [key: string]: Promise } = {}; + public static async initScript(scripts: string[], isModule: boolean) { + const key = scripts.join('|'); + if (Extras._initScriptPromises[key] !== undefined) { + return await Extras._initScriptPromises[key]; + } + + const allScripts = Array.from(document.scripts).map(s => s.src); + const notAddedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); + + if (notAddedScripts.length == 0) return Promise.resolve(); + + const promise = new Promise((resolve: any, reject: any) => { + try { + (async function loadScripts() { + try { + await Promise.all(notAddedScripts.map(addScript)); + resolve(); + } catch (e: any) { + reject(e); + } + }()); + } catch (e: any) { + reject(e); + } + }); + Extras._initScriptPromises[key] = promise; + return promise; + + async function addScript(url: string) { + return new Promise((res, rej) => { + const script = document.createElement('script'); + script.src = url; + if (isModule) { + script.type = 'module'; + } + script.onload = res; + script.onerror = rej; + document.body.appendChild(script); + }) + } + } } } \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs index 9087399658..fc58e0b85d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs @@ -1,9 +1,14 @@ using System.Diagnostics.CodeAnalysis; +using System.Reflection; namespace Bit.BlazorUI; public static class IJSRuntimeExtensions { + public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties; + + + /// /// Only tries to Invoke the js call when the runtime is valid. /// @@ -39,7 +44,7 @@ public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string ident /// /// Only tries to Invoke the js call when the runtime is valid. /// - public static ValueTask Invoke<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + public static ValueTask Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { if (jsRuntime.IsRuntimeInvalid()) return default; @@ -49,7 +54,7 @@ public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string ident /// /// Only tries to Invoke the js call when the runtime is valid. /// - public static ValueTask InvokeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + public static ValueTask Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { if (jsRuntime.IsRuntimeInvalid()) return default; @@ -59,7 +64,7 @@ public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string ident /// /// Only tries to Invoke the js call when the runtime is valid. /// - public static ValueTask InvokeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + public static ValueTask Invoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime.IsRuntimeInvalid()) return default; @@ -69,15 +74,18 @@ public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string ident [SuppressMessage("Trimming", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "")] - internal static bool IsRuntimeInvalid(this IJSRuntime jsRuntime) + public static bool IsRuntimeInvalid(this IJSRuntime jsRuntime) { + if (jsRuntime is null) return false; + var type = jsRuntime.GetType(); return type.Name switch { "UnsupportedJavaScriptRuntime" => true, // Prerendering "RemoteJSRuntime" => (bool)type.GetProperty("IsInitialized")!.GetValue(jsRuntime)! is false, // Blazor server - _ => false // Blazor WASM/Hybrid + "WebViewJSRuntime" => type.GetField("_ipcSender", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(jsRuntime) is null, // Blazor Hybrid + _ => false // Blazor WASM }; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs new file mode 100644 index 0000000000..7cafdad60e --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs @@ -0,0 +1,136 @@ +using System.Text.Json; +using System.Diagnostics.CodeAnalysis; +using Microsoft.JSInterop.Infrastructure; + +namespace Bit.BlazorUI; + +public static class IJSRuntimeFastExtensions +{ + public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties; + + + + /// + /// Invokes the specified JavaScript function with the fastest speed possible. + /// + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + { + return FastInvokeVoid(jsRuntime, identifier, CancellationToken.None, args); + } + + /// + /// Invokes the specified JavaScript function with the fastest speed possible. + /// + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// The duration after which to cancel the async operation. Overrides default timeouts (). + /// JSON-serializable arguments. + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + { + using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); + var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; + + return FastInvokeVoid(jsRuntime, identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function with the fastest speed possible. + /// + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + { + if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) + { + try + { + jsInProcessRuntime.Invoke(identifier, args); + return ValueTask.CompletedTask; + } + catch (JsonException ex) + { + System.Console.Error.WriteLine($"Error invoking '{identifier}' using {nameof(IJSInProcessRuntime)}. A JSON-related issue occurred: {ex.Message}."); + return ValueTask.CompletedTask; + } + } + else + { + return jsRuntime.InvokeVoid(identifier, cancellationToken, args); + } + } + + + + /// + /// Invokes the specified JavaScript function with the fastest speed possible. + /// Note: In Blazor WebAssembly mode, use this method only for synchronous JavaScript functions. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + { + return FastInvoke(jsRuntime, identifier, CancellationToken.None, args); + } + + /// + /// Invokes the specified JavaScript function with the fastest speed possible. + /// Note: In Blazor WebAssembly mode, use this method only for synchronous JavaScript functions. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// The duration after which to cancel the async operation. Overrides default timeouts (). + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + { + using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); + var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; + + return FastInvoke(jsRuntime, identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function with the fastest speed possible. + /// Note: In Blazor WebAssembly mode, use this method only for synchronous JavaScript functions. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + { + if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) + { + try + { + return ValueTask.FromResult(jsInProcessRuntime.Invoke(identifier, args)); + } + catch (JsonException ex) + { + System.Console.Error.WriteLine($"Error invoking '{identifier}' using {nameof(IJSInProcessRuntime)}. A JSON-related issue occurred: {ex.Message}."); + return ValueTask.FromResult(default(TValue)!); + } + } + else + { + return jsRuntime.Invoke(identifier, cancellationToken, args); + } + } +} diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index e7b299b47d..976d2afdf4 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -13,6 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor index e2d145bb2d..2678bc3066 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor @@ -46,14 +46,15 @@ @if (HttpContext.Request.IsLightHouseRequest() is false) { - - - + + + + - - - - + + + + } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor index 8ec3a925d7..e8bf22b44c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor @@ -18,4 +18,4 @@
    - \ No newline at end of file + diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs index 1b7ae4782f..b1c65fe0d5 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs @@ -14,6 +14,7 @@ public partial class BitMarkdownViewerDemo ]; + private string advancedMarkdown = @"![Header](https://user-images.githubusercontent.com/6169846/251658486-b16e1db8-5481-46c4-9fc1-c9b279a4364a.png)
    From 12181b4e955b2abb350b39579e6f925c4aec099e Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Sun, 9 Mar 2025 12:19:37 +0100 Subject: [PATCH 05/14] improve bit md viewer --- .../MarkdownViewer/BitMarkdownViewer.razor.cs | 30 ++++++++----------- .../MarkdownViewer/BitMarkdownViewer.ts | 5 ++++ .../BitMarkdownViewerJsRuntimeExtensions.cs | 5 ++-- .../JsInterop/IJSRuntimeFastExtensions.cs | 14 ++++----- .../Components/App.razor | 2 +- .../BitMarkdownViewerDemo.razor | 3 +- .../Shared/MainLayout.razor.NavItems.cs | 2 +- 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 208e69bf73..322f6cdbdc 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -1,4 +1,5 @@ using Jint; +using System.Globalization; namespace Bit.BlazorUI; @@ -39,27 +40,16 @@ protected override async Task OnInitializedAsync() { await RunJint(); } - catch (Exception ex) + catch (FileNotFoundException ex) when (ex.FileName?.StartsWith("Jint") is true) { - System.Console.Error.WriteLine(ex.ToString()); + Console.Error.WriteLine("Please install `Jint` nuget package into your SERVER project."); } - } - else if (OperatingSystem.IsBrowser()) - { - if (await _js.BitMarkdownViewerCheckScript(_ScriptPath)) + catch (Exception ex) { - await ParseAndRender(); + Console.Error.WriteLine(ex.Message); } } - - await base.OnInitializedAsync(); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await base.OnAfterRenderAsync(firstRender); - - if (firstRender) + else { if (await _js.BitMarkdownViewerCheckScript(_ScriptPath) is false) { @@ -71,8 +61,9 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await ParseAndRender(); } } - } + await base.OnInitializedAsync(); + } private async Task RunJint() { @@ -83,9 +74,11 @@ await Task.Run(async () => var scriptPath = Path.Combine(Environment.CurrentDirectory, "..", "..", "Bit.BlazorUI.Extras", "wwwroot", "marked", "marked-15.0.7.js"); var script = await File.ReadAllTextAsync(scriptPath); - var engine = new Engine(options => + using var engine = new Engine(options => { + options.Strict(); options.CancellationToken(_cts.Token); + options.Culture(CultureInfo.CurrentUICulture); }).Execute(script); var fn = engine.Evaluate("marked.parse").AsFunctionInstance(); @@ -93,6 +86,7 @@ await Task.Run(async () => _html = fn.Call(Markdown).AsString(); await InvokeAsync(StateHasChanged); + }, _cts.Token); } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts index 4e5b5bae78..4b10f1f5b0 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -11,5 +11,10 @@ namespace BitBlazorUI { const html = marked.parse(md, { async: false }); return html; } + + public static async parseAsync(md: string) { + const html = await marked.parse(md); + return html; + } } } \ No newline at end of file diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs index 283c221663..f9ed176e46 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -8,8 +8,9 @@ public static ValueTask BitMarkdownViewerCheckScript(this IJSRuntime jsRun return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.checkScript", script); } - public static ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) + public static async ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) { - return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.parse", markdown); + return OperatingSystem.IsBrowser() ? await jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.parse", markdown) + : await jsRuntime.InvokeAsync("BitBlazorUI.MarkdownViewer.parseAsync", markdown); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs index 7cafdad60e..74126d18c2 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs @@ -4,7 +4,7 @@ namespace Bit.BlazorUI; -public static class IJSRuntimeFastExtensions +internal static class IJSRuntimeFastExtensions { public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties; @@ -16,7 +16,7 @@ public static class IJSRuntimeFastExtensions /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { return FastInvokeVoid(jsRuntime, identifier, CancellationToken.None, args); } @@ -28,7 +28,7 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// The duration after which to cancel the async operation. Overrides default timeouts (). /// JSON-serializable arguments. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; @@ -46,7 +46,7 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// /// JSON-serializable arguments. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) { @@ -78,7 +78,7 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + internal static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { return FastInvoke(jsRuntime, identifier, CancellationToken.None, args); } @@ -93,7 +93,7 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + internal static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; @@ -114,7 +114,7 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + internal static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) { diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor index 2678bc3066..da1637186f 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor @@ -46,7 +46,7 @@ @if (HttpContext.Request.IsLightHouseRequest() is false) { - + diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor index e8bf22b44c..f77910bc70 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor @@ -5,8 +5,9 @@ Description="markdownviewer component of the bit BlazorUI components" /> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs index 709b26429f..e90ce87f8b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs @@ -156,7 +156,7 @@ public partial class MainLayout new() { Text = "DataGrid", Url = "/components/datagrid", AdditionalUrls = ["/components/data-grid"] }, new() { Text = "ErrorBoundary", Url = "/components/errorboundary" }, new() { Text = "InfiniteScrolling", Url = "/components/infinitescrolling" }, - new() { Text = "MarkdownViewer", Url = "/components/markdownviewer" }, + new() { Text = "MarkdownViewer", Url = "/components/markdownviewer", Description = "MD viewer" }, new() { Text = "MessageBox", Url = "/components/messagebox" }, new() { Text = "NavPanel", Url = "/components/navpanel" }, new() { Text = "PdfReader", Url = "/components/pdfreader" }, From b7df4903fd328da2a2a0d0945337c03feb73d749 Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Sun, 9 Mar 2025 12:26:42 +0100 Subject: [PATCH 06/14] fix --- .../Components/MarkdownViewer/BitMarkdownViewer.ts | 2 +- .../Demo/Bit.BlazorUI.Demo.Server/Components/App.razor | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts index 4b10f1f5b0..2ef8729040 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -13,7 +13,7 @@ namespace BitBlazorUI { } public static async parseAsync(md: string) { - const html = await marked.parse(md); + const html = await marked.parse(md, { async: true }); return html; } } diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor index da1637186f..1ea7301378 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor @@ -46,7 +46,10 @@ @if (HttpContext.Request.IsLightHouseRequest() is false) { - + if (AppRenderMode.PrerenderEnabled) + { + + } From d86aee27bf67a2b7d3ea7d605c468492801ea56b Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 9 Mar 2025 15:22:18 +0330 Subject: [PATCH 07/14] improve demo page and fix stuff --- .../MarkdownViewer/BitMarkdownViewer.razor.cs | 2 +- .../BitMarkdownViewerJsRuntimeExtensions.cs | 7 ++--- .../JsInterop/IJSRuntimeFastExtensions.cs | 14 +++++----- .../BitMarkdownViewerDemo.razor | 26 ++++++++++++------- .../Shared/MainLayout.razor.NavItems.cs | 2 +- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 322f6cdbdc..f76ff28aa3 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -4,7 +4,7 @@ namespace Bit.BlazorUI; /// -/// BitMarkdownViewer is a Blazor wrapper around the famous markedjs library. +/// BitMarkdownViewer is a SEO friendly Blazor wrapper around the famous markedjs library. /// /// public partial class BitMarkdownViewer : BitComponentBase diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs index f9ed176e46..f29c704934 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -8,9 +8,10 @@ public static ValueTask BitMarkdownViewerCheckScript(this IJSRuntime jsRun return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.checkScript", script); } - public static async ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) + public static ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) { - return OperatingSystem.IsBrowser() ? await jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.parse", markdown) - : await jsRuntime.InvokeAsync("BitBlazorUI.MarkdownViewer.parseAsync", markdown); + return OperatingSystem.IsBrowser() + ? jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.parse", markdown) + : jsRuntime.Invoke("BitBlazorUI.MarkdownViewer.parseAsync", markdown); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs index 74126d18c2..7cafdad60e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs @@ -4,7 +4,7 @@ namespace Bit.BlazorUI; -internal static class IJSRuntimeFastExtensions +public static class IJSRuntimeFastExtensions { public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties; @@ -16,7 +16,7 @@ internal static class IJSRuntimeFastExtensions /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { return FastInvokeVoid(jsRuntime, identifier, CancellationToken.None, args); } @@ -28,7 +28,7 @@ internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string ident /// The duration after which to cancel the async operation. Overrides default timeouts (). /// JSON-serializable arguments. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; @@ -46,7 +46,7 @@ internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string ident /// /// JSON-serializable arguments. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) { @@ -78,7 +78,7 @@ internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string ident /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - internal static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { return FastInvoke(jsRuntime, identifier, CancellationToken.None, args); } @@ -93,7 +93,7 @@ internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string ident /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - internal static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; @@ -114,7 +114,7 @@ internal static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string ident /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] - internal static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor index f77910bc70..4648528560 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor @@ -6,17 +6,23 @@ - - - + + + + - -
    - -
    -
    + + + + + + +
    + +
    +
    +
    diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs index e90ce87f8b..95725dbd8f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs @@ -156,7 +156,7 @@ public partial class MainLayout new() { Text = "DataGrid", Url = "/components/datagrid", AdditionalUrls = ["/components/data-grid"] }, new() { Text = "ErrorBoundary", Url = "/components/errorboundary" }, new() { Text = "InfiniteScrolling", Url = "/components/infinitescrolling" }, - new() { Text = "MarkdownViewer", Url = "/components/markdownviewer", Description = "MD viewer" }, + new() { Text = "MarkdownViewer", Url = "/components/markdownviewer", Description = "MdViewer, MD" }, new() { Text = "MessageBox", Url = "/components/messagebox" }, new() { Text = "NavPanel", Url = "/components/navpanel" }, new() { Text = "PdfReader", Url = "/components/pdfreader" }, From 57644b502721fa803353fdf4e18abed6c11faf61 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 9 Mar 2025 15:25:46 +0330 Subject: [PATCH 08/14] improve script path variable --- .../MarkdownViewer/BitMarkdownViewer.razor.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index f76ff28aa3..6952f1a52d 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -9,10 +9,6 @@ namespace Bit.BlazorUI; /// public partial class BitMarkdownViewer : BitComponentBase { - private static string _ScriptPath = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"; - - - private string? _html; private CancellationTokenSource _cts = new(); @@ -42,7 +38,7 @@ protected override async Task OnInitializedAsync() } catch (FileNotFoundException ex) when (ex.FileName?.StartsWith("Jint") is true) { - Console.Error.WriteLine("Please install `Jint` nuget package into your SERVER project."); + Console.Error.WriteLine("Please install `Jint` nuget package on your SERVER project."); } catch (Exception ex) { @@ -51,9 +47,10 @@ protected override async Task OnInitializedAsync() } else { - if (await _js.BitMarkdownViewerCheckScript(_ScriptPath) is false) + var scriptPath = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"; + if (await _js.BitMarkdownViewerCheckScript(scriptPath) is false) { - await _js.BitExtrasInitScripts([_ScriptPath]); + await _js.BitExtrasInitScripts([scriptPath]); } if (_html.HasNoValue()) From 24449caf9b98b8d64397f1493958340b85fd37d8 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 9 Mar 2025 15:34:05 +0330 Subject: [PATCH 09/14] add missing quotations --- src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor index 1ea7301378..0bef6e26a6 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor @@ -48,7 +48,7 @@ { if (AppRenderMode.PrerenderEnabled) { - + } From c62c0ed1b8ca1b723be2c35ffb7ca86db26d3d5a Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Sun, 9 Mar 2025 13:19:14 +0100 Subject: [PATCH 10/14] fix --- .../MarkdownViewer/BitMarkdownViewer.razor.cs | 37 +++++++++++++++++-- .../Bit.BlazorUI.Demo.Server.csproj | 6 +++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 6952f1a52d..5958bcebd7 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -1,5 +1,7 @@ using Jint; +using System.Reflection; using System.Globalization; +using Microsoft.Extensions.DependencyInjection; namespace Bit.BlazorUI; @@ -15,6 +17,7 @@ public partial class BitMarkdownViewer : BitComponentBase [Inject] private IJSRuntime _js { get; set; } = default!; + [Inject] private IServiceProvider _serviceProvider { get; set; } = default!; @@ -62,21 +65,47 @@ protected override async Task OnInitializedAsync() await base.OnInitializedAsync(); } + private static string? _script; + private static SemaphoreSlim _semaphore = new(1, 1); + private async Task GetMarkedJSScript() + { + if (_script is not null) + return _script; + try + { + await _semaphore.WaitAsync(); + if (_script is not null) + return _script; + var scriptPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "marked", "marked-15.0.7.js"); + if (File.Exists(scriptPath) is false) + { + var envType = Type.GetType("Microsoft.AspNetCore.Hosting.IWebHostEnvironment, Microsoft.AspNetCore.Hosting.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60")!; + var fileProviderProp = envType.GetProperty("ContentRootFileProvider", BindingFlags.Instance | BindingFlags.Public)!; + var webRootPathProp = envType.GetProperty("WebRootPath")!; + var env = _serviceProvider.GetRequiredService(envType); + var webRootPath = (string)webRootPathProp.GetValue(env)!; + scriptPath = Path.Combine(webRootPath, "marked", "marked-15.0.7.js"); + } + return _script = await File.ReadAllTextAsync(scriptPath); + } + finally + { + _semaphore.Release(); + } + } + private async Task RunJint() { if (Markdown.HasNoValue()) return; await Task.Run(async () => { - var scriptPath = Path.Combine(Environment.CurrentDirectory, "..", "..", "Bit.BlazorUI.Extras", "wwwroot", "marked", "marked-15.0.7.js"); - var script = await File.ReadAllTextAsync(scriptPath); - using var engine = new Engine(options => { options.Strict(); options.CancellationToken(_cts.Token); options.Culture(CultureInfo.CurrentUICulture); - }).Execute(script); + }).Execute(await GetMarkedJSScript()); var fn = engine.Evaluate("marked.parse").AsFunctionInstance(); diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index 976d2afdf4..269fd248cc 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -50,4 +50,10 @@ + + + PreserveNewest + + + From f618ac5f2ab9ad756380221ce85c70caba12435e Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Sun, 9 Mar 2025 15:19:52 +0100 Subject: [PATCH 11/14] fix --- .../MarkdownViewer/BitMarkdownViewer.razor.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 5958bcebd7..92eaea994b 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -73,18 +73,13 @@ private async Task GetMarkedJSScript() return _script; try { - await _semaphore.WaitAsync(); + await _semaphore.WaitAsync(_cts.Token); if (_script is not null) return _script; - var scriptPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "marked", "marked-15.0.7.js"); + var scriptPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "_content", "Bit.BlazorUI.Extras", "marked", "marked-15.0.7.js"); if (File.Exists(scriptPath) is false) { - var envType = Type.GetType("Microsoft.AspNetCore.Hosting.IWebHostEnvironment, Microsoft.AspNetCore.Hosting.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60")!; - var fileProviderProp = envType.GetProperty("ContentRootFileProvider", BindingFlags.Instance | BindingFlags.Public)!; - var webRootPathProp = envType.GetProperty("WebRootPath")!; - var env = _serviceProvider.GetRequiredService(envType); - var webRootPath = (string)webRootPathProp.GetValue(env)!; - scriptPath = Path.Combine(webRootPath, "marked", "marked-15.0.7.js"); + scriptPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "marked", "marked-15.0.7.js"); } return _script = await File.ReadAllTextAsync(scriptPath); } From 4707482a4a1f7f98238d9cf192d701362cda95a0 Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Sun, 9 Mar 2025 15:22:04 +0100 Subject: [PATCH 12/14] fix --- .../Components/MarkdownViewer/BitMarkdownViewer.razor.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index 92eaea994b..fe93c3bf2e 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -56,10 +56,7 @@ protected override async Task OnInitializedAsync() await _js.BitExtrasInitScripts([scriptPath]); } - if (_html.HasNoValue()) - { - await ParseAndRender(); - } + await ParseAndRender(); } await base.OnInitializedAsync(); From 2c5313bc14ed27821f6df63c58f95c19b8372dba Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 9 Mar 2025 20:52:04 +0330 Subject: [PATCH 13/14] fix final issues --- .../Components/Chart/BitChart.razor.cs | 9 +-- .../MarkdownViewer/BitMarkdownViewer.razor.cs | 74 ++++++++++--------- .../MarkdownViewer/BitMarkdownViewer.ts | 7 +- .../BitMarkdownViewerJsRuntimeExtensions.cs | 5 +- .../JsInterop/ExtrasJsRuntimeExtensions.cs | 2 +- .../Bit.BlazorUI.Extras/Scripts/Extras.ts | 27 +++---- .../JsInterop/IJSRuntimeExtensions.cs | 18 ++--- .../JsInterop/IJSRuntimeFastExtensions.cs | 7 +- 8 files changed, 71 insertions(+), 78 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs index b20ff0303a..54c5887532 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs @@ -1,6 +1,7 @@ // a fork from https://github.com/mariusmuntean/ChartJs.Blazor using System.Diagnostics.CodeAnalysis; +using Acornima.Ast; namespace Bit.BlazorUI; @@ -130,20 +131,18 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - var scripts = new List { "_content/Bit.BlazorUI.Extras/chart.js/chartjs-2.9.4.js" }; + await _js.BitExtrasInitScripts(["_content/Bit.BlazorUI.Extras/chart.js/chartjs-2.9.4.js"]); if (IsDateAdapterRequired && DateAdapterScripts is null) { - scripts.Add("_content/Bit.BlazorUI.Extras/chart.js/chartjs-2.9.4-adapter.js"); + await _js.BitExtrasInitScripts(["_content/Bit.BlazorUI.Extras/chart.js/chartjs-2.9.4-adapter.js"]); } if (DateAdapterScripts is not null) { - scripts.AddRange(DateAdapterScripts); + await _js.BitExtrasInitScripts(DateAdapterScripts); } - await _js.BitExtrasInitScripts(scripts); - if (Config is not null) { await _js.BitChartJsSetupChart(Config); diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs index fe93c3bf2e..3dcd18dd05 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -1,7 +1,5 @@ -using Jint; -using System.Reflection; -using System.Globalization; -using Microsoft.Extensions.DependencyInjection; +using System.Globalization; +using Jint; namespace Bit.BlazorUI; @@ -12,12 +10,13 @@ namespace Bit.BlazorUI; public partial class BitMarkdownViewer : BitComponentBase { private string? _html; - private CancellationTokenSource _cts = new(); + private static string? _markedScriptText; + private readonly CancellationTokenSource _cts = new(); + private static readonly SemaphoreSlim _markedScriptReadTextSemaphore = new(1, 1); [Inject] private IJSRuntime _js { get; set; } = default!; - [Inject] private IServiceProvider _serviceProvider { get; set; } = default!; @@ -41,7 +40,7 @@ protected override async Task OnInitializedAsync() } catch (FileNotFoundException ex) when (ex.FileName?.StartsWith("Jint") is true) { - Console.Error.WriteLine("Please install `Jint` nuget package on your SERVER project."); + Console.Error.WriteLine("Please install `Jint` nuget package on the server project."); } catch (Exception ex) { @@ -51,7 +50,7 @@ protected override async Task OnInitializedAsync() else { var scriptPath = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"; - if (await _js.BitMarkdownViewerCheckScript(scriptPath) is false) + if ((await _js.BitMarkdownViewerCheckScriptLoaded(scriptPath)) is false) { await _js.BitExtrasInitScripts([scriptPath]); } @@ -62,29 +61,7 @@ protected override async Task OnInitializedAsync() await base.OnInitializedAsync(); } - private static string? _script; - private static SemaphoreSlim _semaphore = new(1, 1); - private async Task GetMarkedJSScript() - { - if (_script is not null) - return _script; - try - { - await _semaphore.WaitAsync(_cts.Token); - if (_script is not null) - return _script; - var scriptPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "_content", "Bit.BlazorUI.Extras", "marked", "marked-15.0.7.js"); - if (File.Exists(scriptPath) is false) - { - scriptPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "marked", "marked-15.0.7.js"); - } - return _script = await File.ReadAllTextAsync(scriptPath); - } - finally - { - _semaphore.Release(); - } - } + private async Task RunJint() { @@ -92,22 +69,53 @@ private async Task RunJint() await Task.Run(async () => { + await ReadMarkedScriptText(); + if (_markedScriptText.HasNoValue()) return; + using var engine = new Engine(options => { options.Strict(); options.CancellationToken(_cts.Token); options.Culture(CultureInfo.CurrentUICulture); - }).Execute(await GetMarkedJSScript()); + }).Execute(_markedScriptText!); var fn = engine.Evaluate("marked.parse").AsFunctionInstance(); _html = fn.Call(Markdown).AsString(); await InvokeAsync(StateHasChanged); - }, _cts.Token); } + private async Task ReadMarkedScriptText() + { + if (_markedScriptText is not null) return _markedScriptText; + + try + { + await _markedScriptReadTextSemaphore.WaitAsync(_cts.Token); + if (_markedScriptText is not null) return _markedScriptText; + + var scriptPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "_content", "Bit.BlazorUI.Extras", "marked", "marked-15.0.7.js"); + + if (File.Exists(scriptPath) is false) + { + scriptPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "marked", "marked-15.0.7.js"); + } + + if (File.Exists(scriptPath) is false) + { + return _markedScriptText = string.Empty; + } + + return _markedScriptText = await File.ReadAllTextAsync(scriptPath); + } + finally + { + _markedScriptReadTextSemaphore.Release(); + } + } + private async Task OnMarkdownSet() { if (IsRendered is false) return; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts index 2ef8729040..54a0c7eae3 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -1,10 +1,7 @@ namespace BitBlazorUI { export class MarkdownViewer { - private static _initPromise?: Promise; - - public static checkScript(script: string) { - const allScripts = Array.from(document.scripts).map(s => s.src); - return !!allScripts.find(as => as.includes(script)); + public static checkScriptLoaded(script: string) { + return window.marked !== undefined; } public static parse(md: string) { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs index f29c704934..6d7a1ca506 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -1,11 +1,10 @@ namespace Bit.BlazorUI; -[System.Diagnostics.CodeAnalysis.SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] internal static class BitMarkdownViewerJsRuntimeExtensions { - public static ValueTask BitMarkdownViewerCheckScript(this IJSRuntime jsRuntime, string script) + public static ValueTask BitMarkdownViewerCheckScriptLoaded(this IJSRuntime jsRuntime, string script) { - return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.checkScript", script); + return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.checkScriptLoaded", script); } public static ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs index 42a18342c9..e1014438c2 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs @@ -19,6 +19,6 @@ internal static ValueTask BitExtrasScrollBy(this IJSRuntime jsRuntime, ElementRe public static ValueTask BitExtrasInitScripts(this IJSRuntime jsRuntime, IEnumerable scripts, bool isModule = false) { - return jsRuntime.InvokeVoid("BitBlazorUI.Extras.initScript", scripts, isModule); + return jsRuntime.InvokeVoid("BitBlazorUI.Extras.initScripts", scripts, isModule); } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts index b137e343bb..c810493629 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts @@ -19,33 +19,28 @@ namespace BitBlazorUI { element.scrollBy(x, y); } - private static _initScriptPromises: { [key: string]: Promise } = {}; - public static async initScript(scripts: string[], isModule: boolean) { + private static _initScriptsPromises: { [key: string]: Promise } = {}; + public static async initScripts(scripts: string[], isModule: boolean) { const key = scripts.join('|'); - if (Extras._initScriptPromises[key] !== undefined) { - return await Extras._initScriptPromises[key]; + if (Extras._initScriptsPromises[key] !== undefined) { + return Extras._initScriptsPromises[key]; } const allScripts = Array.from(document.scripts).map(s => s.src); - const notAddedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s))); + const notAddedScripts = scripts.filter(s => !allScripts.find(as => as.includes(s))); if (notAddedScripts.length == 0) return Promise.resolve(); - const promise = new Promise((resolve: any, reject: any) => { + const promise = new Promise(async (res: any, rej: any) => { try { - (async function loadScripts() { - try { - await Promise.all(notAddedScripts.map(addScript)); - resolve(); - } catch (e: any) { - reject(e); - } - }()); + await Promise.all(notAddedScripts.map(addScript)); + res(); } catch (e: any) { - reject(e); + rej(e); } }); - Extras._initScriptPromises[key] = promise; + + Extras._initScriptsPromises[key] = promise; return promise; async function addScript(url: string) { diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs index fc58e0b85d..90dec31b50 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs @@ -12,31 +12,31 @@ public static class IJSRuntimeExtensions /// /// Only tries to Invoke the js call when the runtime is valid. /// - public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) + public static ValueTask InvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { - if (jsRuntime.IsRuntimeInvalid()) return; + if (jsRuntime.IsRuntimeInvalid()) return ValueTask.CompletedTask; - await jsRuntime.InvokeVoidAsync(identifier, args); + return jsRuntime.InvokeVoidAsync(identifier, args); } /// /// Only tries to Invoke the js call when the runtime is valid. /// - public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) + public static ValueTask InvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { - if (jsRuntime.IsRuntimeInvalid()) return; + if (jsRuntime.IsRuntimeInvalid()) return ValueTask.CompletedTask; - await jsRuntime.InvokeVoidAsync(identifier, timeout, args); + return jsRuntime.InvokeVoidAsync(identifier, timeout, args); } /// /// Only tries to Invoke the js call when the runtime is valid. /// - public static async ValueTask InvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) + public static ValueTask InvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { - if (jsRuntime.IsRuntimeInvalid()) return; + if (jsRuntime.IsRuntimeInvalid()) return ValueTask.CompletedTask; - await jsRuntime.InvokeVoidAsync(identifier, cancellationToken, args); + return jsRuntime.InvokeVoidAsync(identifier, cancellationToken, args); } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs index 7cafdad60e..3c018f9ab0 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs @@ -4,6 +4,7 @@ namespace Bit.BlazorUI; +[SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "", Scope = "member", Target = "~M:Bit.BlazorUI.IJSRuntimeFastExtensions.FastInvokeVoid(Microsoft.JSInterop.IJSRuntime,System.String,System.Threading.CancellationToken,System.Object[])~System.Threading.Tasks.ValueTask")] public static class IJSRuntimeFastExtensions { public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties; @@ -15,7 +16,6 @@ public static class IJSRuntimeFastExtensions /// /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. - [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { return FastInvokeVoid(jsRuntime, identifier, CancellationToken.None, args); @@ -27,7 +27,6 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// The duration after which to cancel the async operation. Overrides default timeouts (). /// JSON-serializable arguments. - [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); @@ -45,7 +44,6 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// () from being applied. /// /// JSON-serializable arguments. - [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) @@ -77,7 +75,6 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. - [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, params object?[]? args) { return FastInvoke(jsRuntime, identifier, CancellationToken.None, args); @@ -92,7 +89,6 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// The duration after which to cancel the async operation. Overrides default timeouts (). /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. - [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object?[]? args) { using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); @@ -113,7 +109,6 @@ public static ValueTask FastInvokeVoid(this IJSRuntime jsRuntime, string identif /// /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. - [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static ValueTask FastInvoke<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object?[]? args) { if (jsRuntime is IJSInProcessRuntime jsInProcessRuntime) From 95af1c671ba5e274a59daea81ff86e5a37559aed Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 9 Mar 2025 21:04:16 +0330 Subject: [PATCH 14/14] fix review comments --- .../Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs | 1 - .../Components/DataGrid/BitDataGrid.razor.cs | 4 +++- .../Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs index 54c5887532..d3d809f266 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs @@ -1,7 +1,6 @@ // a fork from https://github.com/mariusmuntean/ChartJs.Blazor using System.Diagnostics.CodeAnalysis; -using Acornima.Ast; namespace Bit.BlazorUI; diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs index 3410e2ee8f..472541bba3 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs @@ -1,4 +1,6 @@ -namespace Bit.BlazorUI; +// a fork from the Blazor QuickGrid at https://github.com/dotnet/aspnetcore/tree/main/src/Components/QuickGrid + +namespace Bit.BlazorUI; /// /// BitDataGrid is a robust way to display an information-rich collection of items, and allow people to sort, and filter the content. diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs index 90dec31b50..ad822d6354 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs @@ -1,5 +1,5 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; +using System.Reflection; +using System.Diagnostics.CodeAnalysis; namespace Bit.BlazorUI;