11import { memo , type ReactNode , useEffectEvent , useLayoutEffect , useRef , useState } from "react" ;
22import { Link , Surface , Tooltip , toast } from "@heroui/react" ;
33import { useLingui } from "@lingui/react/macro" ;
4- import { ChevronDown , ChevronUp , Copy } from "lucide-react" ;
4+ import { ChevronDown , ChevronUp , Copy , Sparkles } from "lucide-react" ;
55import type { CanonicalContentBlock , MessageItemPayload } from "@/shared/contracts" ;
66import { friendlyError } from "@/shared/messages" ;
77import { AttachmentBar } from "@/renderer/components/composer/AttachmentBar" ;
@@ -66,8 +66,8 @@ export const UserMessage = memo(function UserMessage({ item, checkpointRevert }:
6666 const { slashCommand, body } = extractLeadingSlashCommand ( rawText ) ;
6767 const text = body ;
6868 const commandPrefixLength = slashCommand ? rawText . length - body . length : 0 ;
69- const hasInlineFileMentions = content . some (
70- ( block ) => block . kind === "file" && block . source !== "attachment" ,
69+ const hasInlineContent = content . some (
70+ ( block ) => block . kind === "skill" || ( block . kind === " file" && block . source !== "attachment" ) ,
7171 ) ;
7272 const attachments = enrichWithSelectorPayloads (
7373 buildUserPromptAttachments ( content ) ,
@@ -205,14 +205,11 @@ export const UserMessage = memo(function UserMessage({ item, checkpointRevert }:
205205 bodyClass = inlineBodyClass ;
206206 bodyContent = (
207207 < >
208- < span className = "poracode-slash-chip poracode-slash-chip--user-message mr-1.5" >
209- < span className = "poracode-slash-chip__slash" > /</ span >
210- < span className = "poracode-slash-chip__name" > { slashCommand } </ span >
211- </ span >
208+ < UserMessageSlashChip icon = "/" label = { slashCommand } />
212209 { renderUserMessageInlineContent ( content , commandPrefixLength , actions ) }
213210 </ >
214211 ) ;
215- } else if ( hasInlineFileMentions ) {
212+ } else if ( hasInlineContent ) {
216213 bodyClass = inlineBodyClass ;
217214 bodyContent = renderUserMessageInlineContent ( content , 0 , actions ) ;
218215 } else if ( text . length > 0 ) {
@@ -351,6 +348,7 @@ function buildUserPromptText(content: CanonicalContentBlock[]): string {
351348 return content
352349 . map ( ( block ) => {
353350 if ( block . kind === "text" ) return block . text ;
351+ if ( block . kind === "skill" ) return block . invocation ;
354352 if ( block . kind === "file" && block . source !== "attachment" ) return block . path ;
355353 return "" ;
356354 } )
@@ -377,6 +375,23 @@ function renderUserMessageInlineContent(
377375 return ;
378376 }
379377
378+ if ( block . kind === "skill" ) {
379+ if ( remainingSkip >= block . invocation . length ) {
380+ remainingSkip -= block . invocation . length ;
381+ return ;
382+ }
383+ remainingSkip = 0 ;
384+ nodes . push (
385+ < UserMessageSlashChip
386+ key = { `skill-${ index } -${ block . name } ` }
387+ icon = { < Sparkles aria-hidden = "true" /> }
388+ label = { block . name }
389+ skillName = { block . name }
390+ /> ,
391+ ) ;
392+ return ;
393+ }
394+
380395 if ( block . kind === "file" ) {
381396 if ( block . source === "attachment" ) return ;
382397 if ( remainingSkip >= block . path . length ) {
@@ -400,6 +415,26 @@ function renderUserMessageInlineContent(
400415 return nodes ;
401416}
402417
418+ function UserMessageSlashChip ( {
419+ icon,
420+ label,
421+ skillName,
422+ } : {
423+ icon : ReactNode ;
424+ label : string ;
425+ skillName ?: string ;
426+ } ) {
427+ return (
428+ < span
429+ className = "poracode-slash-chip poracode-slash-chip--user-message mr-1.5"
430+ { ...( skillName ? { "data-skill-name" : skillName } : { } ) }
431+ >
432+ < span className = "poracode-slash-chip__slash" > { icon } </ span >
433+ < span className = "poracode-slash-chip__name" > { label } </ span >
434+ </ span >
435+ ) ;
436+ }
437+
403438const USER_MESSAGE_URL_RE = / h t t p s ? : \/ \/ [ ^ \s < > " ' ] + / g;
404439
405440function renderUserMessageText ( text : string , keyPrefix : string ) : ReactNode [ ] {
0 commit comments