@expensify/react-native-live-markdown 0.1.187 → 0.1.188

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.
Files changed (75) hide show
  1. package/README.md +55 -8
  2. package/RNLiveMarkdown.podspec +2 -4
  3. package/android/build.gradle +4 -9
  4. package/android/src/main/cpp/CMakeLists.txt +14 -6
  5. package/android/src/main/cpp/MarkdownUtils.cpp +13 -24
  6. package/android/src/main/cpp/MarkdownUtils.h +2 -7
  7. package/android/src/main/cpp/OnLoad.cpp +6 -0
  8. package/android/src/main/java/com/expensify/livemarkdown/CustomFabricUIManager.java +2 -2
  9. package/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java +4 -6
  10. package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java +12 -0
  11. package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorView.java +17 -3
  12. package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorViewManager.java +6 -0
  13. package/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +21 -34
  14. package/android/src/main/new_arch/MarkdownCommitHook.cpp +7 -4
  15. package/android/src/main/new_arch/MarkdownCommitHook.h +2 -0
  16. package/android/src/newarch/LiveMarkdownModuleSpec.java +9 -0
  17. package/android/src/oldarch/LiveMarkdownModuleSpec.java +12 -0
  18. package/android/src/oldarch/MarkdownTextInputDecoratorViewManagerSpec.java +1 -0
  19. package/apple/LiveMarkdownModule.h +16 -0
  20. package/apple/{RCTLiveMarkdownModule.mm → LiveMarkdownModule.mm} +31 -17
  21. package/apple/MarkdownCommitHook.mm +2 -0
  22. package/apple/MarkdownTextInputDecoratorComponentView.mm +4 -0
  23. package/apple/MarkdownTextInputDecoratorView.h +2 -0
  24. package/apple/MarkdownTextInputDecoratorView.mm +13 -0
  25. package/apple/MarkdownTextInputDecoratorViewManager.mm +9 -0
  26. package/apple/RCTMarkdownUtils.h +1 -0
  27. package/apple/RCTMarkdownUtils.mm +43 -45
  28. package/cpp/MarkdownGlobal.cpp +45 -0
  29. package/cpp/MarkdownGlobal.h +24 -0
  30. package/cpp/RuntimeDecorator.cpp +43 -0
  31. package/cpp/RuntimeDecorator.h +13 -0
  32. package/lib/commonjs/MarkdownTextInput.js +43 -2
  33. package/lib/commonjs/MarkdownTextInput.js.map +1 -1
  34. package/lib/commonjs/MarkdownTextInput.web.js +20 -14
  35. package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
  36. package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
  37. package/lib/commonjs/index.js +7 -0
  38. package/lib/commonjs/index.js.map +1 -1
  39. package/lib/commonjs/parseExpensiMark.js +263 -0
  40. package/lib/commonjs/parseExpensiMark.js.map +1 -0
  41. package/lib/commonjs/web/utils/parserUtils.js +2 -2
  42. package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
  43. package/lib/module/MarkdownTextInput.js +43 -2
  44. package/lib/module/MarkdownTextInput.js.map +1 -1
  45. package/lib/module/MarkdownTextInput.web.js +20 -14
  46. package/lib/module/MarkdownTextInput.web.js.map +1 -1
  47. package/lib/module/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
  48. package/lib/module/index.js +1 -0
  49. package/lib/module/index.js.map +1 -1
  50. package/lib/module/parseExpensiMark.js +258 -0
  51. package/lib/module/parseExpensiMark.js.map +1 -0
  52. package/lib/module/web/utils/parserUtils.js +2 -2
  53. package/lib/module/web/utils/parserUtils.js.map +1 -1
  54. package/lib/typescript/src/MarkdownTextInput.d.ts +9 -1
  55. package/lib/typescript/src/MarkdownTextInput.d.ts.map +1 -1
  56. package/lib/typescript/src/MarkdownTextInput.web.d.ts +2 -1
  57. package/lib/typescript/src/MarkdownTextInput.web.d.ts.map +1 -1
  58. package/lib/typescript/src/MarkdownTextInputDecoratorViewNativeComponent.d.ts +2 -1
  59. package/lib/typescript/src/MarkdownTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  60. package/lib/typescript/src/index.d.ts +2 -0
  61. package/lib/typescript/src/index.d.ts.map +1 -1
  62. package/lib/typescript/src/parseExpensiMark.d.ts +4 -0
  63. package/lib/typescript/src/parseExpensiMark.d.ts.map +1 -0
  64. package/lib/typescript/src/web/utils/parserUtils.d.ts +1 -1
  65. package/lib/typescript/src/web/utils/parserUtils.d.ts.map +1 -1
  66. package/package.json +13 -10
  67. package/src/MarkdownTextInput.tsx +62 -2
  68. package/src/MarkdownTextInput.web.tsx +23 -15
  69. package/src/MarkdownTextInputDecoratorViewNativeComponent.ts +2 -1
  70. package/src/index.tsx +2 -0
  71. package/src/parseExpensiMark.ts +277 -0
  72. package/src/web/utils/parserUtils.ts +2 -1
  73. package/apple/RCTLiveMarkdownModule.h +0 -16
  74. package/lib/parser/react-native-live-markdown-parser.js +0 -29
  75. package/parser/react-native-live-markdown-parser.js +0 -29
@@ -1,40 +1,55 @@
1
+ #import "LiveMarkdownModule.h"
2
+
3
+ #import <RNLiveMarkdown/RuntimeDecorator.h>
4
+
1
5
  #ifdef RCT_NEW_ARCH_ENABLED
6
+ #import <RNLiveMarkdown/MarkdownCommitHook.h>
7
+ #endif // RCT_NEW_ARCH_ENABLED
2
8
 
9
+ #import <React/RCTBridge+Private.h>
10
+
11
+ #ifdef RCT_NEW_ARCH_ENABLED
3
12
  #import <React/RCTScheduler.h>
4
13
  #import <React/RCTSurfacePresenter.h>
14
+ #endif // RCT_NEW_ARCH_ENABLED
5
15
 
6
- #import "MarkdownCommitHook.h"
7
- #import "MarkdownShadowFamilyRegistry.h"
8
- #import "RCTLiveMarkdownModule.h"
16
+ #import <jsi/jsi.h>
9
17
 
18
+ using namespace facebook;
10
19
  using namespace expensify::livemarkdown;
11
20
 
12
21
  // A turbomodule used to register the commit hook
13
22
  // I think this is the easiest way to access the UIManager, which we need to
14
23
  // actually register the hook
15
24
 
16
- @implementation RCTLiveMarkdownModule {
17
- BOOL installed_;
25
+ @implementation LiveMarkdownModule {
26
+ #ifdef RCT_NEW_ARCH_ENABLED
18
27
  std::shared_ptr<MarkdownCommitHook> commitHook_;
19
28
  __weak RCTSurfacePresenter *surfacePresenter_;
29
+ #endif // RCT_NEW_ARCH_ENABLED
20
30
  }
21
31
 
22
- RCT_EXPORT_MODULE(@"LiveMarkdownModule")
32
+ RCT_EXPORT_MODULE()
23
33
 
24
- - (NSNumber *)install {
25
- if (!installed_ && surfacePresenter_ != nil) {
26
- RCTScheduler *scheduler = [surfacePresenter_ scheduler];
34
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
35
+ {
36
+ RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
37
+ jsi::Runtime &rt = *(jsi::Runtime *)cxxBridge.runtime;
38
+ expensify::livemarkdown::injectJSIBindings(rt);
27
39
 
28
- commitHook_ = std::make_shared<MarkdownCommitHook>(scheduler.uiManager);
29
- installed_ = YES;
30
- }
31
- return @1;
40
+ #ifdef RCT_NEW_ARCH_ENABLED
41
+ RCTScheduler *scheduler = [surfacePresenter_ scheduler];
42
+ commitHook_ = std::make_shared<MarkdownCommitHook>(scheduler.uiManager);
43
+ #endif // RCT_NEW_ARCH_ENABLED
44
+
45
+ return @(1);
32
46
  }
33
47
 
48
+ #ifdef RCT_NEW_ARCH_ENABLED
49
+
34
50
  - (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification
35
51
  {
36
52
  surfacePresenter_ = self.bridge.surfacePresenter;
37
- [self install];
38
53
  }
39
54
 
40
55
  - (void)setBridge:(RCTBridge *)bridge
@@ -62,7 +77,6 @@ RCT_EXPORT_MODULE(@"LiveMarkdownModule")
62
77
  surfacePresenter_ = surfacePresenter;
63
78
  }
64
79
 
65
-
66
80
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
67
81
  (const facebook::react::ObjCTurboModule::InitParams &)params {
68
82
  return std::make_shared<facebook::react::NativeLiveMarkdownModuleSpecJSI>(
@@ -75,6 +89,6 @@ RCT_EXPORT_MODULE(@"LiveMarkdownModule")
75
89
  [super invalidate];
76
90
  }
77
91
 
78
- @end
79
-
80
92
  #endif // RCT_NEW_ARCH_ENABLED
93
+
94
+ @end
@@ -158,6 +158,7 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
158
158
  usedUtils =
159
159
  MarkdownCommitHook::getOrCreateMarkdownUtils(*nodes.decorator);
160
160
  [usedUtils setMarkdownStyle:markdownStyle];
161
+ [usedUtils setParserId:@(markdownProps.parserId)];
161
162
 
162
163
  // convert the attibuted string stored in state to
163
164
  // NSAttributedString
@@ -236,6 +237,7 @@ RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
236
237
  usedUtils =
237
238
  MarkdownCommitHook::getOrCreateMarkdownUtils(*nodes.decorator);
238
239
  [usedUtils setMarkdownStyle:markdownStyle];
240
+ [usedUtils setParserId:@(markdownProps.parserId)];
239
241
 
240
242
  // convert the attibuted string stored in state to
241
243
  // NSAttributedString
@@ -68,6 +68,10 @@ using namespace facebook::react;
68
68
  const auto &oldViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(_props);
69
69
  const auto &newViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(props);
70
70
 
71
+ if (oldViewProps.parserId != newViewProps.parserId) {
72
+ [_view setParserId:@(newViewProps.parserId)];
73
+ }
74
+
71
75
  // TODO: if (oldViewProps.markdownStyle != newViewProps.markdownStyle)
72
76
  RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc] initWithStruct:newViewProps.markdownStyle];
73
77
  [_view setMarkdownStyle:markdownStyle];
@@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN
7
7
 
8
8
  - (void)setMarkdownStyle:(RCTMarkdownStyle *)markdownStyle;
9
9
 
10
+ - (void)setParserId:(NSNumber *)parserId;
11
+
10
12
  @end
11
13
 
12
14
  NS_ASSUME_NONNULL_END
@@ -17,6 +17,7 @@
17
17
  @implementation MarkdownTextInputDecoratorView {
18
18
  RCTMarkdownUtils *_markdownUtils;
19
19
  RCTMarkdownStyle *_markdownStyle;
20
+ NSNumber *_parserId;
20
21
  #ifdef RCT_NEW_ARCH_ENABLED
21
22
  __weak RCTTextInputComponentView *_textInput;
22
23
  #else
@@ -62,6 +63,7 @@
62
63
  _markdownUtils = [[RCTMarkdownUtils alloc] init];
63
64
  react_native_assert(_markdownStyle != nil);
64
65
  [_markdownUtils setMarkdownStyle:_markdownStyle];
66
+ [_markdownUtils setParserId:_parserId];
65
67
 
66
68
  [_textInput setMarkdownUtils:_markdownUtils];
67
69
  if ([_backedTextInputView isKindOfClass:[RCTUITextField class]]) {
@@ -109,7 +111,18 @@
109
111
  {
110
112
  _markdownStyle = markdownStyle;
111
113
  [_markdownUtils setMarkdownStyle:markdownStyle];
114
+ [self applyNewStyles];
115
+ }
112
116
 
117
+ - (void)setParserId:(NSNumber *)parserId
118
+ {
119
+ _parserId = parserId;
120
+ [_markdownUtils setParserId:parserId];
121
+ [self applyNewStyles];
122
+ }
123
+
124
+ - (void)applyNewStyles
125
+ {
113
126
  if (_textView != nil) {
114
127
  // We want to use `textStorage` for applying markdown when possible. Currently it's only available for UITextView
115
128
  [_textView textDidChange];
@@ -20,4 +20,13 @@ RCT_CUSTOM_VIEW_PROPERTY(markdownStyle, NSDictionary, MarkdownTextInputDecorator
20
20
  #endif /* RCT_NEW_ARCH_ENABLED */
21
21
  }
22
22
 
23
+ RCT_CUSTOM_VIEW_PROPERTY(parserId, NSNumber, MarkdownTextInputDecoratorView)
24
+ {
25
+ #ifdef RCT_NEW_ARCH_ENABLED
26
+ // implemented in MarkdownTextInputDecoratorView updateProps:
27
+ #else
28
+ [view setParserId:json];
29
+ #endif /* RCT_NEW_ARCH_ENABLED */
30
+ }
31
+
23
32
  @end
@@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN
6
6
  @interface RCTMarkdownUtils : NSObject
7
7
 
8
8
  @property (nonatomic) RCTMarkdownStyle *markdownStyle;
9
+ @property (nonatomic) NSNumber *parserId;
9
10
  @property (nonatomic) NSMutableArray<NSDictionary *> *blockquoteRangesAndLevels;
10
11
 
11
12
  - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey, id>*)attributes;
@@ -1,18 +1,16 @@
1
1
  #import <RNLiveMarkdown/RCTMarkdownUtils.h>
2
+ #import <RNLiveMarkdown/MarkdownGlobal.h>
2
3
  #import "react_native_assert.h"
3
4
  #import <React/RCTAssert.h>
4
5
  #import <React/RCTFont.h>
5
-
6
6
  #include <jsi/jsi.h>
7
- #include <hermes/hermes.h>
8
-
9
- using namespace facebook;
10
7
 
11
8
  @implementation RCTMarkdownUtils {
12
9
  NSString *_prevInputString;
13
10
  NSAttributedString *_prevAttributedString;
14
11
  NSDictionary<NSAttributedStringKey, id> *_prevTextAttributes;
15
12
  __weak RCTMarkdownStyle *_prevMarkdownStyle;
13
+ __weak NSNumber *_prevParserId;
16
14
  }
17
15
 
18
16
  - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes
@@ -23,64 +21,64 @@ using namespace facebook;
23
21
  }
24
22
 
25
23
  NSString *inputString = [input string];
26
- if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle]) {
24
+ if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle] && [_parserId isEqualToNumber:_prevParserId]) {
27
25
  return _prevAttributedString;
28
26
  }
29
27
 
30
- static std::shared_ptr<jsi::Runtime> runtime;
31
28
  static std::mutex runtimeMutex;
32
29
  auto lock = std::lock_guard<std::mutex>(runtimeMutex);
33
30
 
34
- if (runtime == nullptr) {
35
- NSString *path = [[NSBundle mainBundle] pathForResource:@"react-native-live-markdown-parser" ofType:@"js"];
36
- assert(path != nil && "[react-native-live-markdown] Markdown parser bundle not found");
37
- NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
38
- assert(content != nil && "[react-native-live-markdown] Markdown parser bundle is empty");
39
- runtime = facebook::hermes::makeHermesRuntime();
40
- auto codeBuffer = std::make_shared<const jsi::StringBuffer>([content UTF8String]);
41
- runtime->evaluateJavaScript(codeBuffer, "evaluateJavaScript");
42
- }
31
+ auto markdownRuntime = expensify::livemarkdown::getMarkdownRuntime();
32
+ jsi::Runtime &rt = markdownRuntime->getJSIRuntime();
43
33
 
44
- jsi::Runtime &rt = *runtime;
45
- auto text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);
34
+ auto markdownWorklet = expensify::livemarkdown::getMarkdownWorklet([_parserId intValue]);
46
35
 
47
- auto func = rt.global().getPropertyAsFunction(rt, "parseExpensiMarkToRanges");
48
- auto output = func.call(rt, text);
49
- if (output.isUndefined()) {
50
- return input;
51
- }
52
- const auto &ranges = output.asObject(rt).asArray(rt);
36
+ try {
37
+ const auto &text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);
38
+ const auto &output = markdownRuntime->runGuarded(markdownWorklet, text);
39
+ const auto &ranges = output.asObject(rt).asArray(rt);
53
40
 
54
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
55
- [attributedString beginEditing];
41
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
42
+ [attributedString beginEditing];
56
43
 
57
- // If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string.
58
- // It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting.
59
- // This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem.
60
- [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)];
44
+ // If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string.
45
+ // It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting.
46
+ // This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem.
47
+ [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)];
61
48
 
62
- _blockquoteRangesAndLevels = [NSMutableArray new];
49
+ _blockquoteRangesAndLevels = [NSMutableArray new];
63
50
 
64
- for (size_t i = 0, n = ranges.size(rt); i < n; ++i) {
65
- const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt);
66
- const auto &type = item.getProperty(rt, "type").asString(rt).utf8(rt);
67
- const auto &start = static_cast<int>(item.getProperty(rt, "start").asNumber());
68
- const auto &length = static_cast<int>(item.getProperty(rt, "length").asNumber());
69
- const auto &depth = item.hasProperty(rt, "depth") ? static_cast<int>(item.getProperty(rt, "depth").asNumber()) : 1;
51
+ for (size_t i = 0, n = ranges.size(rt); i < n; ++i) {
52
+ const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt);
53
+ const auto &type = item.getProperty(rt, "type").asString(rt).utf8(rt);
54
+ const auto &start = static_cast<int>(item.getProperty(rt, "start").asNumber());
55
+ const auto &length = static_cast<int>(item.getProperty(rt, "length").asNumber());
56
+ const auto &depth = item.hasProperty(rt, "depth") ? static_cast<int>(item.getProperty(rt, "depth").asNumber()) : 1;
70
57
 
71
- [self applyRangeToAttributedString:attributedString type:type start:start length:length depth:depth];
72
- }
58
+ [self applyRangeToAttributedString:attributedString type:type start:start length:length depth:depth];
59
+ }
73
60
 
74
- RCTApplyBaselineOffset(attributedString);
61
+ RCTApplyBaselineOffset(attributedString);
75
62
 
76
- [attributedString endEditing];
63
+ [attributedString endEditing];
77
64
 
78
- _prevInputString = inputString;
79
- _prevAttributedString = attributedString;
80
- _prevTextAttributes = attributes;
81
- _prevMarkdownStyle = _markdownStyle;
65
+ _prevInputString = inputString;
66
+ _prevAttributedString = attributedString;
67
+ _prevTextAttributes = attributes;
68
+ _prevMarkdownStyle = _markdownStyle;
69
+ _prevParserId = _parserId;
82
70
 
83
- return attributedString;
71
+ return attributedString;
72
+ } catch (const jsi::JSError &error) {
73
+ RCTLogWarn(@"[react-native-live-markdown] Incorrect schema of worklet parser output: %s", error.getMessage().c_str());
74
+ NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:inputString attributes:attributes];
75
+ _prevInputString = inputString;
76
+ _prevAttributedString = attributedString;
77
+ _prevTextAttributes = attributes;
78
+ _prevMarkdownStyle = _markdownStyle;
79
+ _prevParserId = _parserId;
80
+ return attributedString;
81
+ }
84
82
  }
85
83
  }
86
84
 
@@ -0,0 +1,45 @@
1
+ #include "MarkdownGlobal.h"
2
+
3
+ #include <unordered_map>
4
+
5
+ using namespace facebook;
6
+
7
+ namespace expensify {
8
+ namespace livemarkdown {
9
+
10
+ std::shared_ptr<WorkletRuntime> globalMarkdownWorkletRuntime;
11
+
12
+ void setMarkdownRuntime(const std::shared_ptr<WorkletRuntime> &markdownWorkletRuntime) {
13
+ globalMarkdownWorkletRuntime = markdownWorkletRuntime;
14
+ }
15
+
16
+ std::shared_ptr<WorkletRuntime> getMarkdownRuntime() {
17
+ return globalMarkdownWorkletRuntime;
18
+ }
19
+
20
+ std::unordered_map<int, std::shared_ptr<ShareableWorklet>> globalMarkdownShareableWorklets;
21
+ std::mutex globalMarkdownShareableWorkletsMutex;
22
+ int nextParserId = 1;
23
+
24
+ const int registerMarkdownWorklet(const std::shared_ptr<ShareableWorklet> &markdownWorklet) {
25
+ assert(markdownWorklet != nullptr);
26
+ auto parserId = nextParserId++;
27
+ std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
28
+ globalMarkdownShareableWorklets[parserId] = markdownWorklet;
29
+ return parserId;
30
+ }
31
+
32
+ void unregisterMarkdownWorklet(const int parserId) {
33
+ std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
34
+ globalMarkdownShareableWorklets.erase(parserId);
35
+ }
36
+
37
+ std::shared_ptr<ShareableWorklet> getMarkdownWorklet(const int parserId) {
38
+ std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
39
+ const auto &worklet = globalMarkdownShareableWorklets[parserId];
40
+ assert(worklet != nullptr);
41
+ return worklet;
42
+ }
43
+
44
+ } // namespace livemarkdown
45
+ } // namespace expensify
@@ -0,0 +1,24 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+
5
+ #include <worklets/WorkletRuntime/WorkletRuntime.h>
6
+
7
+ using namespace facebook;
8
+ using namespace worklets;
9
+
10
+ namespace expensify {
11
+ namespace livemarkdown {
12
+
13
+ void setMarkdownRuntime(const std::shared_ptr<WorkletRuntime> &markdownWorkletRuntime);
14
+
15
+ std::shared_ptr<WorkletRuntime> getMarkdownRuntime();
16
+
17
+ const int registerMarkdownWorklet(const std::shared_ptr<ShareableWorklet> &markdownWorklet);
18
+
19
+ void unregisterMarkdownWorklet(const int parserId);
20
+
21
+ std::shared_ptr<ShareableWorklet> getMarkdownWorklet(const int parserId);
22
+
23
+ } // namespace livemarkdown
24
+ } // namespace expensify
@@ -0,0 +1,43 @@
1
+ #include "RuntimeDecorator.h"
2
+ #include "MarkdownGlobal.h"
3
+
4
+ using namespace facebook;
5
+ using namespace worklets;
6
+
7
+ namespace expensify {
8
+ namespace livemarkdown {
9
+
10
+ void injectJSIBindings(jsi::Runtime &rt) {
11
+
12
+ rt.global().setProperty(rt, "jsi_setMarkdownRuntime", jsi::Function::createFromHostFunction(
13
+ rt,
14
+ jsi::PropNameID::forAscii(rt, "jsi_setMarkdownRuntime"),
15
+ 1,
16
+ [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
17
+ setMarkdownRuntime(extractWorkletRuntime(rt, args[0]));
18
+ return jsi::Value::undefined();
19
+ }));
20
+
21
+ rt.global().setProperty(rt, "jsi_registerMarkdownWorklet", jsi::Function::createFromHostFunction(
22
+ rt,
23
+ jsi::PropNameID::forAscii(rt, "jsi_registerMarkdownWorklet"),
24
+ 1,
25
+ [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
26
+ auto parserId = registerMarkdownWorklet(extractShareableOrThrow<ShareableWorklet>(rt, args[0]));
27
+ return jsi::Value(parserId);
28
+ }));
29
+
30
+ rt.global().setProperty(rt, "jsi_unregisterMarkdownWorklet", jsi::Function::createFromHostFunction(
31
+ rt,
32
+ jsi::PropNameID::forAscii(rt, "jsi_unregisterMarkdownWorklet"),
33
+ 1,
34
+ [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
35
+ auto parserId = static_cast<int>(args[0].asNumber());
36
+ unregisterMarkdownWorklet(parserId);
37
+ return jsi::Value::undefined();
38
+ }));
39
+
40
+ }
41
+
42
+ } // namespace livemarkdown
43
+ } // namespace expensify
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+
5
+ using namespace facebook;
6
+
7
+ namespace expensify {
8
+ namespace livemarkdown {
9
+
10
+ void injectJSIBindings(jsi::Runtime &rt);
11
+
12
+ } // namespace livemarkdown
13
+ } // namespace expensify
@@ -6,13 +6,37 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _reactNative = require("react-native");
8
8
  var _react = _interopRequireDefault(require("react"));
9
+ var _reactNativeReanimated = require("react-native-reanimated");
9
10
  var _MarkdownTextInputDecoratorViewNativeComponent = _interopRequireDefault(require("./MarkdownTextInputDecoratorViewNativeComponent"));
10
11
  var _NativeLiveMarkdownModule = _interopRequireDefault(require("./NativeLiveMarkdownModule"));
11
12
  var _styleUtils = require("./styleUtils");
12
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
14
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
14
- if (_NativeLiveMarkdownModule.default) {
15
+ let initialized = false;
16
+ let workletRuntime;
17
+ function initializeLiveMarkdownIfNeeded() {
18
+ if (initialized) {
19
+ return;
20
+ }
21
+ if (!_NativeLiveMarkdownModule.default) {
22
+ throw new Error('[react-native-live-markdown] NativeLiveMarkdownModule is not available');
23
+ }
15
24
  _NativeLiveMarkdownModule.default.install();
25
+ if (!global.jsi_setMarkdownRuntime) {
26
+ throw new Error('[react-native-live-markdown] global.jsi_setMarkdownRuntime is not available');
27
+ }
28
+ workletRuntime = (0, _reactNativeReanimated.createWorkletRuntime)('LiveMarkdownRuntime');
29
+ global.jsi_setMarkdownRuntime(workletRuntime);
30
+ initialized = true;
31
+ }
32
+ function registerParser(parser) {
33
+ initializeLiveMarkdownIfNeeded();
34
+ const shareableWorklet = (0, _reactNativeReanimated.makeShareableCloneRecursive)(parser);
35
+ const parserId = global.jsi_registerMarkdownWorklet(shareableWorklet);
36
+ return parserId;
37
+ }
38
+ function unregisterParser(parserId) {
39
+ global.jsi_unregisterMarkdownWorklet(parserId);
16
40
  }
17
41
  function processColorsInMarkdownStyle(input) {
18
42
  const output = JSON.parse(JSON.stringify(input));
@@ -34,11 +58,28 @@ function processMarkdownStyle(input) {
34
58
  const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
35
59
  const IS_FABRIC = 'nativeFabricUIManager' in global;
36
60
  const markdownStyle = _react.default.useMemo(() => processMarkdownStyle(props.markdownStyle), [props.markdownStyle]);
61
+ if (props.parser === undefined) {
62
+ throw new Error('[react-native-live-markdown] `parser` is undefined');
63
+ }
64
+
65
+ // eslint-disable-next-line no-underscore-dangle
66
+ const workletHash = props.parser.__workletHash;
67
+ if (workletHash === undefined) {
68
+ throw new Error('[react-native-live-markdown] `parser` is not a worklet');
69
+ }
70
+ const parserId = _react.default.useMemo(() => {
71
+ return registerParser(props.parser);
72
+ // eslint-disable-next-line react-hooks/exhaustive-deps
73
+ }, [workletHash]);
74
+ _react.default.useEffect(() => {
75
+ return () => unregisterParser(parserId);
76
+ }, [parserId]);
37
77
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactNative.TextInput, _extends({}, props, {
38
78
  ref: ref
39
79
  })), /*#__PURE__*/_react.default.createElement(_MarkdownTextInputDecoratorViewNativeComponent.default, {
40
80
  style: IS_FABRIC ? styles.farAway : styles.displayNone,
41
- markdownStyle: markdownStyle
81
+ markdownStyle: markdownStyle,
82
+ parserId: parserId
42
83
  }));
43
84
  });
44
85
  const styles = _reactNative.StyleSheet.create({
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_MarkdownTextInputDecoratorViewNativeComponent","_NativeLiveMarkdownModule","_styleUtils","e","__esModule","default","_extends","Object","assign","bind","n","arguments","length","t","r","hasOwnProperty","call","apply","NativeLiveMarkdownModule","install","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","keys","forEach","key","obj","prop","endsWith","processColor","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","React","forwardRef","props","ref","IS_FABRIC","global","markdownStyle","useMemo","createElement","Fragment","TextInput","style","styles","farAway","displayNone","StyleSheet","create","display","position","top","left","_default","exports"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_reactNativeReanimated","_MarkdownTextInputDecoratorViewNativeComponent","_NativeLiveMarkdownModule","_styleUtils","e","__esModule","default","_extends","Object","assign","bind","n","arguments","length","t","r","hasOwnProperty","call","apply","initialized","workletRuntime","initializeLiveMarkdownIfNeeded","NativeLiveMarkdownModule","Error","install","global","jsi_setMarkdownRuntime","createWorkletRuntime","registerParser","parser","shareableWorklet","makeShareableCloneRecursive","parserId","jsi_registerMarkdownWorklet","unregisterParser","jsi_unregisterMarkdownWorklet","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","keys","forEach","key","obj","prop","endsWith","processColor","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","React","forwardRef","props","ref","IS_FABRIC","markdownStyle","useMemo","undefined","workletHash","__workletHash","useEffect","createElement","Fragment","TextInput","style","styles","farAway","displayNone","StyleSheet","create","display","position","top","left","_default","exports"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":"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","ignoreList":[]}
@@ -18,7 +18,6 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
18
18
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
19
19
  /* eslint-disable @typescript-eslint/no-explicit-any */
20
20
 
21
- require('../parser/react-native-live-markdown-parser.js');
22
21
  const useClientEffect = typeof window === 'undefined' ? _react.useEffect : _react.useLayoutEffect;
23
22
  let focusTimeout = null;
24
23
  const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
@@ -34,6 +33,7 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
34
33
  numberOfLines,
35
34
  multiline = false,
36
35
  markdownStyle,
36
+ parser,
37
37
  onBlur,
38
38
  onChange,
39
39
  onChangeText,
@@ -58,6 +58,12 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
58
58
  addAuthTokenToImageURLCallback,
59
59
  imagePreviewAuthRequiredURLs
60
60
  }, ref) => {
61
+ if (parser === undefined) {
62
+ throw new Error('[react-native-live-markdown] `parser` is undefined');
63
+ }
64
+ if (typeof parser !== 'function') {
65
+ throw new Error('[react-native-live-markdown] `parser` is not a function');
66
+ }
61
67
  const compositionRef = (0, _react.useRef)(false);
62
68
  const divRef = (0, _react.useRef)(null);
63
69
  const currentlyFocusedField = (0, _react.useRef)(null);
@@ -87,7 +93,7 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
87
93
  }
88
94
  return e;
89
95
  }, []);
90
- const parseText = (0, _react.useCallback)((target, text, customMarkdownStyles, cursorPosition = null, shouldAddToHistory = true, shouldForceDOMUpdate = false, shouldScrollIntoView = false) => {
96
+ const parseText = (0, _react.useCallback)((parserFunction, target, text, customMarkdownStyles, cursorPosition = null, shouldAddToHistory = true, shouldForceDOMUpdate = false, shouldScrollIntoView = false) => {
91
97
  if (!divRef.current) {
92
98
  return {
93
99
  text: text || '',
@@ -100,7 +106,7 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
100
106
  cursorPosition: null
101
107
  };
102
108
  }
103
- const parsedText = (0, _parserUtils.updateInputStructure)(target, text, cursorPosition, multiline, customMarkdownStyles, false, shouldForceDOMUpdate, shouldScrollIntoView, {
109
+ const parsedText = (0, _parserUtils.updateInputStructure)(parserFunction, target, text, cursorPosition, multiline, customMarkdownStyles, false, shouldForceDOMUpdate, shouldScrollIntoView, {
104
110
  addAuthTokenToImageURLCallback,
105
111
  imagePreviewAuthRequiredURLs
106
112
  });
@@ -113,10 +119,10 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
113
119
  const processedMarkdownStyle = (0, _react.useMemo)(() => {
114
120
  const newMarkdownStyle = (0, _webStyleUtils.processMarkdownStyle)(markdownStyle);
115
121
  if (divRef.current) {
116
- parseText(divRef.current, divRef.current.value, newMarkdownStyle, null, false, false);
122
+ parseText(parser, divRef.current, divRef.current.value, newMarkdownStyle, null, false, false);
117
123
  }
118
124
  return newMarkdownStyle;
119
- }, [markdownStyle, parseText]);
125
+ }, [parser, markdownStyle, parseText]);
120
126
  const inputStyles = (0, _react.useMemo)(() => _reactNative.StyleSheet.flatten([styles.defaultInputStyles, flattenedStyle && {
121
127
  caretColor: flattenedStyle.color || 'black'
122
128
  }, {
@@ -131,8 +137,8 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
131
137
  }
132
138
  const item = history.current.undo();
133
139
  const undoValue = item ? item.text : null;
134
- return parseText(target, undoValue, processedMarkdownStyle, item ? item.cursorPosition : null, false);
135
- }, [parseText, processedMarkdownStyle]);
140
+ return parseText(parser, target, undoValue, processedMarkdownStyle, item ? item.cursorPosition : null, false);
141
+ }, [parser, parseText, processedMarkdownStyle]);
136
142
  const redo = (0, _react.useCallback)(target => {
137
143
  if (!history.current) {
138
144
  return {
@@ -142,8 +148,8 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
142
148
  }
143
149
  const item = history.current.redo();
144
150
  const redoValue = item ? item.text : null;
145
- return parseText(target, redoValue, processedMarkdownStyle, item ? item.cursorPosition : null, false);
146
- }, [parseText, processedMarkdownStyle]);
151
+ return parseText(parser, target, redoValue, processedMarkdownStyle, item ? item.cursorPosition : null, false);
152
+ }, [parser, parseText, processedMarkdownStyle]);
147
153
 
148
154
  // Placeholder text color logic
149
155
  const updateTextColor = (0, _react.useCallback)((node, text) => {
@@ -255,7 +261,7 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
255
261
  newInputUpdate = redo(divRef.current);
256
262
  break;
257
263
  default:
258
- newInputUpdate = parseText(divRef.current, parsedText, processedMarkdownStyle, newCursorPosition, true, !inputType, inputType === 'pasteText');
264
+ newInputUpdate = parseText(parser, divRef.current, parsedText, processedMarkdownStyle, newCursorPosition, true, !inputType, inputType === 'pasteText');
259
265
  }
260
266
  const {
261
267
  text,
@@ -299,7 +305,7 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
299
305
  onChangeText(text);
300
306
  }
301
307
  handleContentSizeChange();
302
- }, [updateTextColor, updateSelection, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps, maxLength]);
308
+ }, [parser, updateTextColor, updateSelection, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps, maxLength]);
303
309
  const insertText = (0, _react.useCallback)((e, text) => {
304
310
  if (!contentSelection.current || !divRef.current) {
305
311
  return;
@@ -490,14 +496,14 @@ const MarkdownTextInput = /*#__PURE__*/_react.default.forwardRef(({
490
496
  return;
491
497
  }
492
498
  if (value === undefined) {
493
- parseText(divRef.current, divRef.current.value, processedMarkdownStyle);
499
+ parseText(parser, divRef.current, divRef.current.value, processedMarkdownStyle);
494
500
  return;
495
501
  }
496
502
  const normalizedValue = (0, _inputUtils.normalizeValue)(value);
497
503
  divRef.current.value = normalizedValue;
498
- parseText(divRef.current, normalizedValue, processedMarkdownStyle, null, true, false, true);
504
+ parseText(parser, divRef.current, normalizedValue, processedMarkdownStyle, null, true, false, true);
499
505
  updateTextColor(divRef.current, value);
500
- }, [multiline, processedMarkdownStyle, value, maxLength]);
506
+ }, [parser, multiline, processedMarkdownStyle, value, maxLength]);
501
507
  useClientEffect(function adjustHeight() {
502
508
  if (!divRef.current || !multiline) {
503
509
  return;