-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathTerm.razor.cs
More file actions
237 lines (209 loc) · 7.07 KB
/
Term.razor.cs
File metadata and controls
237 lines (209 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.AspNetCore.Components;
using System.Buffers;
namespace BootstrapBlazor.Components;
/// <summary>
/// <para lang="zh">Term 终端组件</para>
/// <para lang="en">Term Component</para>
/// </summary>
[JSModuleAutoLoader("./_content/BootstrapBlazor.Term/Components/Term.razor.js", JSObjectReference = true)]
public partial class Term
{
/// <summary>
/// <para lang="zh">获得/设置 <see cref="TermOptions"/> 实例 默认 null</para>
/// <para lang="en">Gets or sets the <see cref="TermOptions"/> instance Default value is null.</para>
/// </summary>
[Parameter]
public TermOptions? Options { get; set; }
/// <summary>
/// <para lang="zh">获得/设置 收到数据回调</para>
/// <para lang="en">Gets or sets the callback when data is received.</para>
/// </summary>
[Parameter]
public Func<byte[], Task>? OnReceivedAsync { get; set; }
/// <summary>
/// <para lang="zh">获得/设置 终端 Resize 回调</para>
/// <para lang="en">Gets or sets the callback when terminal is resized.</para>
/// </summary>
[Parameter]
public Func<int, int, Task>? OnResize { get; set; }
/// <summary>
/// <para lang="zh">获得/设置 高度 默认 300px</para>
/// <para lang="en">Gets or sets the height. Default is 300px.</para>
/// </summary>
[Parameter]
public string Height { get; set; } = "300px";
/// <summary>
/// <para lang="zh">获得 终端行数</para>
/// <para lang="en">Gets the number of rows in the terminal.</para>
/// </summary>
public int Rows { get; private set; }
/// <summary>
/// <para lang="zh">获得 终端列数</para>
/// <para lang="en">Gets the number of columns in the terminal.</para>
/// </summary>
public int Columns { get; private set; }
private string? ClassString => CssBuilder.Default("bb-term")
.AddClassFromAttributes(AdditionalAttributes)
.Build();
private string? StyleString => CssBuilder.Default()
.AddClass($"height: {Height};", !string.IsNullOrEmpty(Height))
.AddStyleFromAttributes(AdditionalAttributes)
.Build();
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override async Task InvokeInitAsync()
{
await InvokeVoidAsync("init", Id, Interop, Options);
}
/// <summary>
/// <para lang="zh">写入数据</para>
/// <para lang="en">Writes data to the terminal.</para>
/// </summary>
/// <param name="data"></param>
public async Task Write(string data)
{
await InvokeVoidAsync("write", Id, data);
}
/// <summary>
/// <para lang="zh">写入一行文本数据</para>
/// <para lang="en">Writes a line of data to the terminal.</para>
/// </summary>
/// <param name="data"></param>
public async Task WriteLine(string data)
{
await InvokeVoidAsync("writeln", Id, data);
}
/// <summary>
/// <para lang="zh">写入数据 (<see langword="byte[]"/>)</para>
/// <para lang="en">Writes byte array data to the terminal.</para>
/// </summary>
/// <param name="data"></param>
public async Task Write(byte[] data)
{
await InvokeVoidAsync("write", Id, data);
}
/// <summary>
/// <para lang="zh">写入数据 (<see langword="Memory[]"/>)</para>
/// <para lang="en">Writes data to the terminal.</para>
/// </summary>
/// <param name="data"></param>
public async Task Write(Memory<byte> data)
{
await InvokeVoidAsync("write", Id, data);
}
/// <summary>
/// <para lang="zh">清空终端</para>
/// <para lang="en">Clears the terminal.</para>
/// </summary>
public async Task Clear()
{
await InvokeVoidAsync("clear", Id);
}
/// <summary>
/// <para lang="zh">连接流</para>
/// <para lang="en">Connects a stream.</para>
/// </summary>
/// <param name="stream"></param>
public Task Open(Stream stream) => ReadStreamAsync(stream);
private Stream? _stream;
private CancellationTokenSource? _cancellationTokenSource;
private async Task ReadStreamAsync(Stream stream)
{
if (stream is { CanRead: true })
{
_ = Task.Run(() => LoopRead(stream));
}
}
private async Task LoopRead(Stream stream)
{
_stream = stream;
if (_cancellationTokenSource is { IsCancellationRequested: false })
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
try
{
_cancellationTokenSource = new();
using var memoryOwner = MemoryPool<byte>.Shared.Rent(1024);
var buffer = memoryOwner.Memory;
while (_cancellationTokenSource is { IsCancellationRequested: false })
{
if (stream is { CanRead: true })
{
var length = await stream.ReadAsync(buffer, _cancellationTokenSource.Token);
if (length == 0)
{
await Task.Delay(50);
continue;
}
await Write(buffer.Slice(0, length).ToArray());
}
else
{
break;
}
}
}
catch (OperationCanceledException)
{
// ignored
}
catch (Exception ex)
{
await WriteLine($"\r\nError: {ex.Message}");
}
}
/// <summary>
/// <para lang="zh">收到数据回调方法由 JavaScript 调用</para>
/// <para lang="en">Callback when data is received from JS</para>
/// </summary>
/// <param name="data"></param>
[JSInvokable]
public async Task TriggerReceiveDataAsync(byte[] data)
{
if (_stream is { CanWrite: true })
{
await _stream.WriteAsync(data);
await _stream.FlushAsync();
}
if (OnReceivedAsync != null)
{
await OnReceivedAsync(data);
}
}
/// <summary>
/// <para lang="zh">Resize JSInvoke</para>
/// <para lang="en">Callback when terminal is resized from JS.</para>
/// </summary>
/// <param name="rows"></param>
/// <param name="cols"></param>
[JSInvokable]
public async Task TriggerResizeAsync(int rows, int cols)
{
Rows = rows;
Columns = cols;
if (OnResize != null)
{
await OnResize(rows, cols);
}
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="disposing"></param>
protected override async ValueTask DisposeAsync(bool disposing)
{
await base.DisposeAsync(disposing);
if (disposing)
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
}
}