react-native-jet-markdown 0.2.0-beta.7

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 (157) hide show
  1. package/JetMarkdown.podspec +42 -0
  2. package/LICENSE +20 -0
  3. package/README.md +247 -0
  4. package/android/CMakeLists.txt +52 -0
  5. package/android/build.gradle +55 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/JniBindings.cpp +243 -0
  8. package/android/src/main/java/com/jetmarkdown/JetMarkdownNative.kt +114 -0
  9. package/android/src/main/java/com/jetmarkdown/JetMarkdownPackage.kt +22 -0
  10. package/android/src/main/java/com/jetmarkdown/JetMarkdownView.kt +235 -0
  11. package/android/src/main/java/com/jetmarkdown/JetMarkdownViewManager.kt +75 -0
  12. package/android/src/main/java/com/jetmarkdown/editor/EditorSpans.kt +217 -0
  13. package/android/src/main/java/com/jetmarkdown/editor/JetMarkdownEditorManager.kt +224 -0
  14. package/android/src/main/java/com/jetmarkdown/editor/JetMarkdownEditorView.kt +1736 -0
  15. package/android/src/main/java/com/jetmarkdown/measure/MarkdownMeasurer.kt +54 -0
  16. package/android/src/main/java/com/jetmarkdown/parser/AstDecoder.kt +59 -0
  17. package/android/src/main/java/com/jetmarkdown/parser/MdNode.kt +43 -0
  18. package/android/src/main/java/com/jetmarkdown/render/ContentCache.kt +53 -0
  19. package/android/src/main/java/com/jetmarkdown/render/RenderedContent.kt +318 -0
  20. package/android/src/main/java/com/jetmarkdown/render/SpannableRenderer.kt +658 -0
  21. package/android/src/main/java/com/jetmarkdown/render/spans/ChipSpan.kt +17 -0
  22. package/android/src/main/java/com/jetmarkdown/render/spans/InteractiveSpans.kt +11 -0
  23. package/android/src/main/java/com/jetmarkdown/render/spans/MarkdownLineHeightSpan.kt +59 -0
  24. package/android/src/main/java/com/jetmarkdown/render/spans/RunSpan.kt +40 -0
  25. package/android/src/main/java/com/jetmarkdown/style/Fonts.kt +34 -0
  26. package/android/src/main/java/com/jetmarkdown/style/LayoutStyleSpec.kt +71 -0
  27. package/android/src/main/java/com/jetmarkdown/style/PlatformColorResolver.kt +77 -0
  28. package/android/src/main/java/com/jetmarkdown/style/StyleConfig.kt +80 -0
  29. package/android/src/main/java/com/jetmarkdown/style/TextStyleSpec.kt +61 -0
  30. package/android/src/main/java/com/jetmarkdown/views/BlockStackView.kt +292 -0
  31. package/android/src/main/java/com/jetmarkdown/views/BlockTextView.kt +378 -0
  32. package/android/src/main/java/com/jetmarkdown/views/BoxDrawing.kt +50 -0
  33. package/android/src/main/java/com/jetmarkdown/views/MarkdownHost.kt +12 -0
  34. package/android/src/main/java/com/jetmarkdown/views/MarkdownImageView.kt +113 -0
  35. package/android/src/main/java/com/jetmarkdown/views/NestedHorizontalScrollView.kt +87 -0
  36. package/android/src/main/java/com/jetmarkdown/views/TableBlockView.kt +139 -0
  37. package/cpp/core/Ast.h +85 -0
  38. package/cpp/core/AstJson.cpp +129 -0
  39. package/cpp/core/AstJson.h +12 -0
  40. package/cpp/core/AstSerializer.cpp +40 -0
  41. package/cpp/core/AstSerializer.h +21 -0
  42. package/cpp/core/AstToMarkdown.cpp +549 -0
  43. package/cpp/core/AstToMarkdown.h +16 -0
  44. package/cpp/core/EditorRuns.cpp +640 -0
  45. package/cpp/core/EditorRuns.h +94 -0
  46. package/cpp/core/EditorText.cpp +15 -0
  47. package/cpp/core/EditorText.h +18 -0
  48. package/cpp/core/InlineExtensions.cpp +305 -0
  49. package/cpp/core/InlineExtensions.h +17 -0
  50. package/cpp/core/Parser.cpp +470 -0
  51. package/cpp/core/Parser.h +16 -0
  52. package/cpp/core/Preprocess.cpp +181 -0
  53. package/cpp/core/Preprocess.h +14 -0
  54. package/cpp/md4c/LICENSE +22 -0
  55. package/cpp/md4c/VERSION +1 -0
  56. package/cpp/md4c/md4c.c +6462 -0
  57. package/cpp/md4c/md4c.h +407 -0
  58. package/cpp/react/JetMarkdownEditorShadowNode.cpp +54 -0
  59. package/cpp/react/JetMarkdownEditorShadowNode.h +55 -0
  60. package/cpp/react/JetMarkdownEditorState.h +35 -0
  61. package/cpp/react/JetMarkdownMeasurer.cpp +32 -0
  62. package/cpp/react/JetMarkdownMeasurer.h +41 -0
  63. package/cpp/react/JetMarkdownShadowNode.cpp +100 -0
  64. package/cpp/react/JetMarkdownShadowNode.h +53 -0
  65. package/cpp/react/JetMarkdownState.h +52 -0
  66. package/cpp/react/override/react/renderer/components/JetMarkdownViewSpec/ComponentDescriptors.h +21 -0
  67. package/cpp/tests/parser_tests.cpp +662 -0
  68. package/cpp/tests/run_tests.sh +25 -0
  69. package/ios/JetMarkdownView.h +14 -0
  70. package/ios/JetMarkdownView.mm +468 -0
  71. package/ios/editor/JetMarkdownEditor.h +12 -0
  72. package/ios/editor/JetMarkdownEditor.mm +2002 -0
  73. package/ios/measure/JMDMarkdownMeasurer.h +22 -0
  74. package/ios/measure/JMDMarkdownMeasurer.mm +38 -0
  75. package/ios/render/JMDBlock.h +97 -0
  76. package/ios/render/JMDBlock.m +28 -0
  77. package/ios/render/JMDBlockRenderer.h +17 -0
  78. package/ios/render/JMDBlockRenderer.mm +852 -0
  79. package/ios/render/JMDContentCache.h +17 -0
  80. package/ios/render/JMDContentCache.mm +42 -0
  81. package/ios/render/JMDRenderedContent.h +35 -0
  82. package/ios/render/JMDRenderedContent.mm +274 -0
  83. package/ios/style/JMDFontScale.h +33 -0
  84. package/ios/style/JMDLayoutStyle.h +37 -0
  85. package/ios/style/JMDLayoutStyle.mm +68 -0
  86. package/ios/style/JMDStyleConfig.h +39 -0
  87. package/ios/style/JMDStyleConfig.mm +111 -0
  88. package/ios/style/JMDTextStyle.h +25 -0
  89. package/ios/style/JMDTextStyle.mm +68 -0
  90. package/ios/views/JMDBlockStackView.h +18 -0
  91. package/ios/views/JMDBlockStackView.m +97 -0
  92. package/ios/views/JMDBlockTextView.h +24 -0
  93. package/ios/views/JMDBlockTextView.m +340 -0
  94. package/ios/views/JMDBoxViews.h +33 -0
  95. package/ios/views/JMDBoxViews.m +292 -0
  96. package/ios/views/JMDImageView.h +19 -0
  97. package/ios/views/JMDImageView.m +68 -0
  98. package/ios/views/JMDMarkdownHost.h +16 -0
  99. package/ios/views/JMDTableView.h +16 -0
  100. package/ios/views/JMDTableView.m +172 -0
  101. package/lib/module/JetMarkdownEditor.js +85 -0
  102. package/lib/module/JetMarkdownEditor.js.map +1 -0
  103. package/lib/module/JetMarkdownEditor.native.js +244 -0
  104. package/lib/module/JetMarkdownEditor.native.js.map +1 -0
  105. package/lib/module/JetMarkdownEditorNativeComponent.ts +173 -0
  106. package/lib/module/JetMarkdownView.js +19 -0
  107. package/lib/module/JetMarkdownView.js.map +1 -0
  108. package/lib/module/JetMarkdownView.native.js +44 -0
  109. package/lib/module/JetMarkdownView.native.js.map +1 -0
  110. package/lib/module/JetMarkdownViewNativeComponent.ts +35 -0
  111. package/lib/module/defaultStyles.js +122 -0
  112. package/lib/module/defaultStyles.js.map +1 -0
  113. package/lib/module/index.js +9 -0
  114. package/lib/module/index.js.map +1 -0
  115. package/lib/module/package.json +1 -0
  116. package/lib/module/serializeStyles.js +259 -0
  117. package/lib/module/serializeStyles.js.map +1 -0
  118. package/lib/module/types.js +4 -0
  119. package/lib/module/types.js.map +1 -0
  120. package/lib/module/useJetMarkdownEditor.js +41 -0
  121. package/lib/module/useJetMarkdownEditor.js.map +1 -0
  122. package/lib/typescript/package.json +1 -0
  123. package/lib/typescript/src/JetMarkdownEditor.d.ts +6 -0
  124. package/lib/typescript/src/JetMarkdownEditor.d.ts.map +1 -0
  125. package/lib/typescript/src/JetMarkdownEditor.native.d.ts +6 -0
  126. package/lib/typescript/src/JetMarkdownEditor.native.d.ts.map +1 -0
  127. package/lib/typescript/src/JetMarkdownEditorNativeComponent.d.ts +105 -0
  128. package/lib/typescript/src/JetMarkdownEditorNativeComponent.d.ts.map +1 -0
  129. package/lib/typescript/src/JetMarkdownView.d.ts +3 -0
  130. package/lib/typescript/src/JetMarkdownView.d.ts.map +1 -0
  131. package/lib/typescript/src/JetMarkdownView.native.d.ts +3 -0
  132. package/lib/typescript/src/JetMarkdownView.native.d.ts.map +1 -0
  133. package/lib/typescript/src/JetMarkdownViewNativeComponent.d.ts +28 -0
  134. package/lib/typescript/src/JetMarkdownViewNativeComponent.d.ts.map +1 -0
  135. package/lib/typescript/src/defaultStyles.d.ts +20 -0
  136. package/lib/typescript/src/defaultStyles.d.ts.map +1 -0
  137. package/lib/typescript/src/index.d.ts +7 -0
  138. package/lib/typescript/src/index.d.ts.map +1 -0
  139. package/lib/typescript/src/serializeStyles.d.ts +34 -0
  140. package/lib/typescript/src/serializeStyles.d.ts.map +1 -0
  141. package/lib/typescript/src/types.d.ts +352 -0
  142. package/lib/typescript/src/types.d.ts.map +1 -0
  143. package/lib/typescript/src/useJetMarkdownEditor.d.ts +36 -0
  144. package/lib/typescript/src/useJetMarkdownEditor.d.ts.map +1 -0
  145. package/package.json +150 -0
  146. package/react-native.config.js +11 -0
  147. package/src/JetMarkdownEditor.native.tsx +301 -0
  148. package/src/JetMarkdownEditor.tsx +90 -0
  149. package/src/JetMarkdownEditorNativeComponent.ts +173 -0
  150. package/src/JetMarkdownView.native.tsx +67 -0
  151. package/src/JetMarkdownView.tsx +16 -0
  152. package/src/JetMarkdownViewNativeComponent.ts +35 -0
  153. package/src/defaultStyles.ts +102 -0
  154. package/src/index.tsx +34 -0
  155. package/src/serializeStyles.ts +369 -0
  156. package/src/types.ts +438 -0
  157. package/src/useJetMarkdownEditor.ts +50 -0
@@ -0,0 +1,301 @@
1
+ import {
2
+ type Ref,
3
+ useCallback,
4
+ useImperativeHandle,
5
+ useMemo,
6
+ useRef,
7
+ } from "react";
8
+ import type { NativeSyntheticEvent } from "react-native";
9
+
10
+ import NativeJetMarkdownEditor, {
11
+ Commands,
12
+ } from "./JetMarkdownEditorNativeComponent";
13
+ import {
14
+ type MainStyle,
15
+ serializeStyles,
16
+ splitContainerStyle,
17
+ } from "./serializeStyles";
18
+ import type {
19
+ JetMarkdownEditorProps,
20
+ JetMarkdownEditorRef,
21
+ MarkdownImageData,
22
+ MarkdownSelection,
23
+ } from "./types";
24
+
25
+ type NativeEditor = React.ElementRef<typeof NativeJetMarkdownEditor>;
26
+
27
+ export function JetMarkdownEditor({
28
+ allowFontScaling,
29
+ autoCapitalize,
30
+ autoCorrect,
31
+ autoFocus,
32
+ cursorColor,
33
+ defaultValue,
34
+ editable,
35
+ mentionTriggers,
36
+ multiline,
37
+ onBlur,
38
+ onChangeMarkdown,
39
+ onChangeSelection,
40
+ onChangeState,
41
+ onChangeText,
42
+ onFocus,
43
+ onLinkDetected,
44
+ onMentionChange,
45
+ onMentionEnd,
46
+ onMentionStart,
47
+ onPaste,
48
+ placeholder,
49
+ placeholderTextColor,
50
+ scrollEnabled,
51
+ selectionColor,
52
+ style,
53
+ styles,
54
+ ref,
55
+ }: JetMarkdownEditorProps & { ref?: Ref<JetMarkdownEditorRef> }) {
56
+ const nativeRef = useRef<NativeEditor>(null);
57
+ // getMarkdown resolves from the latest onEditorChangeMarkdown payload;
58
+ // until the serializer emits (E1), plain text doubles as the markdown.
59
+ const markdownRef = useRef<string | null>(null);
60
+ const textRef = useRef<string>(defaultValue ?? "");
61
+
62
+ const { hostStyle, main } = splitContainerStyle(style);
63
+ // maxHeight rides as a native prop (autogrow cap; the editor scrolls
64
+ // internally past it) instead of a host style key. It is omitted when
65
+ // unset: Yoga reads the same raw prop as max-height, and a 0 would clamp
66
+ // flex-sized editors to zero height.
67
+ const { maxHeight, ...editorHostStyle } = hostStyle;
68
+ const mainKey = JSON.stringify(main);
69
+ const stylesJson = useMemo(
70
+ () => serializeStyles(styles, JSON.parse(mainKey) as MainStyle),
71
+ [styles, mainKey]
72
+ );
73
+
74
+ useImperativeHandle(
75
+ ref,
76
+ (): JetMarkdownEditorRef => ({
77
+ blur: () => {
78
+ if (nativeRef.current) {
79
+ Commands.blur(nativeRef.current);
80
+ }
81
+ },
82
+ focus: () => {
83
+ if (nativeRef.current) {
84
+ Commands.focus(nativeRef.current);
85
+ }
86
+ },
87
+ getMarkdown: () =>
88
+ Promise.resolve(markdownRef.current ?? textRef.current),
89
+ insertLink: (url: string, label?: string) => {
90
+ if (nativeRef.current) {
91
+ Commands.insertLink(nativeRef.current, url, label ?? "");
92
+ }
93
+ },
94
+ insertMarkdown: (markdown: string) => {
95
+ if (nativeRef.current) {
96
+ Commands.insertMarkdown(nativeRef.current, markdown);
97
+ }
98
+ },
99
+ insertMention: (trigger: string, label: string, url: string) => {
100
+ if (nativeRef.current) {
101
+ Commands.insertMention(nativeRef.current, trigger, label, url);
102
+ }
103
+ },
104
+ removeLink: () => {
105
+ if (nativeRef.current) {
106
+ Commands.removeLink(nativeRef.current);
107
+ }
108
+ },
109
+ setSelection: (start: number, end: number) => {
110
+ if (nativeRef.current) {
111
+ Commands.setSelection(nativeRef.current, start, end);
112
+ }
113
+ },
114
+ setValue: (markdown: string) => {
115
+ // Seed with the value being set so getMarkdown() never reports the
116
+ // pre-setValue document; native re-emits its canonical serialization
117
+ // right after.
118
+ markdownRef.current = markdown;
119
+ textRef.current = markdown;
120
+ if (nativeRef.current) {
121
+ Commands.setValue(nativeRef.current, markdown);
122
+ }
123
+ },
124
+ toggleBlockQuote: () => {
125
+ if (nativeRef.current) {
126
+ Commands.toggleBlockQuote(nativeRef.current);
127
+ }
128
+ },
129
+ toggleBold: () => {
130
+ if (nativeRef.current) {
131
+ Commands.toggleBold(nativeRef.current);
132
+ }
133
+ },
134
+ toggleCode: () => {
135
+ if (nativeRef.current) {
136
+ Commands.toggleCode(nativeRef.current);
137
+ }
138
+ },
139
+ toggleCodeBlock: () => {
140
+ if (nativeRef.current) {
141
+ Commands.toggleCodeBlock(nativeRef.current);
142
+ }
143
+ },
144
+ toggleHeading: (level: number) => {
145
+ if (nativeRef.current) {
146
+ Commands.toggleHeading(nativeRef.current, level);
147
+ }
148
+ },
149
+ toggleItalic: () => {
150
+ if (nativeRef.current) {
151
+ Commands.toggleItalic(nativeRef.current);
152
+ }
153
+ },
154
+ toggleOrderedList: () => {
155
+ if (nativeRef.current) {
156
+ Commands.toggleOrderedList(nativeRef.current);
157
+ }
158
+ },
159
+ toggleSpoiler: () => {
160
+ if (nativeRef.current) {
161
+ Commands.toggleSpoiler(nativeRef.current);
162
+ }
163
+ },
164
+ toggleStrikethrough: () => {
165
+ if (nativeRef.current) {
166
+ Commands.toggleStrikethrough(nativeRef.current);
167
+ }
168
+ },
169
+ toggleSubscript: () => {
170
+ if (nativeRef.current) {
171
+ Commands.toggleSubscript(nativeRef.current);
172
+ }
173
+ },
174
+ toggleSuperscript: () => {
175
+ if (nativeRef.current) {
176
+ Commands.toggleSuperscript(nativeRef.current);
177
+ }
178
+ },
179
+ toggleUnorderedList: () => {
180
+ if (nativeRef.current) {
181
+ Commands.toggleUnorderedList(nativeRef.current);
182
+ }
183
+ },
184
+ }),
185
+ []
186
+ );
187
+
188
+ // The native side never inserts pasted content itself: it reports the
189
+ // clipboard here, and unless the app's onPaste calls preventDefault()
190
+ // synchronously, the default insertion happens via insertMarkdown.
191
+ const handlePaste = useCallback(
192
+ (
193
+ event: NativeSyntheticEvent<{
194
+ images: readonly MarkdownImageData[];
195
+ text: string;
196
+ }>
197
+ ) => {
198
+ const { images, text } = event.nativeEvent;
199
+ let prevented = false;
200
+ onPaste?.({
201
+ images: images.length > 0 ? [...images] : undefined,
202
+ preventDefault: () => {
203
+ prevented = true;
204
+ },
205
+ text: text.length > 0 ? text : undefined,
206
+ });
207
+ if (!prevented && text.length > 0 && nativeRef.current) {
208
+ Commands.insertMarkdown(nativeRef.current, text);
209
+ }
210
+ },
211
+ [onPaste]
212
+ );
213
+
214
+ return (
215
+ <NativeJetMarkdownEditor
216
+ allowFontScaling={allowFontScaling}
217
+ autoCapitalize={autoCapitalize}
218
+ autoCorrect={autoCorrect}
219
+ autoFocus={autoFocus}
220
+ cursorColor={cursorColor}
221
+ defaultValue={defaultValue}
222
+ editable={editable}
223
+ maxHeight={
224
+ typeof maxHeight === "number" && maxHeight > 0 ? maxHeight : undefined
225
+ }
226
+ mentionTriggers={mentionTriggers}
227
+ multiline={multiline}
228
+ onEditorBlur={onBlur ? () => onBlur() : undefined}
229
+ onEditorChangeMarkdown={(
230
+ event: NativeSyntheticEvent<{ markdown: string }>
231
+ ) => {
232
+ markdownRef.current = event.nativeEvent.markdown;
233
+ onChangeMarkdown?.(event.nativeEvent.markdown);
234
+ }}
235
+ onEditorChangeSelection={
236
+ onChangeSelection
237
+ ? (event: NativeSyntheticEvent<MarkdownSelection>) =>
238
+ onChangeSelection({
239
+ end: event.nativeEvent.end,
240
+ start: event.nativeEvent.start,
241
+ })
242
+ : undefined
243
+ }
244
+ onEditorChangeState={
245
+ onChangeState
246
+ ? (event) => {
247
+ // Copy the declared fields so consumers never see the raw
248
+ // nativeEvent extras (target, etc.).
249
+ const state = event.nativeEvent;
250
+ onChangeState({
251
+ headingLevel: state.headingLevel,
252
+ isBlockQuote: state.isBlockQuote,
253
+ isBold: state.isBold,
254
+ isCodeBlock: state.isCodeBlock,
255
+ isInlineCode: state.isInlineCode,
256
+ isItalic: state.isItalic,
257
+ isOrderedList: state.isOrderedList,
258
+ isSpoiler: state.isSpoiler,
259
+ isStrikethrough: state.isStrikethrough,
260
+ isSubscript: state.isSubscript,
261
+ isSuperscript: state.isSuperscript,
262
+ isUnorderedList: state.isUnorderedList,
263
+ });
264
+ }
265
+ : undefined
266
+ }
267
+ onEditorChangeText={(event: NativeSyntheticEvent<{ text: string }>) => {
268
+ textRef.current = event.nativeEvent.text;
269
+ onChangeText?.(event.nativeEvent.text);
270
+ }}
271
+ onEditorFocus={onFocus ? () => onFocus() : undefined}
272
+ onEditorLinkDetected={
273
+ onLinkDetected
274
+ ? (event: NativeSyntheticEvent<{ url: string }>) =>
275
+ onLinkDetected({ url: event.nativeEvent.url })
276
+ : undefined
277
+ }
278
+ onEditorMentionChange={
279
+ onMentionChange
280
+ ? (event) => onMentionChange(event.nativeEvent)
281
+ : undefined
282
+ }
283
+ onEditorMentionEnd={
284
+ onMentionEnd ? (event) => onMentionEnd(event.nativeEvent) : undefined
285
+ }
286
+ onEditorMentionStart={
287
+ onMentionStart
288
+ ? (event) => onMentionStart(event.nativeEvent)
289
+ : undefined
290
+ }
291
+ onEditorPaste={handlePaste}
292
+ placeholder={placeholder}
293
+ placeholderTextColor={placeholderTextColor}
294
+ ref={nativeRef}
295
+ scrollEnabled={scrollEnabled}
296
+ selectionColor={selectionColor}
297
+ style={editorHostStyle}
298
+ stylesJson={stylesJson}
299
+ />
300
+ );
301
+ }
@@ -0,0 +1,90 @@
1
+ import { type Ref, useImperativeHandle, useRef } from "react";
2
+ import { type StyleProp, TextInput, type TextStyle } from "react-native";
3
+
4
+ import type { JetMarkdownEditorProps, JetMarkdownEditorRef } from "./types";
5
+
6
+ // Non-native (web) fallback: a plain multiline text input over raw markdown.
7
+ export function JetMarkdownEditor({
8
+ allowFontScaling,
9
+ autoCapitalize,
10
+ autoCorrect,
11
+ autoFocus,
12
+ cursorColor,
13
+ defaultValue,
14
+ editable,
15
+ multiline = true,
16
+ onBlur,
17
+ onChangeMarkdown,
18
+ onChangeText,
19
+ onFocus,
20
+ placeholder,
21
+ placeholderTextColor,
22
+ scrollEnabled,
23
+ selectionColor,
24
+ style,
25
+ ref,
26
+ }: JetMarkdownEditorProps & { ref?: Ref<JetMarkdownEditorRef> }) {
27
+ const inputRef = useRef<React.ComponentRef<typeof TextInput>>(null);
28
+ const textRef = useRef(defaultValue ?? "");
29
+
30
+ useImperativeHandle(
31
+ ref,
32
+ (): JetMarkdownEditorRef => ({
33
+ blur: () => inputRef.current?.blur(),
34
+ focus: () => inputRef.current?.focus(),
35
+ getMarkdown: () => Promise.resolve(textRef.current),
36
+ insertLink: () => undefined,
37
+ insertMarkdown: () => undefined,
38
+ insertMention: () => undefined,
39
+ removeLink: () => undefined,
40
+ setSelection: () => {
41
+ // Not supported in the web fallback.
42
+ },
43
+ setValue: (markdown: string) => {
44
+ textRef.current = markdown;
45
+ inputRef.current?.setNativeProps({ text: markdown });
46
+ },
47
+ // Formatting toggles are not supported in the web fallback (raw
48
+ // markdown).
49
+ toggleBlockQuote: () => undefined,
50
+ toggleBold: () => undefined,
51
+ toggleCode: () => undefined,
52
+ toggleCodeBlock: () => undefined,
53
+ toggleHeading: () => undefined,
54
+ toggleItalic: () => undefined,
55
+ toggleOrderedList: () => undefined,
56
+ toggleSpoiler: () => undefined,
57
+ toggleStrikethrough: () => undefined,
58
+ toggleSubscript: () => undefined,
59
+ toggleSuperscript: () => undefined,
60
+ toggleUnorderedList: () => undefined,
61
+ }),
62
+ []
63
+ );
64
+
65
+ return (
66
+ <TextInput
67
+ allowFontScaling={allowFontScaling}
68
+ autoCapitalize={autoCapitalize}
69
+ autoCorrect={autoCorrect}
70
+ autoFocus={autoFocus}
71
+ cursorColor={cursorColor}
72
+ defaultValue={defaultValue}
73
+ editable={editable}
74
+ multiline={multiline}
75
+ onBlur={onBlur}
76
+ onChangeText={(text) => {
77
+ textRef.current = text;
78
+ onChangeText?.(text);
79
+ onChangeMarkdown?.(text);
80
+ }}
81
+ onFocus={onFocus}
82
+ placeholder={placeholder}
83
+ placeholderTextColor={placeholderTextColor}
84
+ ref={inputRef}
85
+ scrollEnabled={scrollEnabled}
86
+ selectionColor={selectionColor}
87
+ style={style as StyleProp<TextStyle>}
88
+ />
89
+ );
90
+ }
@@ -0,0 +1,173 @@
1
+ import {
2
+ type CodegenTypes,
3
+ type ColorValue,
4
+ codegenNativeCommands,
5
+ codegenNativeComponent,
6
+ type HostComponent,
7
+ type ViewProps,
8
+ } from "react-native";
9
+
10
+ interface TextEvent {
11
+ text: string;
12
+ }
13
+
14
+ interface MarkdownEvent {
15
+ markdown: string;
16
+ }
17
+
18
+ interface SelectionEvent {
19
+ end: CodegenTypes.Int32;
20
+ start: CodegenTypes.Int32;
21
+ }
22
+
23
+ interface StateEvent {
24
+ headingLevel: CodegenTypes.Int32;
25
+ isBlockQuote: boolean;
26
+ isBold: boolean;
27
+ isCodeBlock: boolean;
28
+ isInlineCode: boolean;
29
+ isItalic: boolean;
30
+ isOrderedList: boolean;
31
+ isSpoiler: boolean;
32
+ isStrikethrough: boolean;
33
+ isSubscript: boolean;
34
+ isSuperscript: boolean;
35
+ isUnorderedList: boolean;
36
+ }
37
+
38
+ interface UrlEvent {
39
+ url: string;
40
+ }
41
+
42
+ interface MentionEvent {
43
+ trigger: string;
44
+ }
45
+
46
+ interface MentionQueryEvent {
47
+ query: string;
48
+ trigger: string;
49
+ }
50
+
51
+ interface PasteEvent {
52
+ // Codegen's event parser needs inline element types and rejects readonly
53
+ // arrays (the props parser accepts both).
54
+ images: {
55
+ height: CodegenTypes.Double;
56
+ url: string;
57
+ width: CodegenTypes.Double;
58
+ }[];
59
+ text: string;
60
+ }
61
+
62
+ interface NativeProps extends ViewProps {
63
+ allowFontScaling?: CodegenTypes.WithDefault<boolean, true>;
64
+ autoCapitalize?: CodegenTypes.WithDefault<
65
+ "none" | "sentences" | "words" | "characters",
66
+ "sentences"
67
+ >;
68
+ autoCorrect?: CodegenTypes.WithDefault<boolean, true>;
69
+ autoFocus?: CodegenTypes.WithDefault<boolean, false>;
70
+ cursorColor?: ColorValue;
71
+ defaultValue?: string;
72
+ editable?: CodegenTypes.WithDefault<boolean, true>;
73
+ /**
74
+ * Autogrow cap in points; native default 0 = unbounded. Yoga ALSO parses
75
+ * this key out of the raw-props bag as max-height, so the wrapper MUST
76
+ * omit it entirely when unset — an unconditional 0 clamps the node to
77
+ * zero height (dead flex-sized editors). When set, Yoga's clamp equals
78
+ * the autogrow cap, which is exactly the intended geometry.
79
+ */
80
+ maxHeight?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
81
+ mentionTriggers?: readonly string[];
82
+ multiline?: CodegenTypes.WithDefault<boolean, true>;
83
+ onEditorBlur?: CodegenTypes.DirectEventHandler<null>;
84
+ onEditorChangeMarkdown?: CodegenTypes.DirectEventHandler<MarkdownEvent>;
85
+ onEditorChangeSelection?: CodegenTypes.DirectEventHandler<SelectionEvent>;
86
+ onEditorChangeState?: CodegenTypes.DirectEventHandler<StateEvent>;
87
+ onEditorChangeText?: CodegenTypes.DirectEventHandler<TextEvent>;
88
+ onEditorFocus?: CodegenTypes.DirectEventHandler<null>;
89
+ onEditorLinkDetected?: CodegenTypes.DirectEventHandler<UrlEvent>;
90
+ onEditorMentionChange?: CodegenTypes.DirectEventHandler<MentionQueryEvent>;
91
+ onEditorMentionEnd?: CodegenTypes.DirectEventHandler<MentionEvent>;
92
+ onEditorMentionStart?: CodegenTypes.DirectEventHandler<MentionEvent>;
93
+ onEditorPaste?: CodegenTypes.DirectEventHandler<PasteEvent>;
94
+ placeholder?: string;
95
+ placeholderTextColor?: ColorValue;
96
+ scrollEnabled?: CodegenTypes.WithDefault<boolean, false>;
97
+ selectionColor?: ColorValue;
98
+ stylesJson?: string;
99
+ }
100
+
101
+ type EditorComponentType = HostComponent<NativeProps>;
102
+
103
+ interface NativeCommands {
104
+ blur: (viewRef: React.ElementRef<EditorComponentType>) => void;
105
+ focus: (viewRef: React.ElementRef<EditorComponentType>) => void;
106
+ insertLink: (
107
+ viewRef: React.ElementRef<EditorComponentType>,
108
+ url: string,
109
+ label: string
110
+ ) => void;
111
+ insertMarkdown: (
112
+ viewRef: React.ElementRef<EditorComponentType>,
113
+ value: string
114
+ ) => void;
115
+ insertMention: (
116
+ viewRef: React.ElementRef<EditorComponentType>,
117
+ trigger: string,
118
+ label: string,
119
+ url: string
120
+ ) => void;
121
+ removeLink: (viewRef: React.ElementRef<EditorComponentType>) => void;
122
+ setSelection: (
123
+ viewRef: React.ElementRef<EditorComponentType>,
124
+ start: CodegenTypes.Int32,
125
+ end: CodegenTypes.Int32
126
+ ) => void;
127
+ setValue: (
128
+ viewRef: React.ElementRef<EditorComponentType>,
129
+ value: string
130
+ ) => void;
131
+ toggleBlockQuote: (viewRef: React.ElementRef<EditorComponentType>) => void;
132
+ toggleBold: (viewRef: React.ElementRef<EditorComponentType>) => void;
133
+ toggleCode: (viewRef: React.ElementRef<EditorComponentType>) => void;
134
+ toggleCodeBlock: (viewRef: React.ElementRef<EditorComponentType>) => void;
135
+ toggleHeading: (
136
+ viewRef: React.ElementRef<EditorComponentType>,
137
+ level: CodegenTypes.Int32
138
+ ) => void;
139
+ toggleItalic: (viewRef: React.ElementRef<EditorComponentType>) => void;
140
+ toggleOrderedList: (viewRef: React.ElementRef<EditorComponentType>) => void;
141
+ toggleSpoiler: (viewRef: React.ElementRef<EditorComponentType>) => void;
142
+ toggleStrikethrough: (viewRef: React.ElementRef<EditorComponentType>) => void;
143
+ toggleSubscript: (viewRef: React.ElementRef<EditorComponentType>) => void;
144
+ toggleSuperscript: (viewRef: React.ElementRef<EditorComponentType>) => void;
145
+ toggleUnorderedList: (viewRef: React.ElementRef<EditorComponentType>) => void;
146
+ }
147
+
148
+ export const Commands = codegenNativeCommands<NativeCommands>({
149
+ supportedCommands: [
150
+ "blur",
151
+ "focus",
152
+ "insertLink",
153
+ "insertMarkdown",
154
+ "insertMention",
155
+ "removeLink",
156
+ "setSelection",
157
+ "setValue",
158
+ "toggleBlockQuote",
159
+ "toggleBold",
160
+ "toggleCode",
161
+ "toggleCodeBlock",
162
+ "toggleHeading",
163
+ "toggleItalic",
164
+ "toggleOrderedList",
165
+ "toggleSpoiler",
166
+ "toggleStrikethrough",
167
+ "toggleSubscript",
168
+ "toggleSuperscript",
169
+ "toggleUnorderedList",
170
+ ],
171
+ });
172
+
173
+ export default codegenNativeComponent<NativeProps>("JetMarkdownEditor");
@@ -0,0 +1,67 @@
1
+ import { useMemo } from "react";
2
+ import type { NativeSyntheticEvent } from "react-native";
3
+
4
+ import NativeJetMarkdownView from "./JetMarkdownViewNativeComponent";
5
+ import {
6
+ type MainStyle,
7
+ serializeStyles,
8
+ splitContainerStyle,
9
+ } from "./serializeStyles";
10
+ import type {
11
+ JetMarkdownViewProps,
12
+ MarkdownImageEvent,
13
+ MarkdownUrlEvent,
14
+ } from "./types";
15
+
16
+ export function JetMarkdownView({
17
+ allowFontScaling,
18
+ markdown,
19
+ style,
20
+ styles,
21
+ images,
22
+ onLinkPress,
23
+ onLinkLongPress,
24
+ onImagePress,
25
+ }: JetMarkdownViewProps) {
26
+ const { hostStyle, main } = splitContainerStyle(style);
27
+
28
+ const mainKey = JSON.stringify(main);
29
+ const stylesJson = useMemo(
30
+ () => serializeStyles(styles, JSON.parse(mainKey) as MainStyle),
31
+ [styles, mainKey]
32
+ );
33
+
34
+ return (
35
+ <NativeJetMarkdownView
36
+ allowFontScaling={allowFontScaling}
37
+ images={images}
38
+ markdown={markdown}
39
+ onImagePress={
40
+ onImagePress
41
+ ? (event: NativeSyntheticEvent<MarkdownImageEvent>) =>
42
+ onImagePress({
43
+ height: event.nativeEvent.height,
44
+ url: event.nativeEvent.url,
45
+ width: event.nativeEvent.width,
46
+ x: event.nativeEvent.x,
47
+ y: event.nativeEvent.y,
48
+ })
49
+ : undefined
50
+ }
51
+ onLinkLongPress={
52
+ onLinkLongPress
53
+ ? (event: NativeSyntheticEvent<MarkdownUrlEvent>) =>
54
+ onLinkLongPress({ url: event.nativeEvent.url })
55
+ : undefined
56
+ }
57
+ onLinkPress={
58
+ onLinkPress
59
+ ? (event: NativeSyntheticEvent<MarkdownUrlEvent>) =>
60
+ onLinkPress({ url: event.nativeEvent.url })
61
+ : undefined
62
+ }
63
+ style={hostStyle}
64
+ stylesJson={stylesJson}
65
+ />
66
+ );
67
+ }
@@ -0,0 +1,16 @@
1
+ import { type StyleProp, Text, View, type ViewStyle } from "react-native";
2
+
3
+ import type { JetMarkdownViewProps } from "./types";
4
+
5
+ // Non-native (web) fallback: renders the raw markdown as plain text.
6
+ export function JetMarkdownView({
7
+ allowFontScaling,
8
+ markdown,
9
+ style,
10
+ }: JetMarkdownViewProps) {
11
+ return (
12
+ <View style={style as StyleProp<ViewStyle>}>
13
+ <Text allowFontScaling={allowFontScaling}>{markdown}</Text>
14
+ </View>
15
+ );
16
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ type CodegenTypes,
3
+ codegenNativeComponent,
4
+ type ViewProps,
5
+ } from "react-native";
6
+
7
+ interface UrlEvent {
8
+ url: string;
9
+ }
10
+
11
+ interface ImagePressEvent {
12
+ height: CodegenTypes.Double;
13
+ url: string;
14
+ width: CodegenTypes.Double;
15
+ x: CodegenTypes.Double;
16
+ y: CodegenTypes.Double;
17
+ }
18
+
19
+ interface ImageData {
20
+ height: CodegenTypes.Double;
21
+ url: string;
22
+ width: CodegenTypes.Double;
23
+ }
24
+
25
+ interface NativeProps extends ViewProps {
26
+ allowFontScaling?: CodegenTypes.WithDefault<boolean, true>;
27
+ images?: readonly ImageData[];
28
+ markdown: string;
29
+ onImagePress?: CodegenTypes.DirectEventHandler<ImagePressEvent>;
30
+ onLinkLongPress?: CodegenTypes.DirectEventHandler<UrlEvent>;
31
+ onLinkPress?: CodegenTypes.DirectEventHandler<UrlEvent>;
32
+ stylesJson?: string;
33
+ }
34
+
35
+ export default codegenNativeComponent<NativeProps>("JetMarkdownView");