# FluidMarkdown **Repository Path**: mirrors/FluidMarkdown ## Basic Information - **Project Name**: FluidMarkdown - **Description**: FluidMarkdown 是专为智能化业务场景打造的移动端原生 Markdown 渲染引擎,它能够轻松应对大模型的逐字输出,并为开发者提供高度可定制的交互与视觉表现,支持 iOS - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: https://www.oschina.net/p/fluidmarkdown - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-26 - **Last Updated**: 2025-12-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # FluidMarkdown We are committed to enabling the streaming rendering of Markdown content generated by large language models on the client side within AI-driven business applications. # Overview This library is designed for native Android, iOS and HarmonyOS developers. Built on the open-source CommonMark parsing library, it supports core Markdown syntax and selected HTML tags, rendering them progressively within UI components. It exposes Markdown styling as a structured model, enabling customization and integration into your specific application contexts. For faster integration, refer to the sample code to preview the rendering effect based on input text. # Features + Support for markdown syntax: titles, paragraphs, ordered lists, unordered lists, tables, code blocks, mathematical formulas, inline code blocks, quotes, dividing lines, footnotes, links, and images. + Support for HTML tags:`` `` `` `` `` `` `` `` `` `` ``, etc. + Streaming rendering and one-time full rendering modes. + Customizable rendering styles for Markdown syntax. + Adjustable streaming speed via custom parameters. + Event support for clickable elements, including click handling, visibility callbacks, and rendering status updates, etc. + Added some new extended HTML tags such as `` ``in `AMHTMLTransformer` class. # Install The source code of this project is open-source. For information on how to download and run the project, please refer to the file [INSTALL](https://github.com/antgroup/FluidMarkdown/blob/main/INSTALL.md). # Directory structure ## iOS + AntMarkdown —— a standard markdown parser and rendering module based on commonMark. + FluidMarkdown —— Component for streaming output. ## Android + fluid-markdown —— Component for streaming output. + markwon-xxx —— Syntax parsing and style rendering implementation based on the open-source markwon library. ## HarmonyOS + markdown —— the folder contains all the source code for the markdown component, such as syntax parsing, layout rendering, theme configuration, runtime services, etc. + playground —— a demo to show markdown component feature list. ``` shell your/project/markdown/src/main/ets ├── engine // [folder] engine manages all services and plugins at runtime. ├── index.ets // [file] default exports. ├── markdown.ets // [file] markdown component compliant with the @ComponentV2 specification. ├── render // [folder] rendering-related logic based on the StyledString mechanism. ├── service // [folder] runtime service modules, such as syntax parsing, code highlighting, etc. ├── theme // [folder] style and theme-related logic based on the StyledString mechanism. └── util // [folder] built-in utility api. ``` # Usage ## iOS + Native API: `AMXMarkdownWidget.h` is summary header file of the open API. The following is the general calling process of streaming rendering components. 1. Create a TextView instance. 2. Generate default styles and set custom styles ( if needed ) 3. Begin streaming the Markdown content. 4. Append data dynamically during rendering. 5. Adjust list scrolling in response to content size changes in the TextView. 6. Handle completion when rendering is finished. ```objectivec AMXMarkdownTextView* contentTextView = [[AMXMarkdownTextView alloc] initWithFrame_ant_mark:CGRectMake(0, 0, screenWidht - 20 * 2, 1)]; // get default style config AMXMarkdownStyleConfig* config = [AMXMarkdownStyleConfig defaultConfig]; // modify code block style for example config.codeBlockConfig.backgroundColor = [UIColor greenColor]; // set the style with unique Id [[AMXRenderService shared] setMarkdownStyleWithId:config styleId:@"demo"]; // begin print [self.contentTextView startStreamingWithContent:@"testing data"]; // append content during printing [self.contentTextView addStreamContent:@"**append test data**"]; // stop print when you need [self.contentTextView stop]; ``` ```objectivec @interface StreamPreviewViewController () -(void)initUI { // set delegate self.contentTextView.textViewDelegate = self; } // size change delegate -(void)onSizeChange:(CGSize)size { // adjust size of AMXMarkdownTextView and container view [self.contentTextView setFrame:CGRectMake(0, 0, self.contentTextView.frame.size.width, size.height)]; [self.containerView setContentSize:size]; CGPoint bottomOffset = CGPointMake(0, self.containerView.contentSize.height - self.containerView.bounds.size.height); if (bottomOffset.y > 0) { // scroll the container view to bottom [self.containerView setContentOffset:bottomOffset animated:NO]; } } ``` + Sample Description 1. The `StreamPreviewViewController` class is a sample page for previewing streaming output. 2. The `AIChatViewController` class demonstrates FluidMarkdown usage in simulated AI conversation scenarios. Note that the conversation data is statically defined and intended solely for rendering demonstration purposes. ![StreamingPreview](https://github.com/antgroup/FluidMarkdown/blob/main/media/StreamViewController.gif)![AIChat](https://github.com/antgroup/FluidMarkdown/blob/main/media/AIChatViewController.gif) ## Android 1. Initialize by calling AFMInitializer.init(context, backgroundTaskHandler, imageHandler, logHandler) once globally. Except for context, all other parameters can be null. 2. Create PrinterMarkDownTextView to display markdown content. 3. Create MarkdownStyles to set render styles. 4. **Call PrinterMarkDownTextView.init()** to bind MarkdownStyles and ElementClickEventCallback. Must be called, MarkdownStyles cannot be null. 5. Set markdown content or call the print start method. ```java AFMInitializer.init(context, null, null, null); // Create PrinterMarkDownTextView PrinterMarkDownTextView markdownTextView = findViewById(R.id.markdown_view); // Create MarkdownStyles,or you can also create custom styles with new MarkdownStyles() MarkdownStyles styles = MarkdownStyles.getDefaultStyles(); // set style sample styles.linkStyle().icon("https://you_image_url"); styles.setTitleStyle(0, TitleStyle.create(1.5f).icon(https://you_image_url));// Set title level 1 style // bind MarkdownStyles and ElementClickEventCallback. markdownTextView.init(styles, elementClickEventCallback); // Set markdown content or you can call the startPrinting(content) to starting printing. markdownTextView.setMarkdownText(markdown); ``` + Sample Description - MainActivity - Markdown normal mode sample. - PrinterActivity - Streaming print sample. - ListActivity - Streaming print list sample. ![print](https://github.com/antgroup/FluidMarkdown/blob/main/media/android-print.gif)![list](https://github.com/antgroup/FluidMarkdown/blob/main/media/android-list.gif) ## HarmonyOS ### Combine Markdown + Import and combine the Markdown component in your page `build()` method. + Bind markdown-formatted content text via the `@Param content` parameter. + Handle various callback events inside the Markdown component to enhance the business interaction flow, such as `@Event onMarkdownAreaChange`, `@Event onMarkdownNodeClick`, etc. ``` ts import { Markdown, EMarkdownMode } from 'fluid-markdown'; @ComponentV2 export struct MyComponent { @Param content: string; private scroller: Scroller = new Scroller(); build() { Scroll(this.scroller) { Markdown({ content: this.content, mode: EMarkdownMode.Normal, onMarkdownAreaChange: () => {}, onMarkdownNodeClick: data => {}, }) .margin(12) } .width('100%') .height('100%') .scrollable(ScrollDirection.Vertical) .margin(12) } } ``` ### Streaming Output + Enable streaming output mode by setting the `@Param` mode parameter to `EMarkdownMode.Typing`. + Create a `MarkdownController` instance and bind it to the `@Param controller` parameter to manage the streaming output process, such as `update()`, `pause()`, `resume()`, etc. + Note: Control methods of `MarkdownController`, such as `update()`, can only be reliably executed within the `@Event onMarkdownTypingReady` callback event. + Note: The `@Param content` parameter will be ignored in streaming output mode. ``` ts import { Markdown, EMarkdownMode, MarkdownController, ETypingMode, } from 'fluid-markdown'; @ComponentV2 export struct MyComponent { private scroller: Scroller = new Scroller(); private markdownController: MarkdownController = new MarkdownController(); build() { Scroll(this.scroller) { Markdown({ controller: this.markdownController, mode: EMarkdownMode.Typing, onMarkdownTypingReady: () => { this.markdownController.typing.update('Hello FluidMarkdown', ETypingMode.Begin); }, }) .margin(12) } .width('100%') .height('100%') .scrollable(ScrollDirection.Vertical) .margin(12) } } ``` ### Theme Config + Create a new Engine instance and bind it to the `@Param engine` parameter. + Set `ITheme` properties for theme styling through the `theme service` within the Engine. ``` ts import { Markdown, EMarkdownMode, BaseEngine } from 'fluid-markdown'; @ComponentV2 export struct MyComponent { @Param content: string; private scroller: Scroller = new Scroller(); private engine: BaseEngine = new BaseEngine(); aboutToAppear() { this.engine.theme!.theme!.document!.font!.fontColor = Color.Red; } build() { Scroll(this.scroller) { Markdown({ engine: this.engine, content: this.content, mode: EMarkdownMode.Normal, onMarkdownAreaChange: () => {}, onMarkdownNodeClick: data => {}, }) .margin(12) } .width('100%') .height('100%') .scrollable(ScrollDirection.Vertical) .margin(12) } } ``` ### Playground Build and run the Playground app to try out FluidMarkdown—have fun! ![fluid-markdown-ohos-playground](https://mdn.alipayobjects.com/huamei_iobbj9/afts/img/A*uca1RLdcS90AAAAAgEAAAAgAetF-AQ/original) # Known Issues + Clickable elements within tables appear as plain text and are not interactive. + Nested HTML tags within table cells are not supported. + Tables on Android may overflow their container and do not support horizontal scrolling. + LaTex capability on the HarmonyOS platform is under development and not yet available. + The minimum required HarmonyOS API version is 15 or higher. # Contribute The FluidMarkdown team welcomes individual or team contributions. For more informations, please refer to the file [CONTRIBUTING](https://github.com/antgroup/FluidMarkdown/blob/main/CONTRIBUTING.md). # Licensing All source code is licensed under the Apache 2.0 license. For details, please refer to [LICENSE](https://github.com/antgroup/FluidMarkdown/blob/main/LICENSE). We acknowledge the following open-source projects: + [noties/Markwon](https://github.com/noties/Markwon) - license: Apache-2.0 + [indragiek/CocoaMarkdown](https://github.com/indragiek/CocoaMarkdown) - license: MIT License + [commonmark/commonmark-spec](https://commonmark.org/) - license: MIT License + [https://github.com/max-lfeng/iosMath/](https://github.com/max-lfeng/iosMath/) - license: MIT License + [https://github.com/mattt/Ono/](https://github.com/mattt/Ono/) - license: MIT License + [markdown-it](https://github.com/markdown-it/markdown-it/blob/master/LICENSE) - license: MIT License + [highlight.js](https://github.com/highlightjs/highlight.js/blob/main/LICENSE) - license: BSD 3-Clause License + [csstree](https://github.com/csstree/csstree/blob/master/LICENSE) - license: MIT License + [htmlparser2](https://github.com/fb55/htmlparser2/blob/master/LICENSE) - license: MIT License # Acknowledgements and References + All developers who have contributed code to the Ant Markdown component + Thanks to the following open source projects: - [noties/Markwon](https://github.com/noties/Markwon) - [indragiek/CocoaMarkdown](https://github.com/indragiek/CocoaMarkdown) - [commonmark/commonmark-spec](https://commonmark.org/) - [https://github.com/max-lfeng/iosMath/](https://github.com/max-lfeng/iosMath/) - [https://github.com/mattt/Ono/](https://github.com/mattt/Ono/) - [markdown-it](https://github.com/markdown-it/markdown-it/) - [highlight.js](https://github.com/highlightjs/highlight.js/) - [csstree](https://github.com/csstree/csstree/) - [htmlparser2](https://github.com/fb55/htmlparser2/)