Skip to content

Commit fd51561

Browse files
authored
fix(mobile): extend blockquotes across wrapped lines (#6482)
1 parent 9e20194 commit fd51561

4 files changed

Lines changed: 79 additions & 8 deletions

File tree

apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.ios.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@ import type { MarkdownNode } from "react-native-nitro-markdown/headless";
44

55
import { CopyTextButton } from "./CopyTextButton";
66
import { MarkdownTextPrimitive } from "./MarkdownTextPrimitive";
7-
import {
8-
nativeMarkdownDocumentRuns,
9-
nativeMarkdownListItemBlocks,
10-
nativeMarkdownTextRuns,
11-
} from "./nativeMarkdownText";
7+
import { nativeMarkdownDocumentRuns, nativeMarkdownListItemBlocks } from "./nativeMarkdownText";
128
import { NativeMarkdownSelectableText } from "./NativeMarkdownSelectableText.ios";
139
import type {
1410
MarkdownCodeHighlighter,
1511
MarkdownHighlightedToken,
1612
NativeMarkdownTextStyle,
13+
SelectableMarkdownSkill,
1714
} from "./SelectableMarkdownText.types";
1815

1916
type HighlightedCode = ReadonlyArray<ReadonlyArray<MarkdownHighlightedToken>>;
@@ -48,12 +45,13 @@ function documentFor(node: MarkdownNode): MarkdownNode {
4845

4946
function SelectableNode(props: {
5047
readonly node: MarkdownNode;
48+
readonly skills: ReadonlyArray<SelectableMarkdownSkill>;
5149
readonly textStyle: NativeMarkdownTextStyle;
5250
readonly onLinkPress?: (href: string) => void;
5351
}) {
5452
return (
5553
<NativeMarkdownSelectableText
56-
runs={nativeMarkdownDocumentRuns(documentFor(props.node))}
54+
runs={nativeMarkdownDocumentRuns(documentFor(props.node), props.skills)}
5755
textStyle={props.textStyle}
5856
onLinkPress={props.onLinkPress}
5957
/>
@@ -322,6 +320,7 @@ function collectTableRows(node: MarkdownNode): MarkdownNode[] {
322320

323321
function NativeTable(props: {
324322
readonly node: MarkdownNode;
323+
readonly skills: ReadonlyArray<SelectableMarkdownSkill>;
325324
readonly textStyle: NativeMarkdownTextStyle;
326325
readonly onLinkPress?: (href: string) => void;
327326
}) {
@@ -359,7 +358,7 @@ function NativeTable(props: {
359358
}}
360359
>
361360
<NativeMarkdownSelectableText
362-
runs={nativeMarkdownTextRuns(cell).map((run) =>
361+
runs={nativeMarkdownDocumentRuns(documentFor(cell), props.skills).map((run) =>
363362
rowIndex === 0 || cell.isHeader ? { ...run, bold: true } : run,
364363
)}
365364
textStyle={props.textStyle}
@@ -376,6 +375,7 @@ function NativeTable(props: {
376375

377376
function NativeMarkdownImage(props: {
378377
readonly node: MarkdownNode;
378+
readonly skills: ReadonlyArray<SelectableMarkdownSkill>;
379379
readonly textStyle: NativeMarkdownTextStyle;
380380
readonly onLinkPress?: (href: string) => void;
381381
}) {
@@ -384,6 +384,7 @@ function NativeMarkdownImage(props: {
384384
return (
385385
<SelectableNode
386386
node={props.node}
387+
skills={props.skills}
387388
textStyle={props.textStyle}
388389
onLinkPress={props.onLinkPress}
389390
/>
@@ -445,6 +446,7 @@ function inlineGroups(nodes: ReadonlyArray<MarkdownNode>): MarkdownNode[] {
445446

446447
function NativeMixedParagraph(props: {
447448
readonly node: MarkdownNode;
449+
readonly skills: ReadonlyArray<SelectableMarkdownSkill>;
448450
readonly textStyle: NativeMarkdownTextStyle;
449451
readonly onLinkPress?: (href: string) => void;
450452
}) {
@@ -455,13 +457,15 @@ function NativeMixedParagraph(props: {
455457
<NativeMarkdownImage
456458
key={nodeKey(child, index)}
457459
node={child}
460+
skills={props.skills}
458461
textStyle={props.textStyle}
459462
onLinkPress={props.onLinkPress}
460463
/>
461464
) : (
462465
<SelectableNode
463466
key={nodeKey(child, index)}
464467
node={child}
468+
skills={props.skills}
465469
textStyle={props.textStyle}
466470
onLinkPress={props.onLinkPress}
467471
/>
@@ -473,6 +477,7 @@ function NativeMixedParagraph(props: {
473477

474478
function NativeList(props: {
475479
readonly node: MarkdownNode;
480+
readonly skills: ReadonlyArray<SelectableMarkdownSkill>;
476481
readonly textStyle: NativeMarkdownTextStyle;
477482
readonly highlightCode: MarkdownCodeHighlighter;
478483
readonly onLinkPress?: (href: string) => void;
@@ -534,6 +539,7 @@ function NativeList(props: {
534539
<NativeMarkdownBlock
535540
key={nodeKey(child, childIndex)}
536541
node={child}
542+
skills={props.skills}
537543
textStyle={props.textStyle}
538544
highlightCode={props.highlightCode}
539545
onLinkPress={props.onLinkPress}
@@ -551,6 +557,7 @@ function NativeList(props: {
551557

552558
export function NativeMarkdownBlock(props: {
553559
readonly node: MarkdownNode;
560+
readonly skills: ReadonlyArray<SelectableMarkdownSkill>;
554561
readonly textStyle: NativeMarkdownTextStyle;
555562
readonly highlightCode: MarkdownCodeHighlighter;
556563
readonly onLinkPress?: (href: string) => void;
@@ -566,6 +573,7 @@ export function NativeMarkdownBlock(props: {
566573
<NativeMarkdownBlock
567574
key={nodeKey(child, index)}
568575
node={child}
576+
skills={props.skills}
569577
textStyle={props.textStyle}
570578
highlightCode={props.highlightCode}
571579
onLinkPress={props.onLinkPress}
@@ -587,6 +595,7 @@ export function NativeMarkdownBlock(props: {
587595
return (
588596
<NativeTable
589597
node={props.node}
598+
skills={props.skills}
590599
textStyle={props.textStyle}
591600
onLinkPress={props.onLinkPress}
592601
/>
@@ -595,6 +604,7 @@ export function NativeMarkdownBlock(props: {
595604
return (
596605
<NativeMarkdownImage
597606
node={props.node}
607+
skills={props.skills}
598608
textStyle={props.textStyle}
599609
onLinkPress={props.onLinkPress}
600610
/>
@@ -624,6 +634,7 @@ export function NativeMarkdownBlock(props: {
624634
<NativeMarkdownBlock
625635
key={nodeKey(child, index)}
626636
node={child}
637+
skills={props.skills}
627638
textStyle={props.textStyle}
628639
highlightCode={props.highlightCode}
629640
onLinkPress={props.onLinkPress}
@@ -637,6 +648,7 @@ export function NativeMarkdownBlock(props: {
637648
return (
638649
<NativeList
639650
node={props.node}
651+
skills={props.skills}
640652
textStyle={props.textStyle}
641653
highlightCode={props.highlightCode}
642654
onLinkPress={props.onLinkPress}
@@ -647,12 +659,14 @@ export function NativeMarkdownBlock(props: {
647659
return (props.node.children ?? []).some((child) => child.type === "image") ? (
648660
<NativeMixedParagraph
649661
node={props.node}
662+
skills={props.skills}
650663
textStyle={props.textStyle}
651664
onLinkPress={props.onLinkPress}
652665
/>
653666
) : (
654667
<SelectableNode
655668
node={props.node}
669+
skills={props.skills}
656670
textStyle={props.textStyle}
657671
onLinkPress={props.onLinkPress}
658672
/>
@@ -673,6 +687,7 @@ export function NativeMarkdownBlock(props: {
673687
>
674688
<SelectableNode
675689
node={props.node}
690+
skills={props.skills}
676691
textStyle={props.textStyle}
677692
onLinkPress={props.onLinkPress}
678693
/>
@@ -690,6 +705,7 @@ export function NativeMarkdownBlock(props: {
690705
<NativeMarkdownBlock
691706
key={nodeKey(child, index)}
692707
node={child}
708+
skills={props.skills}
693709
textStyle={props.textStyle}
694710
highlightCode={props.highlightCode}
695711
onLinkPress={props.onLinkPress}
@@ -703,6 +719,7 @@ export function NativeMarkdownBlock(props: {
703719
return (
704720
<SelectableNode
705721
node={props.node}
722+
skills={props.skills}
706723
textStyle={props.textStyle}
707724
onLinkPress={props.onLinkPress}
708725
/>

apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.ios.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export function SelectableMarkdownText({
6969
chunk.kind === "rich" ? (
7070
<NativeMarkdownBlock
7171
node={chunk.node}
72+
skills={skills}
7273
textStyle={textStyle}
7374
highlightCode={highlightCode}
7475
onLinkPress={onLinkPress}

apps/mobile/modules/t3-markdown-text/src/nativeMarkdownText.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ function appendDocumentBlock(
661661
function containsRichBlock(node: MarkdownNode): boolean {
662662
if (
663663
node.type === "code_block" ||
664+
node.type === "blockquote" ||
664665
node.type === "table" ||
665666
node.type === "image" ||
666667
node.type === "horizontal_rule" ||

apps/mobile/src/lib/nativeMarkdownText.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,25 @@ describe("nativeMarkdownDocumentRuns", () => {
173173
]);
174174
});
175175

176+
it("decorates known skill references inside blockquotes", () => {
177+
const node: MarkdownNode = {
178+
type: "blockquote",
179+
children: [
180+
{
181+
type: "paragraph",
182+
children: [{ type: "text", content: "Use $ui for this." }],
183+
},
184+
],
185+
};
186+
187+
expect(nativeMarkdownDocumentRuns(node, [{ name: "ui", displayName: "UI" }])).toContainEqual({
188+
text: "$ui",
189+
role: "body",
190+
skillName: "ui",
191+
skillLabel: "UI",
192+
});
193+
});
194+
176195
it("leaves unknown skill-like text unchanged", () => {
177196
const node: MarkdownNode = {
178197
type: "document",
@@ -328,7 +347,7 @@ describe("nativeMarkdownDocumentRuns", () => {
328347
]);
329348
});
330349

331-
it("includes quotes and fenced code in the same selectable string", () => {
350+
it("preserves quotes and fenced code in document runs", () => {
332351
const node: MarkdownNode = {
333352
type: "document",
334353
children: [
@@ -414,6 +433,39 @@ describe("nativeMarkdownListItemBlocks", () => {
414433
});
415434

416435
describe("nativeMarkdownDocumentChunks", () => {
436+
it("renders plain blockquotes as rich blocks so their marker spans wrapped lines", () => {
437+
const blockquote: MarkdownNode = {
438+
type: "blockquote",
439+
beg: 0,
440+
end: 120,
441+
children: [
442+
{
443+
type: "paragraph",
444+
children: [
445+
{
446+
type: "text",
447+
content:
448+
"Persistent random per-result keys are the strongest design, even when this text wraps.",
449+
},
450+
],
451+
},
452+
],
453+
};
454+
455+
expect(
456+
nativeMarkdownDocumentChunks({
457+
type: "document",
458+
children: [blockquote],
459+
}),
460+
).toEqual([
461+
{
462+
kind: "rich",
463+
key: "rich:blockquote:0:120",
464+
node: blockquote,
465+
},
466+
]);
467+
});
468+
417469
it("keeps headings and plain lists in one selectable document", () => {
418470
const document: MarkdownNode = {
419471
type: "document",

0 commit comments

Comments
 (0)