@@ -310,6 +310,10 @@ const runtimeLayers = [
310310} > ;
311311
312312const tuiDemoPhases = [
313+ 'slash' ,
314+ 'mention' ,
315+ 'shell' ,
316+ 'research' ,
313317 'compose' ,
314318 'plan' ,
315319 'delegate' ,
@@ -318,7 +322,30 @@ const tuiDemoPhases = [
318322 'remoteui' ,
319323 'answer' ,
320324] as const ;
321- const tuiDemoDurations = [ 2400 , 1900 , 2600 , 2300 , 2100 , 2600 , 4200 ] ;
325+ type TuiDemoPhase = ( typeof tuiDemoPhases ) [ number ] ;
326+ const tuiDemoDurations = {
327+ slash : 1900 ,
328+ mention : 1900 ,
329+ shell : 2200 ,
330+ research : 2400 ,
331+ compose : 2400 ,
332+ plan : 1900 ,
333+ delegate : 2600 ,
334+ track : 2300 ,
335+ artifact : 2100 ,
336+ remoteui : 2600 ,
337+ answer : 4200 ,
338+ } satisfies Record < TuiDemoPhase , number > ;
339+ const tuiDemoIndex = {
340+ compose : tuiDemoPhases . indexOf ( 'compose' ) ,
341+ plan : tuiDemoPhases . indexOf ( 'plan' ) ,
342+ delegate : tuiDemoPhases . indexOf ( 'delegate' ) ,
343+ track : tuiDemoPhases . indexOf ( 'track' ) ,
344+ artifact : tuiDemoPhases . indexOf ( 'artifact' ) ,
345+ remoteui : tuiDemoPhases . indexOf ( 'remoteui' ) ,
346+ answer : tuiDemoPhases . indexOf ( 'answer' ) ,
347+ } as const ;
348+ type TuiComposerMode = 'default' | 'shell' | 'research' ;
322349type TuiDemoStatus = 'pending' | 'active' | 'done' ;
323350const tuiDemoStatusGlyph : Record < TuiDemoStatus , string > = {
324351 pending : '◻' ,
@@ -446,6 +473,17 @@ const copy = {
446473 tuiWorkspace : '~/workspace/a3s' ,
447474 tuiMode : 'default' ,
448475 tuiTip : '输入消息 · / 打开命令 · Shift+Tab 切换模式 · Ctrl+C 两次退出' ,
476+ tuiSlashInput : '/effort' ,
477+ tuiSlashEffort : '调整推理强度' ,
478+ tuiSlashModel : '切换模型与 Provider' ,
479+ tuiSlashTheme : '选择终端主题' ,
480+ tuiMentionInput : '检查 @AGENTS.md' ,
481+ tuiFilePicker : '@ file · ↑/↓ · →/← folder · Enter · Esc' ,
482+ tuiFileInstructions : '项目指令' ,
483+ tuiFileManifest : 'Rust workspace' ,
484+ tuiFileWebsite : '文档应用' ,
485+ tuiShellInput : 'cargo test -p a3s-code-core' ,
486+ tuiResearchInput : '调研 A3S Code 最新发布与兼容性变化' ,
449487 tuiUser : 'You' ,
450488 tuiWorking : 'Working…' ,
451489 tuiPlan : 'Plan' ,
@@ -546,6 +584,17 @@ const copy = {
546584 tuiMode : 'default' ,
547585 tuiTip :
548586 'Type a message · / for commands · Shift+Tab cycles mode · Ctrl+C twice to exit' ,
587+ tuiSlashInput : '/effort' ,
588+ tuiSlashEffort : 'adjust model effort' ,
589+ tuiSlashModel : 'switch model and provider' ,
590+ tuiSlashTheme : 'select the terminal theme' ,
591+ tuiMentionInput : 'Review @AGENTS.md' ,
592+ tuiFilePicker : '@ file · ↑/↓ · →/← folder · Enter · Esc' ,
593+ tuiFileInstructions : 'workspace instructions' ,
594+ tuiFileManifest : 'Rust workspace' ,
595+ tuiFileWebsite : 'documentation app' ,
596+ tuiShellInput : 'cargo test -p a3s-code-core' ,
597+ tuiResearchInput : 'Research the latest A3S Code releases and compatibility' ,
549598 tuiUser : 'You' ,
550599 tuiWorking : 'Working…' ,
551600 tuiPlan : 'Plan' ,
@@ -678,23 +727,91 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
678727 const [ activeIndex , setActiveIndex ] = useState ( tuiDemoPhases . length - 1 ) ;
679728 const [ isPlaying , setIsPlaying ] = useState ( false ) ;
680729 const [ isVisible , setIsVisible ] = useState ( false ) ;
681- const [ typedCount , setTypedCount ] = useState ( labels . flowTask . length ) ;
730+ const [ typedCount , setTypedCount ] = useState ( 0 ) ;
682731 const active = tuiDemoPhases [ activeIndex ] ?? tuiDemoPhases [ 0 ] ;
732+ const composerDemos : Partial <
733+ Record <
734+ TuiDemoPhase ,
735+ { mode : TuiComposerMode ; symbol : string ; text : string }
736+ >
737+ > = {
738+ slash : { mode : 'default' , symbol : '❯' , text : labels . tuiSlashInput } ,
739+ mention : { mode : 'default' , symbol : '❯' , text : labels . tuiMentionInput } ,
740+ shell : { mode : 'shell' , symbol : '!' , text : labels . tuiShellInput } ,
741+ research : {
742+ mode : 'research' ,
743+ symbol : '?' ,
744+ text : labels . tuiResearchInput ,
745+ } ,
746+ compose : { mode : 'default' , symbol : '❯' , text : labels . flowTask } ,
747+ } ;
748+ const composerDemo = composerDemos [ active ] ;
749+ const composerMode = composerDemo ?. mode ?? 'default' ;
750+ const composerText = composerDemo ?. text ?? '' ;
751+ const typedComposerText = composerText . slice ( 0 , typedCount ) ;
752+ const composerSymbol = composerDemo ?. symbol ?? '❯' ;
753+ const composerStatus =
754+ composerMode === 'research'
755+ ? '◇ deep research · --web | --local-only'
756+ : '◇ high' ;
757+ const mentionStart = typedComposerText . lastIndexOf ( '@' ) ;
758+ const mentionQuery =
759+ mentionStart >= 0
760+ ? typedComposerText . slice ( mentionStart + 1 ) . toLowerCase ( )
761+ : '' ;
762+ const inputMenuIsOpen =
763+ ( active === 'slash' && typedComposerText . startsWith ( '/' ) ) ||
764+ ( active === 'mention' && mentionStart >= 0 ) ;
765+ const slashMenuItems : Array < [ string , string ] > = [
766+ [ '/effort' , labels . tuiSlashEffort ] ,
767+ [ '/model' , labels . tuiSlashModel ] ,
768+ [ '/theme' , labels . tuiSlashTheme ] ,
769+ ] ;
770+ const fileMenuItems : Array < [ string , string ] > = [
771+ [ 'AGENTS.md' , labels . tuiFileInstructions ] ,
772+ [ 'Cargo.toml' , labels . tuiFileManifest ] ,
773+ [ 'website/' , labels . tuiFileWebsite ] ,
774+ ] ;
775+ const inputMenuItems : Array < [ string , string ] > =
776+ active === 'slash'
777+ ? slashMenuItems . filter ( ( [ command ] ) =>
778+ command . startsWith ( typedComposerText || '/' ) ,
779+ )
780+ : active === 'mention'
781+ ? fileMenuItems . filter ( ( [ path ] ) =>
782+ path . toLowerCase ( ) . includes ( mentionQuery ) ,
783+ )
784+ : [ ] ;
683785 const isRunning = isPlaying && isVisible ;
684- const isWorking = activeIndex > 0 && activeIndex < tuiDemoPhases . length - 1 ;
786+ const isWorking =
787+ activeIndex > tuiDemoIndex . compose && activeIndex < tuiDemoIndex . answer ;
685788 const planItems : Array < { label : string ; status : TuiDemoStatus } > = [
686789 {
687790 label : labels . tuiPlanInspect ,
688791 status :
689- activeIndex < 1 ? 'pending' : activeIndex === 1 ? 'active' : 'done' ,
792+ activeIndex < tuiDemoIndex . plan
793+ ? 'pending'
794+ : activeIndex === tuiDemoIndex . plan
795+ ? 'active'
796+ : 'done' ,
690797 } ,
691798 {
692799 label : labels . tuiPlanDelegate ,
693- status : activeIndex < 2 ? 'pending' : activeIndex < 4 ? 'active' : 'done' ,
800+ status :
801+ activeIndex < tuiDemoIndex . delegate
802+ ? 'pending'
803+ : activeIndex < tuiDemoIndex . artifact
804+ ? 'active'
805+ : 'done' ,
694806 } ,
695807 {
696808 label : labels . tuiPlanPublish ,
697- status : activeIndex < 4 ? 'pending' : activeIndex < 5 ? 'active' : 'done' ,
809+ status :
810+ activeIndex < tuiDemoIndex . artifact
811+ ? 'pending'
812+ : activeIndex < tuiDemoIndex . remoteui
813+ ? 'active'
814+ : 'done' ,
698815 } ,
699816 ] ;
700817 const subagents : Array < {
@@ -706,19 +823,19 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
706823 {
707824 name : 'explore' ,
708825 task : labels . tuiAgentExploreTask ,
709- status : activeIndex >= 3 ? 'done' : 'active' ,
826+ status : activeIndex >= tuiDemoIndex . track ? 'done' : 'active' ,
710827 tokens : '0.8k' ,
711828 } ,
712829 {
713830 name : 'test' ,
714831 task : labels . tuiAgentTestTask ,
715- status : activeIndex >= 4 ? 'done' : 'active' ,
832+ status : activeIndex >= tuiDemoIndex . artifact ? 'done' : 'active' ,
716833 tokens : '1.5k' ,
717834 } ,
718835 {
719836 name : 'review' ,
720837 task : labels . tuiAgentReviewTask ,
721- status : activeIndex >= 3 ? 'done' : 'active' ,
838+ status : activeIndex >= tuiDemoIndex . track ? 'done' : 'active' ,
722839 tokens : '0.9k' ,
723840 } ,
724841 ] ;
@@ -727,18 +844,22 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
727844 ) . length ;
728845 const runningSubagentCount = subagents . length - completedSubagentCount ;
729846 const visibleSubagents =
730- activeIndex === 2
847+ activeIndex === tuiDemoIndex . delegate
731848 ? subagents
732- : activeIndex === 3
849+ : activeIndex === tuiDemoIndex . track
733850 ? subagents . filter ( ( agent ) => agent . status === 'active' )
734851 : [ ] ;
735852 const subagentTokens =
736- activeIndex < 3 ? '1.8k' : activeIndex < 4 ? '2.7k' : '3.2k' ;
737- const artifactIsPublished = activeIndex >= 5 ;
738- const remoteUiIsReady = activeIndex >= 6 ;
853+ activeIndex < tuiDemoIndex . track
854+ ? '1.8k'
855+ : activeIndex < tuiDemoIndex . artifact
856+ ? '2.7k'
857+ : '3.2k' ;
858+ const artifactIsPublished = activeIndex >= tuiDemoIndex . remoteui ;
859+ const remoteUiIsReady = activeIndex >= tuiDemoIndex . answer ;
739860 const typingInterval = Math . max (
740- 18 ,
741- Math . floor ( 1550 / Math . max ( labels . flowTask . length , 1 ) ) ,
861+ 16 ,
862+ Math . floor ( 1200 / Math . max ( composerText . length , 1 ) ) ,
742863 ) ;
743864
744865 useEffect ( ( ) => {
@@ -772,36 +893,30 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
772893 useEffect ( ( ) => {
773894 if ( ! isRunning ) return undefined ;
774895
775- const delay = tuiDemoDurations [ activeIndex ] ?? 1600 ;
896+ const delay = tuiDemoDurations [ active ] ?? 1600 ;
776897 const timer = window . setTimeout ( ( ) => {
898+ setTypedCount ( 0 ) ;
777899 if ( activeIndex >= tuiDemoPhases . length - 1 ) {
778- setTypedCount ( 0 ) ;
779900 setActiveIndex ( 0 ) ;
780901 } else {
781902 setActiveIndex ( ( index ) => index + 1 ) ;
782903 }
783904 } , delay ) ;
784905
785906 return ( ) => window . clearTimeout ( timer ) ;
786- } , [ activeIndex , isRunning ] ) ;
907+ } , [ active , activeIndex , isRunning ] ) ;
787908
788909 useEffect ( ( ) => {
789- if ( ! isRunning || activeIndex ! == 0 ) return undefined ;
790- if ( typedCount >= labels . flowTask . length ) return undefined ;
910+ if ( ! isRunning || composerText . length = == 0 ) return undefined ;
911+ if ( typedCount >= composerText . length ) return undefined ;
791912
792913 const timer = window . setTimeout (
793914 ( ) => setTypedCount ( ( count ) => count + 1 ) ,
794915 typingInterval ,
795916 ) ;
796917
797918 return ( ) => window . clearTimeout ( timer ) ;
798- } , [
799- activeIndex ,
800- isRunning ,
801- labels . flowTask . length ,
802- typedCount ,
803- typingInterval ,
804- ] ) ;
919+ } , [ active , composerText . length , isRunning , typedCount , typingInterval ] ) ;
805920
806921 function playFlow ( ) {
807922 if ( window . matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches ) return ;
@@ -862,7 +977,7 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
862977 < p className = "a3s-tui-tip" > { labels . tuiTip } </ p >
863978
864979 < div className = "a3s-tui-transcript" aria-live = "off" >
865- { activeIndex > 0 ? (
980+ { activeIndex > tuiDemoIndex . compose ? (
866981 < article className = "a3s-tui-entry a3s-tui-entry--user" >
867982 < span aria-hidden = "true" > ›</ span >
868983 < div >
@@ -872,7 +987,7 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
872987 </ article >
873988 ) : null }
874989
875- { activeIndex >= 4 ? (
990+ { activeIndex >= tuiDemoIndex . artifact ? (
876991 < article
877992 className = { [
878993 'a3s-tui-artifact' ,
@@ -896,7 +1011,7 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
8961011 < span
8971012 className = { [
8981013 'a3s-tui-open-view' ,
899- activeIndex === 5 ? 'is-opening' : '' ,
1014+ activeIndex === tuiDemoIndex . remoteui ? 'is-opening' : '' ,
9001015 ]
9011016 . filter ( Boolean )
9021017 . join ( ' ' ) }
@@ -944,7 +1059,7 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
9441059 </ article >
9451060 ) : null }
9461061
947- { activeIndex >= 6 ? (
1062+ { activeIndex >= tuiDemoIndex . answer ? (
9481063 < article className = "a3s-tui-entry a3s-tui-entry--assistant" >
9491064 < span aria-hidden = "true" > •</ span >
9501065 < div >
@@ -953,10 +1068,29 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
9531068 </ div >
9541069 </ article >
9551070 ) : null }
1071+
1072+ { inputMenuIsOpen ? (
1073+ < div
1074+ aria-hidden = "true"
1075+ className = { `a3s-tui-input-menu is-${ active } ` }
1076+ >
1077+ { active === 'mention' ? (
1078+ < strong > { labels . tuiFilePicker } </ strong >
1079+ ) : null }
1080+ < ul >
1081+ { inputMenuItems . map ( ( [ label , detail ] , index ) => (
1082+ < li className = { index === 0 ? 'is-selected' : '' } key = { label } >
1083+ < code > { label } </ code >
1084+ < span > { detail } </ span >
1085+ </ li >
1086+ ) ) }
1087+ </ ul >
1088+ </ div >
1089+ ) : null }
9561090 </ div >
9571091 </ section >
9581092
959- < section className = "a3s-tui-composer" >
1093+ < section className = "a3s-tui-composer" data-input-mode = { composerMode } >
9601094 < div className = "a3s-tui-activity" aria-live = "polite" >
9611095 { isWorking ? (
9621096 < >
@@ -966,7 +1100,7 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
9661100 </ >
9671101 ) : null }
9681102 </ div >
969- { activeIndex >= 1 ? (
1103+ { activeIndex >= tuiDemoIndex . plan ? (
9701104 < section className = "a3s-tui-plan" aria-label = { labels . tuiPlan } >
9711105 < ol >
9721106 { planItems . map ( ( item , index ) => (
@@ -980,12 +1114,12 @@ function RuntimeExecutionFlow({ labels }: { labels: (typeof copy)[Locale] }) {
9801114 </ section >
9811115 ) : null }
9821116 < div className = "a3s-tui-effort-rule" >
983- < span > ◇ high </ span >
1117+ < span > { composerStatus } </ span >
9841118 </ div >
9851119 < div className = "a3s-tui-input" >
986- < span aria-hidden = "true" > ❯ </ span >
1120+ < span aria-hidden = "true" > { composerSymbol } </ span >
9871121 < p >
988- { activeIndex === 0 ? labels . flowTask . slice ( 0 , typedCount ) : '' }
1122+ { typedComposerText }
9891123 < i aria-hidden = "true" />
9901124 </ p >
9911125 </ div >
0 commit comments