react-native-nitro-markdown 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +605 -318
- package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +27 -8
- package/cpp/bindings/HybridMarkdownParser.cpp +216 -66
- package/cpp/bindings/HybridMarkdownParser.hpp +2 -0
- package/ios/HybridMarkdownSession.swift +33 -7
- package/lib/commonjs/MarkdownContext.js +2 -1
- package/lib/commonjs/MarkdownContext.js.map +1 -1
- package/lib/commonjs/headless.js +41 -5
- package/lib/commonjs/headless.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/markdown-stream.js +109 -13
- package/lib/commonjs/markdown-stream.js.map +1 -1
- package/lib/commonjs/markdown.js +215 -44
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/code.js +4 -3
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/heading.js +1 -1
- package/lib/commonjs/renderers/heading.js.map +1 -1
- package/lib/commonjs/renderers/image.js +7 -5
- package/lib/commonjs/renderers/image.js.map +1 -1
- package/lib/commonjs/renderers/link.js +15 -3
- package/lib/commonjs/renderers/link.js.map +1 -1
- package/lib/commonjs/renderers/list.js +2 -2
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/table.js +126 -21
- package/lib/commonjs/renderers/table.js.map +1 -1
- package/lib/commonjs/use-markdown-stream.js +16 -14
- package/lib/commonjs/use-markdown-stream.js.map +1 -1
- package/lib/commonjs/utils/incremental-ast.js +153 -0
- package/lib/commonjs/utils/incremental-ast.js.map +1 -0
- package/lib/commonjs/utils/link-security.js +21 -0
- package/lib/commonjs/utils/link-security.js.map +1 -0
- package/lib/commonjs/utils/stream-timeline.js +62 -0
- package/lib/commonjs/utils/stream-timeline.js.map +1 -0
- package/lib/module/MarkdownContext.js +2 -1
- package/lib/module/MarkdownContext.js.map +1 -1
- package/lib/module/headless.js +37 -4
- package/lib/module/headless.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown-stream.js +110 -14
- package/lib/module/markdown-stream.js.map +1 -1
- package/lib/module/markdown.js +217 -46
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/blockquote.js.map +1 -1
- package/lib/module/renderers/code.js +4 -3
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/heading.js +1 -1
- package/lib/module/renderers/heading.js.map +1 -1
- package/lib/module/renderers/image.js +7 -5
- package/lib/module/renderers/image.js.map +1 -1
- package/lib/module/renderers/link.js +15 -3
- package/lib/module/renderers/link.js.map +1 -1
- package/lib/module/renderers/list.js +2 -2
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/paragraph.js.map +1 -1
- package/lib/module/renderers/table.js +127 -22
- package/lib/module/renderers/table.js.map +1 -1
- package/lib/module/use-markdown-stream.js +16 -14
- package/lib/module/use-markdown-stream.js.map +1 -1
- package/lib/module/utils/incremental-ast.js +147 -0
- package/lib/module/utils/incremental-ast.js.map +1 -0
- package/lib/module/utils/link-security.js +15 -0
- package/lib/module/utils/link-security.js.map +1 -0
- package/lib/module/utils/stream-timeline.js +56 -0
- package/lib/module/utils/stream-timeline.js.map +1 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts +5 -3
- package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/MarkdownContext.d.ts +26 -25
- package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +15 -2
- package/lib/typescript/commonjs/headless.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown-stream.d.ts +7 -2
- package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +62 -5
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts +5 -5
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +2 -2
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/image.d.ts +2 -2
- package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/link.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts +7 -7
- package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts +4 -4
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/table.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts +5 -2
- package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts +2 -2
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/use-markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/incremental-ast.d.ts +12 -0
- package/lib/typescript/commonjs/utils/incremental-ast.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/link-security.d.ts +3 -0
- package/lib/typescript/commonjs/utils/link-security.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/stream-timeline.d.ts +11 -0
- package/lib/typescript/commonjs/utils/stream-timeline.d.ts.map +1 -0
- package/lib/typescript/module/Markdown.nitro.d.ts +5 -3
- package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/module/MarkdownContext.d.ts +26 -25
- package/lib/typescript/module/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +15 -2
- package/lib/typescript/module/headless.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/markdown-stream.d.ts +7 -2
- package/lib/typescript/module/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +62 -5
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts +3 -3
- package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts +5 -5
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/heading.d.ts +3 -3
- package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts +2 -2
- package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/module/renderers/image.d.ts +2 -2
- package/lib/typescript/module/renderers/image.d.ts.map +1 -1
- package/lib/typescript/module/renderers/link.d.ts +3 -3
- package/lib/typescript/module/renderers/link.d.ts.map +1 -1
- package/lib/typescript/module/renderers/list.d.ts +7 -7
- package/lib/typescript/module/renderers/list.d.ts.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts +4 -4
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts +3 -3
- package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/module/renderers/table.d.ts +3 -3
- package/lib/typescript/module/renderers/table.d.ts.map +1 -1
- package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts +5 -2
- package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts +2 -2
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/lib/typescript/module/use-markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/utils/incremental-ast.d.ts +12 -0
- package/lib/typescript/module/utils/incremental-ast.d.ts.map +1 -0
- package/lib/typescript/module/utils/link-security.d.ts +3 -0
- package/lib/typescript/module/utils/link-security.d.ts.map +1 -0
- package/lib/typescript/module/utils/stream-timeline.d.ts +11 -0
- package/lib/typescript/module/utils/stream-timeline.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.cpp +18 -6
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +4 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/Func_void_double_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSessionSpec.kt +11 -3
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.hpp +31 -0
- package/nitrogen/generated/ios/c++/HybridMarkdownSessionSpecSwift.hpp +20 -2
- package/nitrogen/generated/ios/swift/Func_void.swift +0 -1
- package/nitrogen/generated/ios/swift/Func_void_double_double.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec.swift +4 -3
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec_cxx.swift +34 -10
- package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownSessionSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownSessionSpec.hpp +4 -2
- package/package.json +7 -5
- package/src/Markdown.nitro.ts +7 -3
- package/src/MarkdownContext.ts +31 -25
- package/src/headless.ts +44 -6
- package/src/index.ts +8 -0
- package/src/markdown-stream.tsx +159 -15
- package/src/markdown.tsx +406 -50
- package/src/renderers/blockquote.tsx +4 -4
- package/src/renderers/code.tsx +16 -10
- package/src/renderers/heading.tsx +4 -4
- package/src/renderers/horizontal-rule.tsx +3 -3
- package/src/renderers/image.tsx +11 -9
- package/src/renderers/link.tsx +25 -7
- package/src/renderers/list.tsx +9 -12
- package/src/renderers/math.tsx +4 -4
- package/src/renderers/paragraph.tsx +3 -3
- package/src/renderers/table.tsx +270 -98
- package/src/specs/MarkdownSession.nitro.ts +6 -2
- package/src/theme.ts +3 -3
- package/src/use-markdown-stream.ts +22 -16
- package/src/utils/incremental-ast.ts +224 -0
- package/src/utils/link-security.ts +22 -0
- package/src/utils/stream-timeline.ts +72 -0
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
|
-
import Foundation
|
|
9
8
|
import NitroModules
|
|
10
9
|
|
|
11
10
|
/// See ``HybridMarkdownSessionSpec``
|
|
@@ -14,10 +13,12 @@ public protocol HybridMarkdownSessionSpec_protocol: HybridObject {
|
|
|
14
13
|
var highlightPosition: Double { get set }
|
|
15
14
|
|
|
16
15
|
// Methods
|
|
17
|
-
func append(chunk: String) throws ->
|
|
16
|
+
func append(chunk: String) throws -> Double
|
|
18
17
|
func clear() throws -> Void
|
|
19
18
|
func getAllText() throws -> String
|
|
20
|
-
func
|
|
19
|
+
func getLength() throws -> Double
|
|
20
|
+
func getTextRange(from: Double, to: Double) throws -> String
|
|
21
|
+
func addListener(listener: @escaping (_ from: Double, _ to: Double) -> Void) throws -> () -> Void
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
public extension HybridMarkdownSessionSpec_protocol {
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
|
-
import Foundation
|
|
9
8
|
import NitroModules
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -135,13 +134,14 @@ open class HybridMarkdownSessionSpec_cxx {
|
|
|
135
134
|
|
|
136
135
|
// Methods
|
|
137
136
|
@inline(__always)
|
|
138
|
-
public final func append(chunk: std.string) -> bridge.
|
|
137
|
+
public final func append(chunk: std.string) -> bridge.Result_double_ {
|
|
139
138
|
do {
|
|
140
|
-
try self.__implementation.append(chunk: String(chunk))
|
|
141
|
-
|
|
139
|
+
let __result = try self.__implementation.append(chunk: String(chunk))
|
|
140
|
+
let __resultCpp = __result
|
|
141
|
+
return bridge.create_Result_double_(__resultCpp)
|
|
142
142
|
} catch (let __error) {
|
|
143
143
|
let __exceptionPtr = __error.toCpp()
|
|
144
|
-
return bridge.
|
|
144
|
+
return bridge.create_Result_double_(__exceptionPtr)
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -169,12 +169,36 @@ open class HybridMarkdownSessionSpec_cxx {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
@inline(__always)
|
|
172
|
-
public final func
|
|
172
|
+
public final func getLength() -> bridge.Result_double_ {
|
|
173
|
+
do {
|
|
174
|
+
let __result = try self.__implementation.getLength()
|
|
175
|
+
let __resultCpp = __result
|
|
176
|
+
return bridge.create_Result_double_(__resultCpp)
|
|
177
|
+
} catch (let __error) {
|
|
178
|
+
let __exceptionPtr = __error.toCpp()
|
|
179
|
+
return bridge.create_Result_double_(__exceptionPtr)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@inline(__always)
|
|
184
|
+
public final func getTextRange(from: Double, to: Double) -> bridge.Result_std__string_ {
|
|
185
|
+
do {
|
|
186
|
+
let __result = try self.__implementation.getTextRange(from: from, to: to)
|
|
187
|
+
let __resultCpp = std.string(__result)
|
|
188
|
+
return bridge.create_Result_std__string_(__resultCpp)
|
|
189
|
+
} catch (let __error) {
|
|
190
|
+
let __exceptionPtr = __error.toCpp()
|
|
191
|
+
return bridge.create_Result_std__string_(__exceptionPtr)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@inline(__always)
|
|
196
|
+
public final func addListener(listener: bridge.Func_void_double_double) -> bridge.Result_std__function_void____ {
|
|
173
197
|
do {
|
|
174
|
-
let __result = try self.__implementation.addListener(listener: { () -> () -> Void in
|
|
175
|
-
let __wrappedFunction = bridge.
|
|
176
|
-
return { () -> Void in
|
|
177
|
-
__wrappedFunction.call()
|
|
198
|
+
let __result = try self.__implementation.addListener(listener: { () -> (Double, Double) -> Void in
|
|
199
|
+
let __wrappedFunction = bridge.wrap_Func_void_double_double(listener)
|
|
200
|
+
return { (__from: Double, __to: Double) -> Void in
|
|
201
|
+
__wrappedFunction.call(__from, __to)
|
|
178
202
|
}
|
|
179
203
|
}())
|
|
180
204
|
let __resultCpp = { () -> bridge.Func_void in
|
|
@@ -16,6 +16,8 @@ namespace margelo::nitro::Markdown {
|
|
|
16
16
|
registerHybrids(this, [](Prototype& prototype) {
|
|
17
17
|
prototype.registerHybridMethod("parse", &HybridMarkdownParserSpec::parse);
|
|
18
18
|
prototype.registerHybridMethod("parseWithOptions", &HybridMarkdownParserSpec::parseWithOptions);
|
|
19
|
+
prototype.registerHybridMethod("extractPlainText", &HybridMarkdownParserSpec::extractPlainText);
|
|
20
|
+
prototype.registerHybridMethod("extractPlainTextWithOptions", &HybridMarkdownParserSpec::extractPlainTextWithOptions);
|
|
19
21
|
});
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -52,6 +52,8 @@ namespace margelo::nitro::Markdown {
|
|
|
52
52
|
// Methods
|
|
53
53
|
virtual std::string parse(const std::string& text) = 0;
|
|
54
54
|
virtual std::string parseWithOptions(const std::string& text, const ParserOptions& options) = 0;
|
|
55
|
+
virtual std::string extractPlainText(const std::string& text) = 0;
|
|
56
|
+
virtual std::string extractPlainTextWithOptions(const std::string& text, const ParserOptions& options) = 0;
|
|
55
57
|
|
|
56
58
|
protected:
|
|
57
59
|
// Hybrid Setup
|
|
@@ -19,6 +19,8 @@ namespace margelo::nitro::Markdown {
|
|
|
19
19
|
prototype.registerHybridMethod("append", &HybridMarkdownSessionSpec::append);
|
|
20
20
|
prototype.registerHybridMethod("clear", &HybridMarkdownSessionSpec::clear);
|
|
21
21
|
prototype.registerHybridMethod("getAllText", &HybridMarkdownSessionSpec::getAllText);
|
|
22
|
+
prototype.registerHybridMethod("getLength", &HybridMarkdownSessionSpec::getLength);
|
|
23
|
+
prototype.registerHybridMethod("getTextRange", &HybridMarkdownSessionSpec::getTextRange);
|
|
22
24
|
prototype.registerHybridMethod("addListener", &HybridMarkdownSessionSpec::addListener);
|
|
23
25
|
});
|
|
24
26
|
}
|
|
@@ -50,10 +50,12 @@ namespace margelo::nitro::Markdown {
|
|
|
50
50
|
|
|
51
51
|
public:
|
|
52
52
|
// Methods
|
|
53
|
-
virtual
|
|
53
|
+
virtual double append(const std::string& chunk) = 0;
|
|
54
54
|
virtual void clear() = 0;
|
|
55
55
|
virtual std::string getAllText() = 0;
|
|
56
|
-
virtual
|
|
56
|
+
virtual double getLength() = 0;
|
|
57
|
+
virtual std::string getTextRange(double from, double to) = 0;
|
|
58
|
+
virtual std::function<void()> addListener(const std::function<void(double /* from */, double /* to */)>& listener) = 0;
|
|
57
59
|
|
|
58
60
|
protected:
|
|
59
61
|
// Hybrid Setup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "High-performance Markdown parser for React Native using Nitro Modules and md4c",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -50,10 +50,11 @@
|
|
|
50
50
|
"!scripts"
|
|
51
51
|
],
|
|
52
52
|
"scripts": {
|
|
53
|
-
"prebuild": "
|
|
53
|
+
"prebuild": "bun run codegen",
|
|
54
54
|
"build": "bob build",
|
|
55
55
|
"clean": "rimraf lib nitrogen/generated",
|
|
56
56
|
"codegen": "nitrogen --logLevel=\"debug\"",
|
|
57
|
+
"lint": "eslint src --max-warnings=0 --ignore-pattern 'src/**/__tests__/**' --ignore-pattern 'src/**/*.nitro.ts'",
|
|
57
58
|
"typecheck": "tsc --noEmit",
|
|
58
59
|
"test": "jest",
|
|
59
60
|
"test:coverage": "jest --coverage",
|
|
@@ -86,10 +87,11 @@
|
|
|
86
87
|
"registry": "https://registry.npmjs.org/"
|
|
87
88
|
},
|
|
88
89
|
"devDependencies": {
|
|
89
|
-
"@types/react": "^19.2.
|
|
90
|
+
"@types/react": "^19.2.14",
|
|
90
91
|
"@types/react-native": "^0.73.0",
|
|
91
|
-
"react-
|
|
92
|
-
"react-native-
|
|
92
|
+
"@types/react-test-renderer": "^19.1.0",
|
|
93
|
+
"react-native-builder-bob": "^0.40.18",
|
|
94
|
+
"react-test-renderer": "^19.2.4",
|
|
93
95
|
"typescript": "^5.9.3"
|
|
94
96
|
},
|
|
95
97
|
"peerDependencies": {
|
package/src/Markdown.nitro.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import type { HybridObject } from
|
|
1
|
+
import type { HybridObject } from "react-native-nitro-modules";
|
|
2
2
|
|
|
3
3
|
export interface ParserOptions {
|
|
4
4
|
gfm?: boolean;
|
|
5
5
|
math?: boolean;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export interface MarkdownParser
|
|
9
|
-
|
|
8
|
+
export interface MarkdownParser extends HybridObject<{
|
|
9
|
+
ios: "c++";
|
|
10
|
+
android: "c++";
|
|
11
|
+
}> {
|
|
10
12
|
parse(text: string): string;
|
|
11
13
|
parseWithOptions(text: string, options: ParserOptions): string;
|
|
14
|
+
extractPlainText(text: string): string;
|
|
15
|
+
extractPlainTextWithOptions(text: string, options: ParserOptions): string;
|
|
12
16
|
}
|
package/src/MarkdownContext.ts
CHANGED
|
@@ -4,28 +4,28 @@ import {
|
|
|
4
4
|
type ReactNode,
|
|
5
5
|
type ComponentType,
|
|
6
6
|
} from "react";
|
|
7
|
+
import type { MarkdownNode } from "./headless";
|
|
7
8
|
import {
|
|
8
9
|
defaultMarkdownTheme,
|
|
9
10
|
type MarkdownTheme,
|
|
10
11
|
type NodeStyleOverrides,
|
|
11
12
|
type StylingStrategy,
|
|
12
13
|
} from "./theme";
|
|
13
|
-
import type { MarkdownNode } from "./headless";
|
|
14
14
|
|
|
15
|
-
export
|
|
15
|
+
export type NodeRendererProps = {
|
|
16
16
|
node: MarkdownNode;
|
|
17
17
|
depth: number;
|
|
18
18
|
inListItem: boolean;
|
|
19
19
|
parentIsText?: boolean;
|
|
20
|
-
}
|
|
20
|
+
};
|
|
21
21
|
|
|
22
|
-
export
|
|
22
|
+
export type BaseCustomRendererProps = {
|
|
23
23
|
node: MarkdownNode;
|
|
24
24
|
children: ReactNode;
|
|
25
25
|
Renderer: ComponentType<NodeRendererProps>;
|
|
26
|
-
}
|
|
26
|
+
};
|
|
27
27
|
|
|
28
|
-
export
|
|
28
|
+
export type EnhancedRendererProps = {
|
|
29
29
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
30
30
|
href?: string;
|
|
31
31
|
title?: string;
|
|
@@ -36,63 +36,69 @@ export interface EnhancedRendererProps extends BaseCustomRendererProps {
|
|
|
36
36
|
ordered?: boolean;
|
|
37
37
|
start?: number;
|
|
38
38
|
checked?: boolean;
|
|
39
|
-
}
|
|
39
|
+
} & BaseCustomRendererProps;
|
|
40
40
|
|
|
41
|
-
export
|
|
41
|
+
export type HeadingRendererProps = {
|
|
42
42
|
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
43
|
-
}
|
|
43
|
+
} & BaseCustomRendererProps;
|
|
44
44
|
|
|
45
|
-
export
|
|
45
|
+
export type LinkRendererProps = {
|
|
46
46
|
href: string;
|
|
47
47
|
title?: string;
|
|
48
|
-
}
|
|
48
|
+
} & BaseCustomRendererProps;
|
|
49
49
|
|
|
50
|
-
export
|
|
50
|
+
export type ImageRendererProps = {
|
|
51
51
|
url: string;
|
|
52
52
|
alt?: string;
|
|
53
53
|
title?: string;
|
|
54
|
-
}
|
|
54
|
+
} & BaseCustomRendererProps;
|
|
55
55
|
|
|
56
|
-
export
|
|
56
|
+
export type CodeBlockRendererProps = {
|
|
57
57
|
content: string;
|
|
58
58
|
language?: string;
|
|
59
|
-
}
|
|
59
|
+
} & BaseCustomRendererProps;
|
|
60
60
|
|
|
61
|
-
export
|
|
61
|
+
export type InlineCodeRendererProps = {
|
|
62
62
|
content: string;
|
|
63
|
-
}
|
|
63
|
+
} & BaseCustomRendererProps;
|
|
64
64
|
|
|
65
|
-
export
|
|
65
|
+
export type ListRendererProps = {
|
|
66
66
|
ordered: boolean;
|
|
67
67
|
start?: number;
|
|
68
|
-
}
|
|
68
|
+
} & BaseCustomRendererProps;
|
|
69
69
|
|
|
70
|
-
export
|
|
70
|
+
export type TaskListItemRendererProps = {
|
|
71
71
|
checked: boolean;
|
|
72
|
-
}
|
|
72
|
+
} & BaseCustomRendererProps;
|
|
73
|
+
|
|
74
|
+
export type CustomRendererProps = {} & EnhancedRendererProps;
|
|
73
75
|
|
|
74
|
-
export
|
|
76
|
+
export type LinkPressHandler = (
|
|
77
|
+
href: string,
|
|
78
|
+
) => void | boolean | Promise<void | boolean>;
|
|
75
79
|
|
|
76
80
|
export type CustomRenderer = (
|
|
77
|
-
props: EnhancedRendererProps
|
|
81
|
+
props: EnhancedRendererProps,
|
|
78
82
|
) => ReactNode | undefined;
|
|
79
83
|
|
|
80
84
|
export type CustomRenderers = Partial<
|
|
81
85
|
Record<MarkdownNode["type"], CustomRenderer>
|
|
82
86
|
>;
|
|
83
87
|
|
|
84
|
-
export
|
|
88
|
+
export type MarkdownContextValue = {
|
|
85
89
|
renderers: CustomRenderers;
|
|
86
90
|
theme: MarkdownTheme;
|
|
87
91
|
styles?: NodeStyleOverrides;
|
|
88
92
|
stylingStrategy: StylingStrategy;
|
|
89
|
-
|
|
93
|
+
onLinkPress?: LinkPressHandler;
|
|
94
|
+
};
|
|
90
95
|
|
|
91
96
|
export const MarkdownContext = createContext<MarkdownContextValue>({
|
|
92
97
|
renderers: {},
|
|
93
98
|
theme: defaultMarkdownTheme,
|
|
94
99
|
styles: undefined,
|
|
95
100
|
stylingStrategy: "opinionated",
|
|
101
|
+
onLinkPress: undefined,
|
|
96
102
|
});
|
|
97
103
|
|
|
98
104
|
export const useMarkdownContext = () => useContext(MarkdownContext);
|
package/src/headless.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type { ParserOptions } from "./Markdown.nitro";
|
|
|
19
19
|
* Represents a node in the Markdown AST (Abstract Syntax Tree).
|
|
20
20
|
* Each node has a type and optional properties depending on the node type.
|
|
21
21
|
*/
|
|
22
|
-
export
|
|
22
|
+
export type MarkdownNode = {
|
|
23
23
|
/** The type of markdown element this node represents. Used to decide how to render the node. */
|
|
24
24
|
type:
|
|
25
25
|
| "document"
|
|
@@ -71,9 +71,13 @@ export interface MarkdownNode {
|
|
|
71
71
|
isHeader?: boolean;
|
|
72
72
|
/** Text alignment for table cells: 'left', 'center', or 'right'. */
|
|
73
73
|
align?: string;
|
|
74
|
+
/** Source start offset in original markdown text (when provided by native parser). */
|
|
75
|
+
beg?: number;
|
|
76
|
+
/** Source end offset in original markdown text (when provided by native parser). */
|
|
77
|
+
end?: number;
|
|
74
78
|
/** Nested child nodes for hierarchical elements like paragraphs, lists, and tables. */
|
|
75
79
|
children?: MarkdownNode[];
|
|
76
|
-
}
|
|
80
|
+
};
|
|
77
81
|
|
|
78
82
|
export const MarkdownParserModule =
|
|
79
83
|
NitroModules.createHybridObject<MarkdownParser>("MarkdownParser");
|
|
@@ -84,8 +88,12 @@ export const MarkdownParserModule =
|
|
|
84
88
|
* @returns The root node of the parsed AST
|
|
85
89
|
*/
|
|
86
90
|
export function parseMarkdown(text: string): MarkdownNode {
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
if (typeof MarkdownParserModule.parse === "function") {
|
|
92
|
+
const jsonStr = MarkdownParserModule.parse(text);
|
|
93
|
+
return JSON.parse(jsonStr) as MarkdownNode;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { type: "document", children: [] };
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/**
|
|
@@ -98,8 +106,38 @@ export function parseMarkdownWithOptions(
|
|
|
98
106
|
text: string,
|
|
99
107
|
options: ParserOptions,
|
|
100
108
|
): MarkdownNode {
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
if (typeof MarkdownParserModule.parseWithOptions === "function") {
|
|
110
|
+
const jsonStr = MarkdownParserModule.parseWithOptions(text, options);
|
|
111
|
+
return JSON.parse(jsonStr) as MarkdownNode;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { type: "document", children: [] };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Parse markdown and return flattened plain text directly from native parser.
|
|
119
|
+
* Useful for search/indexing flows that don't need full AST rendering.
|
|
120
|
+
*/
|
|
121
|
+
export function extractPlainText(text: string): string {
|
|
122
|
+
if (typeof MarkdownParserModule.extractPlainText === "function") {
|
|
123
|
+
return MarkdownParserModule.extractPlainText(text);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return getFlattenedText(parseMarkdown(text));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Parse markdown with options and return flattened plain text from native parser.
|
|
131
|
+
*/
|
|
132
|
+
export function extractPlainTextWithOptions(
|
|
133
|
+
text: string,
|
|
134
|
+
options: ParserOptions,
|
|
135
|
+
): string {
|
|
136
|
+
if (typeof MarkdownParserModule.extractPlainTextWithOptions === "function") {
|
|
137
|
+
return MarkdownParserModule.extractPlainTextWithOptions(text, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return getFlattenedText(parseMarkdownWithOptions(text, options));
|
|
103
141
|
}
|
|
104
142
|
|
|
105
143
|
export type { MarkdownParser };
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
export * from "./headless";
|
|
2
2
|
|
|
3
3
|
export { Markdown } from "./markdown";
|
|
4
|
+
export type {
|
|
5
|
+
MarkdownProps,
|
|
6
|
+
AstTransform,
|
|
7
|
+
MarkdownPlugin,
|
|
8
|
+
MarkdownVirtualizationOptions,
|
|
9
|
+
} from "./markdown";
|
|
4
10
|
export { MarkdownStream } from "./markdown-stream";
|
|
11
|
+
export type { MarkdownStreamProps } from "./markdown-stream";
|
|
5
12
|
|
|
6
13
|
export { useMarkdownContext, MarkdownContext } from "./MarkdownContext";
|
|
7
14
|
export type {
|
|
@@ -18,6 +25,7 @@ export type {
|
|
|
18
25
|
InlineCodeRendererProps,
|
|
19
26
|
ListRendererProps,
|
|
20
27
|
TaskListItemRendererProps,
|
|
28
|
+
LinkPressHandler,
|
|
21
29
|
MarkdownContextValue,
|
|
22
30
|
} from "./MarkdownContext";
|
|
23
31
|
|
package/src/markdown-stream.tsx
CHANGED
|
@@ -2,13 +2,55 @@ import {
|
|
|
2
2
|
useState,
|
|
3
3
|
useEffect,
|
|
4
4
|
useRef,
|
|
5
|
+
useCallback,
|
|
5
6
|
startTransition,
|
|
6
7
|
type FC,
|
|
7
8
|
} from "react";
|
|
9
|
+
import type { MarkdownNode } from "./headless";
|
|
8
10
|
import { Markdown, type MarkdownProps } from "./markdown";
|
|
9
11
|
import type { MarkdownSession } from "./specs/MarkdownSession.nitro";
|
|
12
|
+
import { getNextStreamAst, parseMarkdownAst } from "./utils/incremental-ast";
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
const normalizeOffset = (value: number): number | null => {
|
|
15
|
+
if (!Number.isFinite(value)) return null;
|
|
16
|
+
if (value <= 0) return 0;
|
|
17
|
+
return Math.floor(value);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const resolveStreamText = ({
|
|
21
|
+
forceFullSync,
|
|
22
|
+
pendingFrom,
|
|
23
|
+
pendingTo,
|
|
24
|
+
previousText,
|
|
25
|
+
session,
|
|
26
|
+
}: {
|
|
27
|
+
forceFullSync: boolean;
|
|
28
|
+
pendingFrom: number | null;
|
|
29
|
+
pendingTo: number | null;
|
|
30
|
+
previousText: string;
|
|
31
|
+
session: MarkdownSession;
|
|
32
|
+
}): string => {
|
|
33
|
+
if (forceFullSync || pendingFrom === null || pendingTo === null) {
|
|
34
|
+
return session.getAllText();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (pendingTo < pendingFrom) {
|
|
38
|
+
return session.getAllText();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (pendingFrom === previousText.length) {
|
|
42
|
+
const appendedChunk = session.getTextRange(pendingFrom, pendingTo);
|
|
43
|
+
return `${previousText}${appendedChunk}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (pendingFrom === 0) {
|
|
47
|
+
return session.getTextRange(0, pendingTo);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return session.getAllText();
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type MarkdownStreamProps = {
|
|
12
54
|
/**
|
|
13
55
|
* The active MarkdownSession to stream content from.
|
|
14
56
|
*/
|
|
@@ -29,7 +71,12 @@ export interface MarkdownStreamProps extends Omit<MarkdownProps, "children"> {
|
|
|
29
71
|
* Useful when you want to prioritize user interactions over stream renders.
|
|
30
72
|
*/
|
|
31
73
|
useTransitionUpdates?: boolean;
|
|
32
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Enable incremental AST updates for append-only streams.
|
|
76
|
+
* Automatically falls back to full parse when updates are not safely mergeable.
|
|
77
|
+
*/
|
|
78
|
+
incrementalParsing?: boolean;
|
|
79
|
+
} & Omit<MarkdownProps, "children" | "sourceAst">;
|
|
33
80
|
|
|
34
81
|
/**
|
|
35
82
|
* A component that renders streaming Markdown from a MarkdownSession.
|
|
@@ -40,20 +87,55 @@ export const MarkdownStream: FC<MarkdownStreamProps> = ({
|
|
|
40
87
|
updateIntervalMs = 50,
|
|
41
88
|
updateStrategy = "interval",
|
|
42
89
|
useTransitionUpdates = false,
|
|
90
|
+
incrementalParsing = true,
|
|
91
|
+
options,
|
|
92
|
+
plugins,
|
|
43
93
|
...props
|
|
44
94
|
}) => {
|
|
45
|
-
const
|
|
95
|
+
const parseText = useCallback(
|
|
96
|
+
(text: string): MarkdownNode => parseMarkdownAst(text, options),
|
|
97
|
+
[options],
|
|
98
|
+
);
|
|
99
|
+
const createEmptyAst = (): MarkdownNode => ({
|
|
100
|
+
type: "document",
|
|
101
|
+
children: [],
|
|
102
|
+
});
|
|
103
|
+
const initialText = session.getAllText();
|
|
104
|
+
const hasBeforeParsePlugins =
|
|
105
|
+
plugins?.some((plugin) => typeof plugin.beforeParse === "function") ??
|
|
106
|
+
false;
|
|
107
|
+
const [renderState, setRenderState] = useState(() => ({
|
|
108
|
+
text: initialText,
|
|
109
|
+
ast: hasBeforeParsePlugins ? createEmptyAst() : parseText(initialText),
|
|
110
|
+
}));
|
|
111
|
+
const renderStateRef = useRef(renderState);
|
|
46
112
|
const pendingUpdateRef = useRef(false);
|
|
113
|
+
const pendingFromRef = useRef<number | null>(null);
|
|
114
|
+
const pendingToRef = useRef<number | null>(null);
|
|
115
|
+
const forceFullSyncRef = useRef(false);
|
|
47
116
|
const updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
48
117
|
const rafRef = useRef<number | null>(null);
|
|
49
|
-
const
|
|
118
|
+
const allowIncremental = incrementalParsing && !hasBeforeParsePlugins;
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
renderStateRef.current = renderState;
|
|
122
|
+
}, [renderState]);
|
|
50
123
|
|
|
51
124
|
useEffect(() => {
|
|
52
|
-
// Ensure initial state is synced
|
|
53
125
|
const initialText = session.getAllText();
|
|
54
|
-
|
|
55
|
-
|
|
126
|
+
const initialState = {
|
|
127
|
+
text: initialText,
|
|
128
|
+
ast: hasBeforeParsePlugins ? createEmptyAst() : parseText(initialText),
|
|
129
|
+
};
|
|
130
|
+
pendingUpdateRef.current = false;
|
|
131
|
+
pendingFromRef.current = null;
|
|
132
|
+
pendingToRef.current = null;
|
|
133
|
+
forceFullSyncRef.current = false;
|
|
134
|
+
renderStateRef.current = initialState;
|
|
135
|
+
setRenderState(initialState);
|
|
136
|
+
}, [hasBeforeParsePlugins, parseText, session]);
|
|
56
137
|
|
|
138
|
+
useEffect(() => {
|
|
57
139
|
const flushUpdate = () => {
|
|
58
140
|
updateTimerRef.current = null;
|
|
59
141
|
if (rafRef.current !== null) {
|
|
@@ -63,14 +145,44 @@ export const MarkdownStream: FC<MarkdownStreamProps> = ({
|
|
|
63
145
|
if (!pendingUpdateRef.current) return;
|
|
64
146
|
pendingUpdateRef.current = false;
|
|
65
147
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
148
|
+
const previousState = renderStateRef.current;
|
|
149
|
+
const pendingFrom = pendingFromRef.current;
|
|
150
|
+
const pendingTo = pendingToRef.current;
|
|
151
|
+
const forceFullSync = forceFullSyncRef.current;
|
|
152
|
+
pendingFromRef.current = null;
|
|
153
|
+
pendingToRef.current = null;
|
|
154
|
+
forceFullSyncRef.current = false;
|
|
155
|
+
|
|
156
|
+
const latest = resolveStreamText({
|
|
157
|
+
forceFullSync,
|
|
158
|
+
pendingFrom,
|
|
159
|
+
pendingTo,
|
|
160
|
+
previousText: previousState.text,
|
|
161
|
+
session,
|
|
162
|
+
});
|
|
163
|
+
if (latest === previousState.text) return;
|
|
164
|
+
|
|
165
|
+
const nextAst = hasBeforeParsePlugins
|
|
166
|
+
? previousState.ast
|
|
167
|
+
: getNextStreamAst({
|
|
168
|
+
allowIncremental,
|
|
169
|
+
nextText: latest,
|
|
170
|
+
options,
|
|
171
|
+
previousAst: previousState.ast,
|
|
172
|
+
previousText: previousState.text,
|
|
173
|
+
});
|
|
174
|
+
const nextState = {
|
|
175
|
+
text: latest,
|
|
176
|
+
ast: nextAst,
|
|
177
|
+
};
|
|
178
|
+
renderStateRef.current = nextState;
|
|
69
179
|
|
|
70
180
|
if (useTransitionUpdates) {
|
|
71
|
-
startTransition(() =>
|
|
181
|
+
startTransition(() => {
|
|
182
|
+
setRenderState(nextState);
|
|
183
|
+
});
|
|
72
184
|
} else {
|
|
73
|
-
|
|
185
|
+
setRenderState(nextState);
|
|
74
186
|
}
|
|
75
187
|
};
|
|
76
188
|
|
|
@@ -87,7 +199,22 @@ export const MarkdownStream: FC<MarkdownStreamProps> = ({
|
|
|
87
199
|
}
|
|
88
200
|
};
|
|
89
201
|
|
|
90
|
-
const unsubscribe = session.addListener(() => {
|
|
202
|
+
const unsubscribe = session.addListener((from, to) => {
|
|
203
|
+
const nextFrom = normalizeOffset(from);
|
|
204
|
+
const nextTo = normalizeOffset(to);
|
|
205
|
+
|
|
206
|
+
if (nextFrom === null || nextTo === null || nextTo < nextFrom) {
|
|
207
|
+
forceFullSyncRef.current = true;
|
|
208
|
+
} else {
|
|
209
|
+
const currentFrom = pendingFromRef.current;
|
|
210
|
+
const currentTo = pendingToRef.current;
|
|
211
|
+
|
|
212
|
+
pendingFromRef.current =
|
|
213
|
+
currentFrom === null ? nextFrom : Math.min(currentFrom, nextFrom);
|
|
214
|
+
pendingToRef.current =
|
|
215
|
+
currentTo === null ? nextTo : Math.max(currentTo, nextTo);
|
|
216
|
+
}
|
|
217
|
+
|
|
91
218
|
pendingUpdateRef.current = true;
|
|
92
219
|
scheduleFlush();
|
|
93
220
|
});
|
|
@@ -103,7 +230,24 @@ export const MarkdownStream: FC<MarkdownStreamProps> = ({
|
|
|
103
230
|
rafRef.current = null;
|
|
104
231
|
}
|
|
105
232
|
};
|
|
106
|
-
}, [
|
|
233
|
+
}, [
|
|
234
|
+
allowIncremental,
|
|
235
|
+
hasBeforeParsePlugins,
|
|
236
|
+
options,
|
|
237
|
+
session,
|
|
238
|
+
updateIntervalMs,
|
|
239
|
+
updateStrategy,
|
|
240
|
+
useTransitionUpdates,
|
|
241
|
+
]);
|
|
107
242
|
|
|
108
|
-
return
|
|
243
|
+
return (
|
|
244
|
+
<Markdown
|
|
245
|
+
{...props}
|
|
246
|
+
options={options}
|
|
247
|
+
plugins={plugins}
|
|
248
|
+
sourceAst={hasBeforeParsePlugins ? undefined : renderState.ast}
|
|
249
|
+
>
|
|
250
|
+
{renderState.text}
|
|
251
|
+
</Markdown>
|
|
252
|
+
);
|
|
109
253
|
};
|