33 defaultRemarkPlugins ,
44} from "@features/editor/components/MarkdownRenderer" ;
55import { File , GithubLogo , Warning } from "@phosphor-icons/react" ;
6- import { Code , Text } from "@radix-ui/themes" ;
6+ import { Text } from "@radix-ui/themes" ;
77import type { ReactNode } from "react" ;
88import { memo } from "react" ;
99import type { Components } from "react-markdown" ;
@@ -13,6 +13,7 @@ const MENTION_TAG_REGEX =
1313 / < f i l e \s + p a t h = " ( [ ^ " ] + ) " \s * \/ > | < g i t h u b _ i s s u e \s + n u m b e r = " ( [ ^ " ] + ) " (?: \s + t i t l e = " ( [ ^ " ] * ) " ) ? (?: \s + u r l = " ( [ ^ " ] * ) " ) ? \s * \/ > | < e r r o r _ c o n t e x t \s + l a b e l = " ( [ ^ " ] * ) " > [ \s \S ] * ?< \/ e r r o r _ c o n t e x t > / g;
1414const MENTION_TAG_TEST =
1515 / < (?: f i l e \s + p a t h | g i t h u b _ i s s u e \s + n u m b e r | e r r o r _ c o n t e x t \s + l a b e l ) = " [ ^ " ] + " / ;
16+ const SLASH_COMMAND_START = / ^ \/ ( [ a - z A - Z ] [ \w - ] * ) (? = \s | $ ) / ;
1617
1718const inlineComponents : Components = {
1819 ...baseComponents ,
@@ -39,11 +40,14 @@ export const InlineMarkdown = memo(function InlineMarkdown({
3940} ) ;
4041
4142export function hasMentionTags ( content : string ) : boolean {
42- return MENTION_TAG_TEST . test ( content ) ;
43+ return MENTION_TAG_TEST . test ( content ) || SLASH_COMMAND_START . test ( content ) ;
4344}
4445
4546export const hasFileMentions = hasMentionTags ;
4647
48+ const chipClass =
49+ "inline-flex items-center gap-1 rounded-[var(--radius-1)] bg-[var(--accent-a3)] px-1 py-px align-middle font-medium text-[var(--accent-11)]" ;
50+
4751function MentionChip ( {
4852 icon,
4953 label,
@@ -53,32 +57,48 @@ function MentionChip({
5357 label : string ;
5458 onClick ?: ( ) => void ;
5559} ) {
60+ const style = {
61+ fontSize : "var(--font-size-1)" ,
62+ margin : "0 2px" ,
63+ } ;
64+
65+ if ( onClick ) {
66+ return (
67+ < button
68+ type = "button"
69+ className = { `${ chipClass } cursor-pointer border-none` }
70+ onClick = { onClick }
71+ style = { style }
72+ >
73+ { icon }
74+ { label }
75+ </ button >
76+ ) ;
77+ }
78+
5679 return (
57- < Code
58- size = "1"
59- variant = "soft"
60- onClick = { onClick }
61- style = { {
62- display : "inline-flex" ,
63- alignItems : "center" ,
64- gap : "4px" ,
65- verticalAlign : "middle" ,
66- margin : "0 2px" ,
67- cursor : onClick ? "pointer" : undefined ,
68- } }
69- >
80+ < span className = { chipClass } style = { style } >
7081 { icon }
7182 { label }
72- </ Code >
83+ </ span >
7384 ) ;
7485}
7586
7687export function parseMentionTags ( content : string ) : ReactNode [ ] {
7788 const parts : ReactNode [ ] = [ ] ;
7889 let lastIndex = 0 ;
7990
91+ const slashMatch = content . match ( SLASH_COMMAND_START ) ;
92+ if ( slashMatch ) {
93+ parts . push (
94+ < MentionChip key = "slash-cmd" icon = { null } label = { `/${ slashMatch [ 1 ] } ` } /> ,
95+ ) ;
96+ lastIndex = slashMatch [ 0 ] . length ;
97+ }
98+
8099 for ( const match of content . matchAll ( MENTION_TAG_REGEX ) ) {
81100 const matchIndex = match . index ?? 0 ;
101+ if ( matchIndex < lastIndex ) continue ;
82102
83103 if ( matchIndex > lastIndex ) {
84104 parts . push (
0 commit comments