react-native-enriched-markdown-scaffold 1.0.2-scaffold.1 → 1.0.2-scaffold.3
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/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +18 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +60 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +31 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyRequestEvent.kt +28 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +50 -13
- package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +64 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +19 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +7 -5
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +6 -4
- package/docs/API_REFERENCE.md +60 -4
- package/docs/COPY_OPTIONS.md +40 -0
- package/ios/EnrichedMarkdown.mm +63 -8
- package/ios/EnrichedMarkdownText.mm +26 -5
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +18 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +60 -0
- package/ios/utils/ENRMBlockMenu.h +66 -0
- package/ios/utils/EditMenuUtils.h +32 -6
- package/ios/utils/EditMenuUtils.m +51 -14
- package/ios/views/ENRMCodeBlockContainerView.h +13 -2
- package/ios/views/ENRMCodeBlockContainerView.m +19 -19
- package/ios/views/ENRMMathContainerView.h +8 -0
- package/ios/views/ENRMMathContainerView.m +10 -18
- package/ios/views/TableContainerView.h +8 -0
- package/ios/views/TableContainerView.m +10 -18
- package/lib/module/EnrichedMarkdownNativeComponent.ts +24 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +24 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +29 -2
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +17 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +17 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +53 -10
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +22 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +24 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +24 -0
- package/src/index.tsx +1 -0
- package/src/native/EnrichedMarkdownText.tsx +37 -1
- package/src/types/MarkdownTextProps.ts +50 -10
- package/src/types/events.ts +23 -0
package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp
CHANGED
|
@@ -52,6 +52,18 @@ payload.setProperty(runtime, "language", event.language);
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
void EnrichedMarkdownEventEmitter::onCopyRequest(OnCopyRequest event) const {
|
|
56
|
+
dispatchEvent("copyRequest", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
57
|
+
auto payload = jsi::Object(runtime);
|
|
58
|
+
payload.setProperty(runtime, "source", event.source);
|
|
59
|
+
payload.setProperty(runtime, "markdown", event.markdown);
|
|
60
|
+
payload.setProperty(runtime, "text", event.text);
|
|
61
|
+
payload.setProperty(runtime, "whole", event.whole);
|
|
62
|
+
return payload;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
55
67
|
void EnrichedMarkdownEventEmitter::onContextMenuItemPress(OnContextMenuItemPress event) const {
|
|
56
68
|
dispatchEvent("contextMenuItemPress", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
57
69
|
auto payload = jsi::Object(runtime);
|
|
@@ -379,6 +391,18 @@ payload.setProperty(runtime, "language", event.language);
|
|
|
379
391
|
}
|
|
380
392
|
|
|
381
393
|
|
|
394
|
+
void EnrichedMarkdownTextEventEmitter::onCopyRequest(OnCopyRequest event) const {
|
|
395
|
+
dispatchEvent("copyRequest", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
396
|
+
auto payload = jsi::Object(runtime);
|
|
397
|
+
payload.setProperty(runtime, "source", event.source);
|
|
398
|
+
payload.setProperty(runtime, "markdown", event.markdown);
|
|
399
|
+
payload.setProperty(runtime, "text", event.text);
|
|
400
|
+
payload.setProperty(runtime, "whole", event.whole);
|
|
401
|
+
return payload;
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
382
406
|
void EnrichedMarkdownTextEventEmitter::onContextMenuItemPress(OnContextMenuItemPress event) const {
|
|
383
407
|
dispatchEvent("contextMenuItemPress", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
384
408
|
auto payload = jsi::Object(runtime);
|
package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h
CHANGED
|
@@ -36,6 +36,13 @@ class EnrichedMarkdownEventEmitter : public ViewEventEmitter {
|
|
|
36
36
|
std::string language;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
struct OnCopyRequest {
|
|
40
|
+
std::string source;
|
|
41
|
+
std::string markdown;
|
|
42
|
+
std::string text;
|
|
43
|
+
bool whole;
|
|
44
|
+
};
|
|
45
|
+
|
|
39
46
|
struct OnContextMenuItemPress {
|
|
40
47
|
std::string itemText;
|
|
41
48
|
std::string selectedText;
|
|
@@ -50,6 +57,8 @@ class EnrichedMarkdownEventEmitter : public ViewEventEmitter {
|
|
|
50
57
|
|
|
51
58
|
void onCopyPress(OnCopyPress value) const;
|
|
52
59
|
|
|
60
|
+
void onCopyRequest(OnCopyRequest value) const;
|
|
61
|
+
|
|
53
62
|
void onContextMenuItemPress(OnContextMenuItemPress value) const;
|
|
54
63
|
};
|
|
55
64
|
class EnrichedMarkdownTextInputEventEmitter : public ViewEventEmitter {
|
|
@@ -303,6 +312,13 @@ class EnrichedMarkdownTextEventEmitter : public ViewEventEmitter {
|
|
|
303
312
|
std::string language;
|
|
304
313
|
};
|
|
305
314
|
|
|
315
|
+
struct OnCopyRequest {
|
|
316
|
+
std::string source;
|
|
317
|
+
std::string markdown;
|
|
318
|
+
std::string text;
|
|
319
|
+
bool whole;
|
|
320
|
+
};
|
|
321
|
+
|
|
306
322
|
struct OnContextMenuItemPress {
|
|
307
323
|
std::string itemText;
|
|
308
324
|
std::string selectedText;
|
|
@@ -317,6 +333,8 @@ class EnrichedMarkdownTextEventEmitter : public ViewEventEmitter {
|
|
|
317
333
|
|
|
318
334
|
void onCopyPress(OnCopyPress value) const;
|
|
319
335
|
|
|
336
|
+
void onCopyRequest(OnCopyRequest value) const;
|
|
337
|
+
|
|
320
338
|
void onContextMenuItemPress(OnContextMenuItemPress value) const;
|
|
321
339
|
};
|
|
322
340
|
} // namespace facebook::react
|
|
@@ -2462,6 +2462,11 @@ struct EnrichedMarkdownSelectionMenuConfigStruct {
|
|
|
2462
2462
|
std::string copyImageUrlLabel{};
|
|
2463
2463
|
std::string copyImageUrlsLabel{};
|
|
2464
2464
|
std::vector<std::string> copyImageUrlPluralTemplates{};
|
|
2465
|
+
bool copyEnabled{false};
|
|
2466
|
+
bool copyFormatMarkdown{false};
|
|
2467
|
+
bool systemItemsNone{false};
|
|
2468
|
+
bool codeBlockCopyButton{false};
|
|
2469
|
+
bool interceptCopy{false};
|
|
2465
2470
|
|
|
2466
2471
|
|
|
2467
2472
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -2476,6 +2481,11 @@ struct EnrichedMarkdownSelectionMenuConfigStruct {
|
|
|
2476
2481
|
result["copyImageUrlLabel"] = copyImageUrlLabel;
|
|
2477
2482
|
result["copyImageUrlsLabel"] = copyImageUrlsLabel;
|
|
2478
2483
|
result["copyImageUrlPluralTemplates"] = ::facebook::react::toDynamic(copyImageUrlPluralTemplates);
|
|
2484
|
+
result["copyEnabled"] = copyEnabled;
|
|
2485
|
+
result["copyFormatMarkdown"] = copyFormatMarkdown;
|
|
2486
|
+
result["systemItemsNone"] = systemItemsNone;
|
|
2487
|
+
result["codeBlockCopyButton"] = codeBlockCopyButton;
|
|
2488
|
+
result["interceptCopy"] = interceptCopy;
|
|
2479
2489
|
return result;
|
|
2480
2490
|
}
|
|
2481
2491
|
#endif
|
|
@@ -2512,6 +2522,26 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
2512
2522
|
if (tmp_copyImageUrlPluralTemplates != map.end()) {
|
|
2513
2523
|
fromRawValue(context, tmp_copyImageUrlPluralTemplates->second, result.copyImageUrlPluralTemplates);
|
|
2514
2524
|
}
|
|
2525
|
+
auto tmp_copyEnabled = map.find("copyEnabled");
|
|
2526
|
+
if (tmp_copyEnabled != map.end()) {
|
|
2527
|
+
fromRawValue(context, tmp_copyEnabled->second, result.copyEnabled);
|
|
2528
|
+
}
|
|
2529
|
+
auto tmp_copyFormatMarkdown = map.find("copyFormatMarkdown");
|
|
2530
|
+
if (tmp_copyFormatMarkdown != map.end()) {
|
|
2531
|
+
fromRawValue(context, tmp_copyFormatMarkdown->second, result.copyFormatMarkdown);
|
|
2532
|
+
}
|
|
2533
|
+
auto tmp_systemItemsNone = map.find("systemItemsNone");
|
|
2534
|
+
if (tmp_systemItemsNone != map.end()) {
|
|
2535
|
+
fromRawValue(context, tmp_systemItemsNone->second, result.systemItemsNone);
|
|
2536
|
+
}
|
|
2537
|
+
auto tmp_codeBlockCopyButton = map.find("codeBlockCopyButton");
|
|
2538
|
+
if (tmp_codeBlockCopyButton != map.end()) {
|
|
2539
|
+
fromRawValue(context, tmp_codeBlockCopyButton->second, result.codeBlockCopyButton);
|
|
2540
|
+
}
|
|
2541
|
+
auto tmp_interceptCopy = map.find("interceptCopy");
|
|
2542
|
+
if (tmp_interceptCopy != map.end()) {
|
|
2543
|
+
fromRawValue(context, tmp_interceptCopy->second, result.interceptCopy);
|
|
2544
|
+
}
|
|
2515
2545
|
}
|
|
2516
2546
|
|
|
2517
2547
|
static inline std::string toString(const EnrichedMarkdownSelectionMenuConfigStruct &value) {
|
|
@@ -6415,6 +6445,11 @@ struct EnrichedMarkdownTextSelectionMenuConfigStruct {
|
|
|
6415
6445
|
std::string copyImageUrlLabel{};
|
|
6416
6446
|
std::string copyImageUrlsLabel{};
|
|
6417
6447
|
std::vector<std::string> copyImageUrlPluralTemplates{};
|
|
6448
|
+
bool copyEnabled{false};
|
|
6449
|
+
bool copyFormatMarkdown{false};
|
|
6450
|
+
bool systemItemsNone{false};
|
|
6451
|
+
bool codeBlockCopyButton{false};
|
|
6452
|
+
bool interceptCopy{false};
|
|
6418
6453
|
|
|
6419
6454
|
|
|
6420
6455
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -6429,6 +6464,11 @@ struct EnrichedMarkdownTextSelectionMenuConfigStruct {
|
|
|
6429
6464
|
result["copyImageUrlLabel"] = copyImageUrlLabel;
|
|
6430
6465
|
result["copyImageUrlsLabel"] = copyImageUrlsLabel;
|
|
6431
6466
|
result["copyImageUrlPluralTemplates"] = ::facebook::react::toDynamic(copyImageUrlPluralTemplates);
|
|
6467
|
+
result["copyEnabled"] = copyEnabled;
|
|
6468
|
+
result["copyFormatMarkdown"] = copyFormatMarkdown;
|
|
6469
|
+
result["systemItemsNone"] = systemItemsNone;
|
|
6470
|
+
result["codeBlockCopyButton"] = codeBlockCopyButton;
|
|
6471
|
+
result["interceptCopy"] = interceptCopy;
|
|
6432
6472
|
return result;
|
|
6433
6473
|
}
|
|
6434
6474
|
#endif
|
|
@@ -6465,6 +6505,26 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
6465
6505
|
if (tmp_copyImageUrlPluralTemplates != map.end()) {
|
|
6466
6506
|
fromRawValue(context, tmp_copyImageUrlPluralTemplates->second, result.copyImageUrlPluralTemplates);
|
|
6467
6507
|
}
|
|
6508
|
+
auto tmp_copyEnabled = map.find("copyEnabled");
|
|
6509
|
+
if (tmp_copyEnabled != map.end()) {
|
|
6510
|
+
fromRawValue(context, tmp_copyEnabled->second, result.copyEnabled);
|
|
6511
|
+
}
|
|
6512
|
+
auto tmp_copyFormatMarkdown = map.find("copyFormatMarkdown");
|
|
6513
|
+
if (tmp_copyFormatMarkdown != map.end()) {
|
|
6514
|
+
fromRawValue(context, tmp_copyFormatMarkdown->second, result.copyFormatMarkdown);
|
|
6515
|
+
}
|
|
6516
|
+
auto tmp_systemItemsNone = map.find("systemItemsNone");
|
|
6517
|
+
if (tmp_systemItemsNone != map.end()) {
|
|
6518
|
+
fromRawValue(context, tmp_systemItemsNone->second, result.systemItemsNone);
|
|
6519
|
+
}
|
|
6520
|
+
auto tmp_codeBlockCopyButton = map.find("codeBlockCopyButton");
|
|
6521
|
+
if (tmp_codeBlockCopyButton != map.end()) {
|
|
6522
|
+
fromRawValue(context, tmp_codeBlockCopyButton->second, result.codeBlockCopyButton);
|
|
6523
|
+
}
|
|
6524
|
+
auto tmp_interceptCopy = map.find("interceptCopy");
|
|
6525
|
+
if (tmp_interceptCopy != map.end()) {
|
|
6526
|
+
fromRawValue(context, tmp_interceptCopy->second, result.interceptCopy);
|
|
6527
|
+
}
|
|
6468
6528
|
}
|
|
6469
6529
|
|
|
6470
6530
|
static inline std::string toString(const EnrichedMarkdownTextSelectionMenuConfigStruct &value) {
|
|
@@ -30,6 +30,7 @@ import com.swmansion.enriched.markdown.utils.common.splitASTIntoSegments
|
|
|
30
30
|
import com.swmansion.enriched.markdown.utils.text.TailFadeInAnimator
|
|
31
31
|
import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
|
|
32
32
|
import com.swmansion.enriched.markdown.utils.text.view.applySelectionColors
|
|
33
|
+
import com.swmansion.enriched.markdown.views.BlockMenuOptions
|
|
33
34
|
import com.swmansion.enriched.markdown.views.BlockSegmentView
|
|
34
35
|
import com.swmansion.enriched.markdown.views.CodeBlockContainerView
|
|
35
36
|
import com.swmansion.enriched.markdown.views.TableContainerView
|
|
@@ -105,6 +106,7 @@ class EnrichedMarkdown
|
|
|
105
106
|
private var onCopyPressCallback: ((String, String) -> Unit)? = null
|
|
106
107
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
107
108
|
var onContextMenuItemPressCallback: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
109
|
+
var onCopyRequestCallback: ((source: String, markdown: String, text: String, whole: Boolean) -> Unit)? = null
|
|
108
110
|
var spoilerOverlay: SpoilerOverlay = SpoilerOverlay.PARTICLES
|
|
109
111
|
set(value) {
|
|
110
112
|
if (field == value) return
|
|
@@ -309,8 +311,8 @@ class EnrichedMarkdown
|
|
|
309
311
|
segmentViews.filterIsInstance<EnrichedMarkdownInternalText>().forEach {
|
|
310
312
|
it.selectionMenuConfig = config
|
|
311
313
|
}
|
|
312
|
-
//
|
|
313
|
-
// (e.g. a language change without a remount) to avoid stale
|
|
314
|
+
// Block views cache the copy labels and menu options, so re-push them on
|
|
315
|
+
// update (e.g. a language change without a remount) to avoid stale values.
|
|
314
316
|
pushCopyLabelsToBlockSegments()
|
|
315
317
|
}
|
|
316
318
|
|
|
@@ -322,11 +324,13 @@ class EnrichedMarkdown
|
|
|
322
324
|
view is TableContainerView -> {
|
|
323
325
|
view.copyLabel = copyLabel
|
|
324
326
|
view.copyAsMarkdownLabel = copyAsMarkdownLabel
|
|
327
|
+
view.menuOptions = blockMenuOptions("table")
|
|
325
328
|
}
|
|
326
329
|
|
|
327
330
|
view is CodeBlockContainerView -> {
|
|
328
331
|
view.copyLabel = copyLabel
|
|
329
332
|
view.copyAsMarkdownLabel = copyAsMarkdownLabel
|
|
333
|
+
view.menuOptions = blockMenuOptions("code")
|
|
330
334
|
}
|
|
331
335
|
|
|
332
336
|
isMathContainerView(view) -> {
|
|
@@ -335,6 +339,9 @@ class EnrichedMarkdown
|
|
|
335
339
|
view.javaClass
|
|
336
340
|
.getMethod("setCopyAsMarkdownLabel", String::class.java)
|
|
337
341
|
.invoke(view, copyAsMarkdownLabel)
|
|
342
|
+
view.javaClass
|
|
343
|
+
.getMethod("setMenuOptions", BlockMenuOptions::class.java)
|
|
344
|
+
.invoke(view, blockMenuOptions("math"))
|
|
338
345
|
}
|
|
339
346
|
}
|
|
340
347
|
}
|
|
@@ -361,6 +368,22 @@ class EnrichedMarkdown
|
|
|
361
368
|
}
|
|
362
369
|
}
|
|
363
370
|
|
|
371
|
+
private fun blockMenuOptions(source: String) =
|
|
372
|
+
BlockMenuOptions.from(selectionMenuConfig) { markdown, text ->
|
|
373
|
+
onCopyRequestCallback?.invoke(source, markdown, text, true)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Segments only see their own text, so a selection is the whole component
|
|
377
|
+
// only when it spans the sole segment; the markdown is then the original.
|
|
378
|
+
private fun forwardSegmentCopyRequest(
|
|
379
|
+
markdown: String,
|
|
380
|
+
text: String,
|
|
381
|
+
fullSelection: Boolean,
|
|
382
|
+
) {
|
|
383
|
+
val whole = fullSelection && segmentViews.size == 1 && currentMarkdown.isNotEmpty()
|
|
384
|
+
onCopyRequestCallback?.invoke("selection", if (whole) currentMarkdown else markdown, text, whole)
|
|
385
|
+
}
|
|
386
|
+
|
|
364
387
|
private fun forwardContextMenuItemPress(
|
|
365
388
|
itemText: String,
|
|
366
389
|
selectedText: String,
|
|
@@ -608,6 +631,7 @@ class EnrichedMarkdown
|
|
|
608
631
|
onTaskListItemPressCallback = { taskIndex, checked, itemText ->
|
|
609
632
|
this@EnrichedMarkdown.onTaskListItemPressCallback?.invoke(taskIndex, checked, itemText)
|
|
610
633
|
}
|
|
634
|
+
onCopyRequest = ::forwardSegmentCopyRequest
|
|
611
635
|
|
|
612
636
|
if (contextMenuItemTexts.isNotEmpty()) {
|
|
613
637
|
setContextMenuItems(contextMenuItemTexts, ::forwardContextMenuItemPress)
|
|
@@ -628,6 +652,7 @@ class EnrichedMarkdown
|
|
|
628
652
|
copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
|
|
629
653
|
copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
|
|
630
654
|
contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
|
|
655
|
+
menuOptions = blockMenuOptions("table")
|
|
631
656
|
applyTableNode(segment.node)
|
|
632
657
|
}
|
|
633
658
|
|
|
@@ -638,6 +663,7 @@ class EnrichedMarkdown
|
|
|
638
663
|
copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
|
|
639
664
|
copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
|
|
640
665
|
contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
|
|
666
|
+
menuOptions = blockMenuOptions("code")
|
|
641
667
|
onCopyPress = { code, language ->
|
|
642
668
|
this@EnrichedMarkdown.onCopyPressCallback?.invoke(code, language)
|
|
643
669
|
}
|
|
@@ -667,6 +693,9 @@ class EnrichedMarkdown
|
|
|
667
693
|
resolvedClass
|
|
668
694
|
.getMethod("setCopyAsMarkdownLabel", String::class.java)
|
|
669
695
|
.invoke(view, selectionMenuConfig.copyAsMarkdownLabel)
|
|
696
|
+
resolvedClass
|
|
697
|
+
.getMethod("setMenuOptions", BlockMenuOptions::class.java)
|
|
698
|
+
.invoke(view, blockMenuOptions("math"))
|
|
670
699
|
setMathContextMenuHidden(view)
|
|
671
700
|
resolvedClass.getMethod("applyLatex", String::class.java).invoke(view, segment.latex)
|
|
672
701
|
view
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt
CHANGED
|
@@ -56,6 +56,7 @@ class EnrichedMarkdownInternalText
|
|
|
56
56
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
57
57
|
private var onContextMenuItemPress: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
58
58
|
var selectionMenuConfig: SelectionMenuConfig = SelectionMenuConfig()
|
|
59
|
+
var onCopyRequest: ((markdown: String, text: String, fullSelection: Boolean) -> Unit)? = null
|
|
59
60
|
private var activeActionMode: ActionMode? = null
|
|
60
61
|
var contextMenuHidden: Boolean = false
|
|
61
62
|
set(value) {
|
|
@@ -82,6 +83,9 @@ class EnrichedMarkdownInternalText
|
|
|
82
83
|
onCustomItemPress = { itemText, selectedText, start, end ->
|
|
83
84
|
onContextMenuItemPress?.invoke(itemText, selectedText, start, end)
|
|
84
85
|
},
|
|
86
|
+
onCopyRequest = { markdown, text, fullSelection ->
|
|
87
|
+
onCopyRequest?.invoke(markdown, text, fullSelection)
|
|
88
|
+
},
|
|
85
89
|
)
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -18,6 +18,7 @@ import com.swmansion.enriched.markdown.utils.common.CodeBlockStreamingMode
|
|
|
18
18
|
import com.swmansion.enriched.markdown.utils.common.TableStreamingMode
|
|
19
19
|
import com.swmansion.enriched.markdown.utils.common.emitContextMenuItemPress
|
|
20
20
|
import com.swmansion.enriched.markdown.utils.common.emitCopyPress
|
|
21
|
+
import com.swmansion.enriched.markdown.utils.common.emitCopyRequest
|
|
21
22
|
import com.swmansion.enriched.markdown.utils.common.emitLinkLongPress
|
|
22
23
|
import com.swmansion.enriched.markdown.utils.common.emitLinkPress
|
|
23
24
|
import com.swmansion.enriched.markdown.utils.common.emitTaskListItemPress
|
|
@@ -65,6 +66,10 @@ class EnrichedMarkdownManager :
|
|
|
65
66
|
emitCopyPress(view, code, language)
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
view.onCopyRequestCallback = { source, markdown, text, whole ->
|
|
70
|
+
emitCopyRequest(view, source, markdown, text, whole)
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
return view
|
|
69
74
|
}
|
|
70
75
|
|
|
@@ -68,6 +68,7 @@ class EnrichedMarkdownText
|
|
|
68
68
|
|
|
69
69
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
70
70
|
var onContextMenuItemPressCallback: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
71
|
+
var onCopyRequestCallback: ((markdown: String, text: String, whole: Boolean) -> Unit)? = null
|
|
71
72
|
|
|
72
73
|
var markdownStyle: StyleConfig? = null
|
|
73
74
|
private set
|
|
@@ -113,6 +114,10 @@ class EnrichedMarkdownText
|
|
|
113
114
|
onCustomItemPress = { itemText, selectedText, start, end ->
|
|
114
115
|
onContextMenuItemPressCallback?.invoke(itemText, selectedText, start, end)
|
|
115
116
|
},
|
|
117
|
+
// A full selection already carries the original markdown.
|
|
118
|
+
onCopyRequest = { markdown, text, fullSelection ->
|
|
119
|
+
onCopyRequestCallback?.invoke(markdown, text, fullSelection && currentMarkdown.isNotEmpty())
|
|
120
|
+
},
|
|
116
121
|
)
|
|
117
122
|
}
|
|
118
123
|
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt
CHANGED
|
@@ -15,6 +15,7 @@ import com.facebook.react.viewmanagers.EnrichedMarkdownTextManagerInterface
|
|
|
15
15
|
import com.facebook.yoga.YogaMeasureMode
|
|
16
16
|
import com.swmansion.enriched.markdown.spoiler.SpoilerOverlay
|
|
17
17
|
import com.swmansion.enriched.markdown.utils.common.emitContextMenuItemPress
|
|
18
|
+
import com.swmansion.enriched.markdown.utils.common.emitCopyRequest
|
|
18
19
|
import com.swmansion.enriched.markdown.utils.common.emitLinkLongPress
|
|
19
20
|
import com.swmansion.enriched.markdown.utils.common.emitLinkPress
|
|
20
21
|
import com.swmansion.enriched.markdown.utils.common.emitTaskListItemPress
|
|
@@ -42,6 +43,9 @@ class EnrichedMarkdownTextManager :
|
|
|
42
43
|
view.onContextMenuItemPressCallback = { itemText, selectedText, selectionStart, selectionEnd ->
|
|
43
44
|
emitContextMenuItemPress(view, itemText, selectedText, selectionStart, selectionEnd)
|
|
44
45
|
}
|
|
46
|
+
view.onCopyRequestCallback = { markdown, text, whole ->
|
|
47
|
+
emitCopyRequest(view, "selection", markdown, text, whole)
|
|
48
|
+
}
|
|
45
49
|
|
|
46
50
|
view.setOnLinkPressCallback { url ->
|
|
47
51
|
emitLinkPress(view, url)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
class CopyRequestEvent(
|
|
8
|
+
surfaceId: Int,
|
|
9
|
+
viewId: Int,
|
|
10
|
+
private val source: String,
|
|
11
|
+
private val markdown: String,
|
|
12
|
+
private val text: String,
|
|
13
|
+
private val whole: Boolean,
|
|
14
|
+
) : Event<CopyRequestEvent>(surfaceId, viewId) {
|
|
15
|
+
override fun getEventName(): String = EVENT_NAME
|
|
16
|
+
|
|
17
|
+
override fun getEventData(): WritableMap =
|
|
18
|
+
Arguments.createMap().apply {
|
|
19
|
+
putString("source", source)
|
|
20
|
+
putString("markdown", markdown)
|
|
21
|
+
putString("text", text)
|
|
22
|
+
putBoolean("whole", whole)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
companion object {
|
|
26
|
+
const val EVENT_NAME: String = "onCopyRequest"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -7,6 +7,7 @@ import com.facebook.react.uimanager.UIManagerHelper
|
|
|
7
7
|
import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
|
|
8
8
|
import com.swmansion.enriched.markdown.events.ContextMenuItemPressEvent
|
|
9
9
|
import com.swmansion.enriched.markdown.events.CopyPressEvent
|
|
10
|
+
import com.swmansion.enriched.markdown.events.CopyRequestEvent
|
|
10
11
|
import com.swmansion.enriched.markdown.events.LinkLongPressEvent
|
|
11
12
|
import com.swmansion.enriched.markdown.events.LinkPressEvent
|
|
12
13
|
import com.swmansion.enriched.markdown.events.TaskListItemPressEvent
|
|
@@ -22,6 +23,8 @@ fun markdownEventTypeConstants(): MutableMap<String, Any> {
|
|
|
22
23
|
mapOf("registrationName" to TaskListItemPressEvent.EVENT_NAME)
|
|
23
24
|
map[CopyPressEvent.EVENT_NAME] =
|
|
24
25
|
mapOf("registrationName" to CopyPressEvent.EVENT_NAME)
|
|
26
|
+
map[CopyRequestEvent.EVENT_NAME] =
|
|
27
|
+
mapOf("registrationName" to CopyRequestEvent.EVENT_NAME)
|
|
25
28
|
map[ContextMenuItemPressEvent.EVENT_NAME] =
|
|
26
29
|
mapOf("registrationName" to ContextMenuItemPressEvent.EVENT_NAME)
|
|
27
30
|
return map
|
|
@@ -74,6 +77,21 @@ fun emitCopyPress(
|
|
|
74
77
|
)
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
fun emitCopyRequest(
|
|
81
|
+
view: View,
|
|
82
|
+
source: String,
|
|
83
|
+
markdown: String,
|
|
84
|
+
text: String,
|
|
85
|
+
whole: Boolean,
|
|
86
|
+
) {
|
|
87
|
+
val context = view.context as com.facebook.react.bridge.ReactContext
|
|
88
|
+
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
89
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
90
|
+
eventDispatcher?.dispatchEvent(
|
|
91
|
+
CopyRequestEvent(surfaceId, view.id, source, markdown, text, whole),
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
77
95
|
fun emitContextMenuItemPress(
|
|
78
96
|
view: View,
|
|
79
97
|
itemText: String,
|
|
@@ -128,6 +146,11 @@ fun parseSelectionMenuConfig(value: ReadableMap?): SelectionMenuConfig {
|
|
|
128
146
|
copyImageUrlLabel = value.getString("copyImageUrlLabel") ?: "",
|
|
129
147
|
copyImageUrlsLabel = value.getString("copyImageUrlsLabel") ?: "",
|
|
130
148
|
copyImageUrlPluralTemplates = parseStringList(value.getArray("copyImageUrlPluralTemplates")),
|
|
149
|
+
copyEnabled = value.getBooleanOrDefault("copyEnabled", true),
|
|
150
|
+
copyFormatMarkdown = value.getBooleanOrDefault("copyFormatMarkdown", false),
|
|
151
|
+
systemItemsNone = value.getBooleanOrDefault("systemItemsNone", false),
|
|
152
|
+
codeBlockCopyButton = value.getBooleanOrDefault("codeBlockCopyButton", true),
|
|
153
|
+
interceptCopy = value.getBooleanOrDefault("interceptCopy", false),
|
|
131
154
|
)
|
|
132
155
|
}
|
|
133
156
|
|
package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt
CHANGED
|
@@ -32,6 +32,14 @@ data class SelectionMenuConfig(
|
|
|
32
32
|
// Plural templates indexed by image count (0..100). Empty when no pluralLabels
|
|
33
33
|
// are set; counts > 100 use copyImageUrlsLabel (the "other" form).
|
|
34
34
|
val copyImageUrlPluralTemplates: List<String> = emptyList(),
|
|
35
|
+
val copyEnabled: Boolean = true,
|
|
36
|
+
// Copy writes Markdown (plain text) instead of plain + HTML / raw block content.
|
|
37
|
+
val copyFormatMarkdown: Boolean = false,
|
|
38
|
+
// Drop every system-provided item (Select all, Share, text assist, PROCESS_TEXT…).
|
|
39
|
+
val systemItemsNone: Boolean = false,
|
|
40
|
+
val codeBlockCopyButton: Boolean = true,
|
|
41
|
+
// Copy hands the selection to onCopyRequest instead of writing the clipboard.
|
|
42
|
+
val interceptCopy: Boolean = false,
|
|
35
43
|
)
|
|
36
44
|
|
|
37
45
|
/**
|
|
@@ -63,6 +71,9 @@ fun createSelectionActionModeCallback(
|
|
|
63
71
|
onActionModeChanged: (mode: ActionMode?) -> Unit = {},
|
|
64
72
|
onCustomItemPress: (itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit =
|
|
65
73
|
{ _, _, _, _ -> },
|
|
74
|
+
// Receives the intercepted Copy when `interceptCopy` is set. `fullSelection`
|
|
75
|
+
// is whether the selection spans the whole text view.
|
|
76
|
+
onCopyRequest: (markdown: String, text: String, fullSelection: Boolean) -> Unit = { _, _, _ -> },
|
|
66
77
|
): ActionMode.Callback =
|
|
67
78
|
object : ActionMode.Callback {
|
|
68
79
|
override fun onCreateActionMode(
|
|
@@ -92,19 +103,26 @@ fun createSelectionActionModeCallback(
|
|
|
92
103
|
menu.removeGroup(MENU_ITEM_CUSTOM_GROUP)
|
|
93
104
|
|
|
94
105
|
val selectionMenuConfig = getSelectionMenuConfig()
|
|
106
|
+
val hasSelection = textView.selectionStart >= 0 && textView.selectionEnd > textView.selectionStart
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
if (selectionMenuConfig.systemItemsNone) {
|
|
109
|
+
// Editor re-adds Select all and the text-assist items before calling
|
|
110
|
+
// us on every prepare, so clear on each pass and put back only Copy.
|
|
111
|
+
// Its click is still intercepted in onActionItemClicked.
|
|
112
|
+
menu.clear()
|
|
113
|
+
if (selectionMenuConfig.copyEnabled && hasSelection) {
|
|
114
|
+
menu.add(Menu.NONE, android.R.id.copy, Menu.NONE, selectionMenuConfig.copyLabel)
|
|
115
|
+
}
|
|
116
|
+
} else if (!selectionMenuConfig.copyEnabled) {
|
|
117
|
+
menu.removeItem(android.R.id.copy)
|
|
118
|
+
} else if (selectionMenuConfig.copyLabel.isNotEmpty()) {
|
|
119
|
+
// The system Copy item is added by the platform; relabel it so it
|
|
120
|
+
// matches the rest of the localized menu (iOS rebuilds the menu so
|
|
121
|
+
// gets this for free; Android only intercepts the click).
|
|
100
122
|
menu.findItem(android.R.id.copy)?.title = selectionMenuConfig.copyLabel
|
|
101
123
|
}
|
|
102
124
|
|
|
103
|
-
if (
|
|
104
|
-
selectionMenuConfig.copyAsMarkdown &&
|
|
105
|
-
textView.selectionStart >= 0 &&
|
|
106
|
-
textView.selectionEnd > textView.selectionStart
|
|
107
|
-
) {
|
|
125
|
+
if (selectionMenuConfig.copyAsMarkdown && hasSelection) {
|
|
108
126
|
menu.add(
|
|
109
127
|
Menu.NONE,
|
|
110
128
|
MENU_ITEM_COPY_MARKDOWN,
|
|
@@ -113,7 +131,7 @@ fun createSelectionActionModeCallback(
|
|
|
113
131
|
)
|
|
114
132
|
}
|
|
115
133
|
|
|
116
|
-
if (
|
|
134
|
+
if (hasSelection) {
|
|
117
135
|
val customItems = getCustomItemTexts()
|
|
118
136
|
customItems.forEachIndexed { index, text ->
|
|
119
137
|
menu
|
|
@@ -144,7 +162,12 @@ fun createSelectionActionModeCallback(
|
|
|
144
162
|
|
|
145
163
|
when (itemId) {
|
|
146
164
|
android.R.id.copy -> {
|
|
147
|
-
|
|
165
|
+
val config = getSelectionMenuConfig()
|
|
166
|
+
if (config.interceptCopy) {
|
|
167
|
+
textView.requestCopy(onCopyRequest)
|
|
168
|
+
} else if (!config.copyFormatMarkdown || !textView.copyMarkdownToClipboard()) {
|
|
169
|
+
textView.copyWithHTML()
|
|
170
|
+
}
|
|
148
171
|
mode?.finish()
|
|
149
172
|
return true
|
|
150
173
|
}
|
|
@@ -214,10 +237,24 @@ private fun TextView.copyWithHTML() {
|
|
|
214
237
|
}
|
|
215
238
|
}
|
|
216
239
|
|
|
217
|
-
|
|
218
|
-
|
|
240
|
+
/** Hands the selection to [onCopyRequest] without touching the clipboard. */
|
|
241
|
+
private fun TextView.requestCopy(onCopyRequest: (markdown: String, text: String, fullSelection: Boolean) -> Unit) {
|
|
242
|
+
val start = selectionStart
|
|
243
|
+
val end = selectionEnd
|
|
244
|
+
if (start < 0 || end < 0 || start >= end) return
|
|
245
|
+
|
|
246
|
+
val selectedText = text.subSequence(start, end).toString()
|
|
247
|
+
val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: selectedText
|
|
248
|
+
// Same threshold MarkdownExtractor uses to return the original markdown.
|
|
249
|
+
onCopyRequest(markdown, selectedText, start == 0 && end >= text.length - 1)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Returns false when the selection has no Markdown to copy. */
|
|
253
|
+
private fun TextView.copyMarkdownToClipboard(): Boolean {
|
|
254
|
+
val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: return false
|
|
219
255
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
220
256
|
clipboard.setPrimaryClip(ClipData.newPlainText("Markdown", markdown))
|
|
257
|
+
return true
|
|
221
258
|
}
|
|
222
259
|
|
|
223
260
|
/** Returns remote image URLs (http/https only) from the current selection. */
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.views
|
|
2
|
+
|
|
3
|
+
import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
|
|
4
|
+
|
|
5
|
+
/** Receives an intercepted block Copy: the block's Markdown and its raw content. */
|
|
6
|
+
typealias BlockCopyRequest = (markdown: String, text: String) -> Unit
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Selection-menu options that table, math and code block views need for their
|
|
10
|
+
* own long-press menus. Every default keeps the legacy behavior, so a block view
|
|
11
|
+
* that has not received options yet behaves exactly as before.
|
|
12
|
+
*/
|
|
13
|
+
data class BlockMenuOptions(
|
|
14
|
+
val copyHidden: Boolean = false,
|
|
15
|
+
// The Copy item writes the block's Markdown instead of its raw content.
|
|
16
|
+
val copyWritesMarkdown: Boolean = false,
|
|
17
|
+
val copyAsMarkdownHidden: Boolean = false,
|
|
18
|
+
// Hides the copy button in the code block header.
|
|
19
|
+
val copyButtonHidden: Boolean = false,
|
|
20
|
+
// Set when `onCopy` is on: the Copy item hands the content here instead of
|
|
21
|
+
// writing the clipboard. The host binds it to the block's source.
|
|
22
|
+
val onCopyRequest: BlockCopyRequest? = null,
|
|
23
|
+
) {
|
|
24
|
+
val hasItems: Boolean get() = !copyHidden || !copyAsMarkdownHidden
|
|
25
|
+
|
|
26
|
+
companion object {
|
|
27
|
+
fun from(
|
|
28
|
+
config: SelectionMenuConfig,
|
|
29
|
+
onCopyRequest: BlockCopyRequest? = null,
|
|
30
|
+
) = BlockMenuOptions(
|
|
31
|
+
copyHidden = !config.copyEnabled,
|
|
32
|
+
copyWritesMarkdown = config.copyFormatMarkdown,
|
|
33
|
+
copyAsMarkdownHidden = !config.copyAsMarkdown,
|
|
34
|
+
copyButtonHidden = !config.codeBlockCopyButton,
|
|
35
|
+
onCopyRequest = onCopyRequest.takeIf { config.interceptCopy },
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Adds the Copy / Copy as Markdown items shared by the table, math and code block menus. */
|
|
41
|
+
fun ContextMenuPopup.Builder.blockCopyItems(
|
|
42
|
+
options: BlockMenuOptions,
|
|
43
|
+
copyLabel: String,
|
|
44
|
+
copyAsMarkdownLabel: String,
|
|
45
|
+
// The block's Markdown and raw content, read only for an intercepted Copy.
|
|
46
|
+
markdown: () -> String,
|
|
47
|
+
rawText: () -> String,
|
|
48
|
+
copyRaw: () -> Unit,
|
|
49
|
+
copyMarkdown: () -> Unit,
|
|
50
|
+
) {
|
|
51
|
+
if (!options.copyHidden) {
|
|
52
|
+
val onCopyRequest = options.onCopyRequest
|
|
53
|
+
val onCopy =
|
|
54
|
+
when {
|
|
55
|
+
onCopyRequest != null -> { -> onCopyRequest(markdown(), rawText()) }
|
|
56
|
+
options.copyWritesMarkdown -> copyMarkdown
|
|
57
|
+
else -> copyRaw
|
|
58
|
+
}
|
|
59
|
+
item(ContextMenuPopup.Icon.COPY, copyLabel, onCopy)
|
|
60
|
+
}
|
|
61
|
+
if (!options.copyAsMarkdownHidden) {
|
|
62
|
+
item(ContextMenuPopup.Icon.DOCUMENT, copyAsMarkdownLabel, copyMarkdown)
|
|
63
|
+
}
|
|
64
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt
CHANGED
|
@@ -70,6 +70,15 @@ class CodeBlockContainerView(
|
|
|
70
70
|
// control, not a context menu, and stays.
|
|
71
71
|
var contextMenuHidden: Boolean = false
|
|
72
72
|
|
|
73
|
+
// Copy / Copy as Markdown visibility, Copy format and the header copy button.
|
|
74
|
+
// The header button always copies the raw code.
|
|
75
|
+
var menuOptions: BlockMenuOptions = BlockMenuOptions()
|
|
76
|
+
set(value) {
|
|
77
|
+
field = value
|
|
78
|
+
// Hiding the button leaves the header (and its height) untouched.
|
|
79
|
+
copyButton.visibility = if (value.copyButtonHidden) View.GONE else View.VISIBLE
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
var onCopyPress: ((code: String, language: String) -> Unit)? = null
|
|
74
83
|
|
|
75
84
|
private var code: String = ""
|
|
@@ -250,10 +259,17 @@ class CodeBlockContainerView(
|
|
|
250
259
|
// the event when there is one (a pending block has no menu yet, and a hidden
|
|
251
260
|
// one never does).
|
|
252
261
|
private fun showContextMenu(anchor: View): Boolean {
|
|
253
|
-
if (pending || contextMenuHidden) return false
|
|
262
|
+
if (pending || contextMenuHidden || !menuOptions.hasItems) return false
|
|
254
263
|
ContextMenuPopup.show(anchor, this) {
|
|
255
|
-
|
|
256
|
-
|
|
264
|
+
blockCopyItems(
|
|
265
|
+
menuOptions,
|
|
266
|
+
copyLabel,
|
|
267
|
+
copyAsMarkdownLabel,
|
|
268
|
+
markdown = { CodeBlockNode.fencedMarkdown(code, language, fenceChar) },
|
|
269
|
+
rawText = { code },
|
|
270
|
+
copyRaw = ::copyCode,
|
|
271
|
+
copyMarkdown = ::copyFencedMarkdown,
|
|
272
|
+
)
|
|
257
273
|
}
|
|
258
274
|
return true
|
|
259
275
|
}
|