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..d3d809f266 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs @@ -130,20 +130,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.BitChartJsInitChartJs(scripts); - if (Config is not null) { await _js.BitChartJsSetupChart(Config); 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/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.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor new file mode 100644 index 0000000000..7ce97e2877 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor @@ -0,0 +1,11 @@ +@namespace Bit.BlazorUI +@inherits BitComponentBase + +
+ @((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 new file mode 100644 index 0000000000..3dcd18dd05 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.razor.cs @@ -0,0 +1,146 @@ +using System.Globalization; +using Jint; + +namespace Bit.BlazorUI; + +/// +/// BitMarkdownViewer is a SEO friendly Blazor wrapper around the famous markedjs library. +/// +/// +public partial class BitMarkdownViewer : BitComponentBase +{ + private string? _html; + private static string? _markedScriptText; + private readonly CancellationTokenSource _cts = new(); + private static readonly SemaphoreSlim _markedScriptReadTextSemaphore = new(1, 1); + + + + [Inject] private IJSRuntime _js { get; set; } = default!; + + + + /// + /// 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 OnInitializedAsync() + { + if (_js.IsRuntimeInvalid()) // prerendering + { + try + { + await RunJint(); + } + catch (FileNotFoundException ex) when (ex.FileName?.StartsWith("Jint") is true) + { + Console.Error.WriteLine("Please install `Jint` nuget package on the server project."); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.Message); + } + } + else + { + var scriptPath = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"; + if ((await _js.BitMarkdownViewerCheckScriptLoaded(scriptPath)) is false) + { + await _js.BitExtrasInitScripts([scriptPath]); + } + + await ParseAndRender(); + } + + await base.OnInitializedAsync(); + } + + + + private async Task RunJint() + { + if (Markdown.HasNoValue()) return; + + 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(_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; + + await ParseAndRender(); + } + + private async Task ParseAndRender() + { + if (Markdown.HasNoValue()) return; + + _html = await _js.BitMarkdownViewerParse(Markdown!); + + 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.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss new file mode 100644 index 0000000000..40a3af3a40 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.scss @@ -0,0 +1,9 @@ +@import '../../../Bit.BlazorUI/Styles/functions.scss'; + +.bit-mdv { + all: revert; + + * { + all: revert; + } +} 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..54a0c7eae3 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts @@ -0,0 +1,17 @@ +namespace BitBlazorUI { + export class MarkdownViewer { + public static checkScriptLoaded(script: string) { + return window.marked !== undefined; + } + + public static parse(md: string) { + const html = marked.parse(md, { async: false }); + return html; + } + + public static async parseAsync(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 new file mode 100644 index 0000000000..6d7a1ca506 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs @@ -0,0 +1,16 @@ +namespace Bit.BlazorUI; + +internal static class BitMarkdownViewerJsRuntimeExtensions +{ + public static ValueTask BitMarkdownViewerCheckScriptLoaded(this IJSRuntime jsRuntime, string script) + { + return jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.checkScriptLoaded", script); + } + + public static ValueTask BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown) + { + return OperatingSystem.IsBrowser() + ? jsRuntime.FastInvoke("BitBlazorUI.MarkdownViewer.parse", markdown) + : jsRuntime.Invoke("BitBlazorUI.MarkdownViewer.parseAsync", markdown); + } +} 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/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..e1014438c2 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.initScripts", scripts, isModule); + } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts index e438769f3f..c810493629 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts @@ -18,5 +18,43 @@ namespace BitBlazorUI { element.scrollBy(x, y); } + + private static _initScriptsPromises: { [key: string]: Promise } = {}; + public static async initScripts(scripts: string[], isModule: boolean) { + const key = scripts.join('|'); + 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.includes(s))); + + if (notAddedScripts.length == 0) return Promise.resolve(); + + const promise = new Promise(async (res: any, rej: any) => { + try { + await Promise.all(notAddedScripts.map(addScript)); + res(); + } catch (e: any) { + rej(e); + } + }); + + Extras._initScriptsPromises[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.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/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})); diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs index 9087399658..ad822d6354 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs @@ -1,37 +1,42 @@ -using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Diagnostics.CodeAnalysis; 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. /// - 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); } @@ -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..3c018f9ab0 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs @@ -0,0 +1,131 @@ +using System.Text.Json; +using System.Diagnostics.CodeAnalysis; +using Microsoft.JSInterop.Infrastructure; + +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; + + + + /// + /// 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. + 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. + 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. + 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. + 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. + 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. + 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..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 @@ -1,4 +1,4 @@ - + net9.0 @@ -13,6 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -49,4 +50,10 @@ + + + PreserveNewest + + + 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..0bef6e26a6 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,18 @@ @if (HttpContext.Request.IsLightHouseRequest() is false) { - - - + if (AppRenderMode.PrerenderEnabled) + { + + } + + + - - - - + + + + } 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..4648528560 --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor @@ -0,0 +1,28 @@ +@page "/components/markdownviewer" + + + + + + + + + + + + + + + + +
    + +
    +
    +
    +
    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..b1c65fe0d5 --- /dev/null +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs @@ -0,0 +1,182 @@ +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..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,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", Description = "MdViewer, MD" }, 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",