33// Website: https://www.blazor.zone or https://argozhang.github.io/
44
55using Microsoft . AspNetCore . Components ;
6+ using System . Buffers ;
67
78namespace BootstrapBlazor . Components ;
89
910/// <summary>
1011/// <para lang="zh">Term 终端组件</para>
1112/// <para lang="en">Term Component</para>
1213/// </summary>
13- [ JSModuleAutoLoader ( "./_content/BootstrapBlazor.Term/Components/Term/Term .razor.js" , JSObjectReference = true ) ]
14+ [ JSModuleAutoLoader ( "./_content/BootstrapBlazor.Term/Components/Term.razor.js" , JSObjectReference = true ) ]
1415public partial class Term
1516{
1617 /// <summary>
17- /// <para lang="zh">获得/设置 UI Element</para>
18- /// <para lang="en">Gets or sets the UI Element.</para>
19- /// </summary>
20- private ElementReference Element { get ; set ; }
21-
22- /// <summary>
23- /// <para lang="zh">获得/设置 Options</para>
24- /// <para lang="en">Gets or sets the Options.</para>
18+ /// <para lang="zh">获得/设置 <see cref="TermOptions"/> 实例</para>
19+ /// <para lang="en">Gets or sets the <see cref="TermOptions"/></para>
2520 /// </summary>
2621 [ Parameter ]
2722 public TermOptions Options { get ; set ; } = new TermOptions ( ) ;
@@ -31,7 +26,7 @@ public partial class Term
3126 /// <para lang="en">Gets or sets the callback when data is received.</para>
3227 /// </summary>
3328 [ Parameter ]
34- public Func < byte [ ] , Task > ? OnData { get ; set ; }
29+ public Func < byte [ ] , Task > ? OnReceivedAsync { get ; set ; }
3530
3631 /// <summary>
3732 /// <para lang="zh">获得/设置 终端 Resize 回调</para>
@@ -48,34 +43,29 @@ public partial class Term
4843 public string Height { get ; set ; } = "300px" ;
4944
5045 /// <summary>
51- /// GetClassString
46+ /// <para lang="zh">获得 终端行数</para>
47+ /// <para lang="en">Gets the number of rows in the terminal.</para>
5248 /// </summary>
53- /// <returns></returns>
54- private string ? GetClassString ( ) => CssBuilder . Default ( "bb-term" )
55- . AddClassFromAttributes ( AdditionalAttributes )
56- . Build ( ) ;
49+ public int Rows { get ; private set ; }
5750
5851 /// <summary>
59- /// GetStyleString
52+ /// <para lang="zh">获得 终端列数</para>
53+ /// <para lang="en">Gets the number of columns in the terminal.</para>
6054 /// </summary>
61- /// <returns></returns>
62- private string ? GetStyleString ( ) => CssBuilder . Default ( )
55+ public int Columns { get ; private set ; }
56+
57+ private string ? ClassString => CssBuilder . Default ( "bb-term" )
58+ . AddClassFromAttributes ( AdditionalAttributes )
59+ . Build ( ) ;
60+
61+ private string ? StyleString => CssBuilder . Default ( )
6362 . AddClass ( $ "height: { Height } ;", ! string . IsNullOrEmpty ( Height ) )
6463 . AddStyleFromAttributes ( AdditionalAttributes )
6564 . Build ( ) ;
6665
6766 /// <summary>
68- /// OnInitialized
67+ /// <inheritdoc/>
6968 /// </summary>
70- protected override void OnInitialized ( )
71- {
72- base . OnInitialized ( ) ;
73- }
74-
75- /// <summary>
76- /// InvokeInitAsync
77- /// </summary>
78- /// <returns></returns>
7969 protected override async Task InvokeInitAsync ( )
8070 {
8171 await InvokeVoidAsync ( "init" , Id , Interop , Options ) ;
@@ -86,28 +76,45 @@ protected override async Task InvokeInitAsync()
8676 /// <para lang="en">Writes data to the terminal.</para>
8777 /// </summary>
8878 /// <param name="data"></param>
89- /// <returns></returns>
9079 public async Task Write ( string data )
9180 {
9281 await InvokeVoidAsync ( "write" , Id , data ) ;
9382 }
9483
9584 /// <summary>
96- /// <para lang="zh">写入一行数据 </para>
85+ /// <para lang="zh">写入一行文本数据 </para>
9786 /// <para lang="en">Writes a line of data to the terminal.</para>
9887 /// </summary>
9988 /// <param name="data"></param>
100- /// <returns></returns>
10189 public async Task WriteLine ( string data )
10290 {
10391 await InvokeVoidAsync ( "writeln" , Id , data ) ;
10492 }
10593
94+ /// <summary>
95+ /// <para lang="zh">写入数据 (<see langword="byte[]"/>)</para>
96+ /// <para lang="en">Writes byte array data to the terminal.</para>
97+ /// </summary>
98+ /// <param name="data"></param>
99+ public async Task Write ( byte [ ] data )
100+ {
101+ await InvokeVoidAsync ( "write" , Id , data ) ;
102+ }
103+
104+ /// <summary>
105+ /// <para lang="zh">写入数据 (<see langword="Memory[]"/>)</para>
106+ /// <para lang="en">Writes data to the terminal.</para>
107+ /// </summary>
108+ /// <param name="data"></param>
109+ public async Task Write ( Memory < byte > data )
110+ {
111+ await InvokeVoidAsync ( "write" , Id , data ) ;
112+ }
113+
106114 /// <summary>
107115 /// <para lang="zh">清空终端</para>
108116 /// <para lang="en">Clears the terminal.</para>
109117 /// </summary>
110- /// <returns></returns>
111118 public async Task Clear ( )
112119 {
113120 await InvokeVoidAsync ( "clear" , Id ) ;
@@ -118,116 +125,105 @@ public async Task Clear()
118125 /// <para lang="en">Connects a stream.</para>
119126 /// </summary>
120127 /// <param name="stream"></param>
121- /// <returns></returns>
122- public async Task Open ( Stream stream )
123- {
124- _stream = stream ;
125- _cancellationTokenSource = new CancellationTokenSource ( ) ;
126- _ = ReadStreamAsync ( ) ;
127-
128- await Task . CompletedTask ;
129- }
128+ public Task Open ( Stream stream ) => ReadStreamAsync ( stream ) ;
130129
131130 private Stream ? _stream ;
132131 private CancellationTokenSource ? _cancellationTokenSource ;
133132
134- private async Task ReadStreamAsync ( )
133+ private async Task ReadStreamAsync ( Stream stream )
135134 {
136- if ( _stream == null ) return ;
135+ if ( stream is { CanRead : true } )
136+ {
137+ _ = Task . Run ( ( ) => LoopRead ( stream ) ) ;
138+ }
139+ }
140+
141+ private async Task LoopRead ( Stream stream )
142+ {
143+ _stream = stream ;
144+
145+ if ( _cancellationTokenSource is { IsCancellationRequested : false } )
146+ {
147+ _cancellationTokenSource . Cancel ( ) ;
148+ _cancellationTokenSource . Dispose ( ) ;
149+ }
137150
138- var buffer = new byte [ 1024 ] ;
139151 try
140152 {
141- while ( ! _cancellationTokenSource ! . IsCancellationRequested )
153+ _cancellationTokenSource = new ( ) ;
154+ using var memoryOwner = MemoryPool < byte > . Shared . Rent ( 1024 ) ;
155+ var buffer = memoryOwner . Memory ;
156+ while ( _cancellationTokenSource is { IsCancellationRequested : false } )
142157 {
143- var read = await _stream . ReadAsync ( buffer , _cancellationTokenSource . Token ) ;
144- if ( read == 0 ) break ;
145- var data = new byte [ read ] ;
146- Array . Copy ( buffer , data , read ) ;
147- await Write ( data ) ;
158+ var length = await stream . ReadAsync ( buffer , _cancellationTokenSource . Token ) ;
159+ if ( length == 0 )
160+ {
161+ break ;
162+ }
163+ await Write ( buffer . Slice ( 0 , length ) ) ;
148164 }
149165 }
150- catch ( TaskCanceledException )
166+ catch ( OperationCanceledException )
151167 {
152168 // ignored
153169 }
154170 catch ( Exception ex )
155171 {
156- // Handle error
157172 await WriteLine ( $ "\r \n Error: { ex . Message } ") ;
158173 }
159174 }
160175
161176 /// <summary>
162- /// <para lang="zh">写入数据 (Byte[]) </para>
163- /// <para lang="en">Writes byte array data to the terminal. </para>
177+ /// <para lang="zh">收到数据回调方法由 JavaScript 调用 </para>
178+ /// <para lang="en">Callback when data is received from JS </para>
164179 /// </summary>
165180 /// <param name="data"></param>
166- /// <returns></returns>
167- public async Task Write ( byte [ ] data )
168- {
169- await InvokeVoidAsync ( "write" , Id , data ) ;
170- }
171-
172- /// <summary>
173- /// <para lang="zh">收到数据 JSInvoke</para>
174- /// <para lang="en">Callback when data is received from JS.</para>
175- /// </summary>
176- /// <param name="data"></param>
177- /// <returns></returns>
178181 [ JSInvokable ]
179182 public async Task OnDataAsync ( byte [ ] data )
180183 {
181- if ( _stream != null && _stream . CanWrite )
184+ if ( _stream is { CanWrite : true } )
182185 {
183186 await _stream . WriteAsync ( data ) ;
184187 await _stream . FlushAsync ( ) ;
185188 }
186189
187- if ( OnData != null )
190+ if ( OnReceivedAsync != null )
188191 {
189- await OnData ( data ) ;
192+ await OnReceivedAsync ( data ) ;
190193 }
191194 }
192195
193- /// <summary>
194- /// Dispose
195- /// </summary>
196- /// <param name="disposing"></param>
197- protected override async ValueTask DisposeAsync ( bool disposing )
198- {
199- _cancellationTokenSource ? . Cancel ( ) ;
200- _cancellationTokenSource ? . Dispose ( ) ;
201- await base . DisposeAsync ( disposing ) ;
202- }
203-
204- /// <summary>
205- /// <para lang="zh">获得 终端行数</para>
206- /// <para lang="en">Gets the number of rows in the terminal.</para>
207- /// </summary>
208- public int Rows { get ; private set ; }
209-
210- /// <summary>
211- /// <para lang="zh">获得 终端列数</para>
212- /// <para lang="en">Gets the number of columns in the terminal.</para>
213- /// </summary>
214- public int Columns { get ; private set ; }
215-
216196 /// <summary>
217197 /// <para lang="zh">Resize JSInvoke</para>
218198 /// <para lang="en">Callback when terminal is resized from JS.</para>
219199 /// </summary>
220200 /// <param name="rows"></param>
221201 /// <param name="cols"></param>
222- /// <returns></returns>
223202 [ JSInvokable ]
224203 public async Task OnResizeAsync ( int rows , int cols )
225204 {
226205 Rows = rows ;
227206 Columns = cols ;
207+
228208 if ( OnResize != null )
229209 {
230210 await OnResize ( rows , cols ) ;
231211 }
232212 }
213+
214+ /// <summary>
215+ /// <inheritdoc/>
216+ /// </summary>
217+ /// <param name="disposing"></param>
218+ protected override async ValueTask DisposeAsync ( bool disposing )
219+ {
220+ await base . DisposeAsync ( disposing ) ;
221+
222+ if ( disposing )
223+ {
224+ _cancellationTokenSource ? . Cancel ( ) ;
225+ _cancellationTokenSource ? . Dispose ( ) ;
226+ _cancellationTokenSource = null ;
227+ }
228+ }
233229}
0 commit comments