Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 80d5b5a

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommittedApr 13, 2023
fix: propagate monitor errors to the frontend
- Handle when the board's platform is not installed (Closes #1974) - UX: Smoother monitor widget reset (Closes #1985) - Fixed monitor <input> readOnly state (Closes #1984) - Set monitor widget header color (Ref #682) Closes #1508 Signed-off-by: Akos Kitta <[email protected]>
1 parent ab5c63c commit 80d5b5a

16 files changed

+718
-353
lines changed
 

‎arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -496,15 +496,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
496496
bind(TabBarToolbarContribution).toService(MonitorViewContribution);
497497
bind(WidgetFactory).toDynamicValue((context) => ({
498498
id: MonitorWidget.ID,
499-
createWidget: () => {
500-
return new MonitorWidget(
501-
context.container.get<MonitorModel>(MonitorModel),
502-
context.container.get<MonitorManagerProxyClient>(
503-
MonitorManagerProxyClient
504-
),
505-
context.container.get<BoardsServiceProvider>(BoardsServiceProvider)
506-
);
507-
},
499+
createWidget: () => context.container.get(MonitorWidget),
508500
}));
509501

510502
bind(MonitorManagerProxyFactory).toFactory(

‎arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import {
2-
CommandRegistry,
2+
ApplicationError,
33
Disposable,
44
Emitter,
55
MessageService,
66
nls,
77
} from '@theia/core';
8+
import { Deferred } from '@theia/core/lib/common/promise-util';
89
import { inject, injectable } from '@theia/core/shared/inversify';
10+
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
11+
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
912
import { Board, Port } from '../common/protocol';
1013
import {
1114
Monitor,
@@ -23,21 +26,31 @@ import { BoardsServiceProvider } from './boards/boards-service-provider';
2326
export class MonitorManagerProxyClientImpl
2427
implements MonitorManagerProxyClient
2528
{
29+
@inject(MessageService)
30+
private readonly messageService: MessageService;
31+
// This is necessary to call the backend methods from the frontend
32+
@inject(MonitorManagerProxyFactory)
33+
private readonly server: MonitorManagerProxyFactory;
34+
@inject(BoardsServiceProvider)
35+
private readonly boardsServiceProvider: BoardsServiceProvider;
36+
@inject(NotificationManager)
37+
private readonly notificationManager: NotificationManager;
38+
2639
// When pluggable monitor messages are received from the backend
2740
// this event is triggered.
2841
// Ideally a frontend component is connected to this event
2942
// to update the UI.
30-
protected readonly onMessagesReceivedEmitter = new Emitter<{
43+
private readonly onMessagesReceivedEmitter = new Emitter<{
3144
messages: string[];
3245
}>();
3346
readonly onMessagesReceived = this.onMessagesReceivedEmitter.event;
3447

35-
protected readonly onMonitorSettingsDidChangeEmitter =
48+
private readonly onMonitorSettingsDidChangeEmitter =
3649
new Emitter<MonitorSettings>();
3750
readonly onMonitorSettingsDidChange =
3851
this.onMonitorSettingsDidChangeEmitter.event;
3952

40-
protected readonly onMonitorShouldResetEmitter = new Emitter();
53+
private readonly onMonitorShouldResetEmitter = new Emitter<void>();
4154
readonly onMonitorShouldReset = this.onMonitorShouldResetEmitter.event;
4255

4356
// WebSocket used to handle pluggable monitor communication between
@@ -51,29 +64,16 @@ export class MonitorManagerProxyClientImpl
5164
return this.wsPort;
5265
}
5366

54-
constructor(
55-
@inject(MessageService)
56-
protected messageService: MessageService,
57-
58-
// This is necessary to call the backend methods from the frontend
59-
@inject(MonitorManagerProxyFactory)
60-
protected server: MonitorManagerProxyFactory,
61-
62-
@inject(CommandRegistry)
63-
protected readonly commandRegistry: CommandRegistry,
64-
65-
@inject(BoardsServiceProvider)
66-
protected readonly boardsServiceProvider: BoardsServiceProvider
67-
) {}
68-
6967
/**
7068
* Connects a localhost WebSocket using the specified port.
7169
* @param addressPort port of the WebSocket
7270
*/
7371
async connect(addressPort: number): Promise<void> {
74-
if (!!this.webSocket) {
75-
if (this.wsPort === addressPort) return;
76-
else this.disconnect();
72+
if (this.webSocket) {
73+
if (this.wsPort === addressPort) {
74+
return;
75+
}
76+
this.disconnect();
7777
}
7878
try {
7979
this.webSocket = new WebSocket(`ws://localhost:${addressPort}`);
@@ -87,6 +87,9 @@ export class MonitorManagerProxyClientImpl
8787
return;
8888
}
8989

90+
const opened = new Deferred<void>();
91+
this.webSocket.onopen = () => opened.resolve();
92+
this.webSocket.onerror = () => opened.reject();
9093
this.webSocket.onmessage = (message) => {
9194
const parsedMessage = JSON.parse(message.data);
9295
if (Array.isArray(parsedMessage))
@@ -99,19 +102,26 @@ export class MonitorManagerProxyClientImpl
99102
}
100103
};
101104
this.wsPort = addressPort;
105+
return opened.promise;
102106
}
103107

104108
/**
105109
* Disconnects the WebSocket if connected.
106110
*/
107111
disconnect(): void {
108-
if (!this.webSocket) return;
112+
if (!this.webSocket) {
113+
return;
114+
}
109115
this.onBoardsConfigChanged?.dispose();
110116
this.onBoardsConfigChanged = undefined;
111117
try {
112-
this.webSocket?.close();
118+
this.webSocket.close();
113119
this.webSocket = undefined;
114-
} catch {
120+
} catch (err) {
121+
console.error(
122+
'Could not close the websocket connection for the monitor.',
123+
err
124+
);
115125
this.messageService.error(
116126
nls.localize(
117127
'arduino/monitor/unableToCloseWebSocket',
@@ -126,6 +136,7 @@ export class MonitorManagerProxyClientImpl
126136
}
127137

128138
async startMonitor(settings?: PluggableMonitorSettings): Promise<void> {
139+
await this.boardsServiceProvider.reconciled;
129140
this.lastConnectedBoard = {
130141
selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard,
131142
selectedPort: this.boardsServiceProvider.boardsConfig.selectedPort,
@@ -150,11 +161,11 @@ export class MonitorManagerProxyClientImpl
150161
? Port.keyOf(this.lastConnectedBoard.selectedPort)
151162
: undefined)
152163
) {
153-
this.onMonitorShouldResetEmitter.fire(null);
154164
this.lastConnectedBoard = {
155165
selectedBoard: selectedBoard,
156166
selectedPort: selectedPort,
157167
};
168+
this.onMonitorShouldResetEmitter.fire();
158169
} else {
159170
// a board is plugged and it's the same as prev, rerun "this.startMonitor" to
160171
// recreate the listener callback
@@ -167,7 +178,14 @@ export class MonitorManagerProxyClientImpl
167178
const { selectedBoard, selectedPort } =
168179
this.boardsServiceProvider.boardsConfig;
169180
if (!selectedBoard || !selectedBoard.fqbn || !selectedPort) return;
170-
await this.server().startMonitor(selectedBoard, selectedPort, settings);
181+
try {
182+
this.clearVisibleNotification();
183+
await this.server().startMonitor(selectedBoard, selectedPort, settings);
184+
} catch (err) {
185+
const message = ApplicationError.is(err) ? err.message : String(err);
186+
this.previousNotificationId = this.notificationId(message);
187+
this.messageService.error(message);
188+
}
171189
}
172190

173191
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings> {
@@ -199,4 +217,24 @@ export class MonitorManagerProxyClientImpl
199217
})
200218
);
201219
}
220+
221+
/**
222+
* This is the internal (Theia) ID of the notification that is currently visible.
223+
* It's stored here as a field to be able to close it before starting a new monitor connection. It's a hack.
224+
*/
225+
private previousNotificationId: string | undefined;
226+
private clearVisibleNotification(): void {
227+
if (this.previousNotificationId) {
228+
this.notificationManager.clear(this.previousNotificationId);
229+
this.previousNotificationId = undefined;
230+
}
231+
}
232+
233+
private notificationId(message: string, ...actions: string[]): string {
234+
return this.notificationManager['getMessageId']({
235+
text: message,
236+
actions,
237+
type: MessageType.Error,
238+
});
239+
}
202240
}

‎arduino-ide-extension/src/browser/monitor-model.ts

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import {
44
LocalStorageService,
55
} from '@theia/core/lib/browser';
66
import { inject, injectable } from '@theia/core/shared/inversify';
7-
import { MonitorManagerProxyClient } from '../common/protocol';
7+
import {
8+
isMonitorConnected,
9+
MonitorConnectionStatus,
10+
monitorConnectionStatusEquals,
11+
MonitorEOL,
12+
MonitorManagerProxyClient,
13+
MonitorState,
14+
} from '../common/protocol';
815
import { isNullOrUndefined } from '../common/utils';
916
import { MonitorSettings } from '../node/monitor-settings/monitor-settings-provider';
1017

@@ -19,48 +26,48 @@ export class MonitorModel implements FrontendApplicationContribution {
1926
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
2027

2128
protected readonly onChangeEmitter: Emitter<
22-
MonitorModel.State.Change<keyof MonitorModel.State>
29+
MonitorState.Change<keyof MonitorState>
2330
>;
2431

2532
protected _autoscroll: boolean;
2633
protected _timestamp: boolean;
27-
protected _lineEnding: MonitorModel.EOL;
34+
protected _lineEnding: MonitorEOL;
2835
protected _interpolate: boolean;
2936
protected _darkTheme: boolean;
3037
protected _wsPort: number;
3138
protected _serialPort: string;
32-
protected _connected: boolean;
39+
protected _connectionStatus: MonitorConnectionStatus;
3340

3441
constructor() {
3542
this._autoscroll = true;
3643
this._timestamp = false;
3744
this._interpolate = false;
38-
this._lineEnding = MonitorModel.EOL.DEFAULT;
45+
this._lineEnding = MonitorEOL.DEFAULT;
3946
this._darkTheme = false;
4047
this._wsPort = 0;
4148
this._serialPort = '';
42-
this._connected = true;
49+
this._connectionStatus = 'not-connected';
4350

4451
this.onChangeEmitter = new Emitter<
45-
MonitorModel.State.Change<keyof MonitorModel.State>
52+
MonitorState.Change<keyof MonitorState>
4653
>();
4754
}
4855

4956
onStart(): void {
5057
this.localStorageService
51-
.getData<MonitorModel.State>(MonitorModel.STORAGE_ID)
58+
.getData<MonitorState>(MonitorModel.STORAGE_ID)
5259
.then(this.restoreState.bind(this));
5360

5461
this.monitorManagerProxy.onMonitorSettingsDidChange(
5562
this.onMonitorSettingsDidChange.bind(this)
5663
);
5764
}
5865

59-
get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
66+
get onChange(): Event<MonitorState.Change<keyof MonitorState>> {
6067
return this.onChangeEmitter.event;
6168
}
6269

63-
protected restoreState(state: MonitorModel.State): void {
70+
protected restoreState(state: MonitorState): void {
6471
if (!state) {
6572
return;
6673
}
@@ -125,11 +132,11 @@ export class MonitorModel implements FrontendApplicationContribution {
125132
this.timestamp = !this._timestamp;
126133
}
127134

128-
get lineEnding(): MonitorModel.EOL {
135+
get lineEnding(): MonitorEOL {
129136
return this._lineEnding;
130137
}
131138

132-
set lineEnding(lineEnding: MonitorModel.EOL) {
139+
set lineEnding(lineEnding: MonitorEOL) {
133140
if (lineEnding === this._lineEnding) return;
134141
this._lineEnding = lineEnding;
135142
this.monitorManagerProxy.changeSettings({
@@ -211,19 +218,26 @@ export class MonitorModel implements FrontendApplicationContribution {
211218
);
212219
}
213220

214-
get connected(): boolean {
215-
return this._connected;
221+
get connectionStatus(): MonitorConnectionStatus {
222+
return this._connectionStatus;
216223
}
217224

218-
set connected(connected: boolean) {
219-
if (connected === this._connected) return;
220-
this._connected = connected;
225+
set connectionStatus(connectionStatus: MonitorConnectionStatus) {
226+
if (
227+
monitorConnectionStatusEquals(connectionStatus, this.connectionStatus)
228+
) {
229+
return;
230+
}
231+
this._connectionStatus = connectionStatus;
221232
this.monitorManagerProxy.changeSettings({
222-
monitorUISettings: { connected },
233+
monitorUISettings: {
234+
connectionStatus,
235+
connected: isMonitorConnected(connectionStatus),
236+
},
223237
});
224238
this.onChangeEmitter.fire({
225-
property: 'connected',
226-
value: this._connected,
239+
property: 'connectionStatus',
240+
value: this._connectionStatus,
227241
});
228242
}
229243

@@ -238,7 +252,7 @@ export class MonitorModel implements FrontendApplicationContribution {
238252
darkTheme,
239253
wsPort,
240254
serialPort,
241-
connected,
255+
connectionStatus,
242256
} = monitorUISettings;
243257

244258
if (!isNullOrUndefined(autoscroll)) this.autoscroll = autoscroll;
@@ -248,31 +262,7 @@ export class MonitorModel implements FrontendApplicationContribution {
248262
if (!isNullOrUndefined(darkTheme)) this.darkTheme = darkTheme;
249263
if (!isNullOrUndefined(wsPort)) this.wsPort = wsPort;
250264
if (!isNullOrUndefined(serialPort)) this.serialPort = serialPort;
251-
if (!isNullOrUndefined(connected)) this.connected = connected;
265+
if (!isNullOrUndefined(connectionStatus))
266+
this.connectionStatus = connectionStatus;
252267
};
253268
}
254-
255-
// TODO: Move this to /common
256-
export namespace MonitorModel {
257-
export interface State {
258-
autoscroll: boolean;
259-
timestamp: boolean;
260-
lineEnding: EOL;
261-
interpolate: boolean;
262-
darkTheme: boolean;
263-
wsPort: number;
264-
serialPort: string;
265-
connected: boolean;
266-
}
267-
export namespace State {
268-
export interface Change<K extends keyof State> {
269-
readonly property: K;
270-
readonly value: State[K];
271-
}
272-
}
273-
274-
export type EOL = '' | '\n' | '\r' | '\r\n';
275-
export namespace EOL {
276-
export const DEFAULT: EOL = '\n';
277-
}
278-
}

‎arduino-ide-extension/src/browser/serial/monitor/monitor-view-contribution.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
1111
import { ArduinoMenus } from '../../menu/arduino-menus';
1212
import { nls } from '@theia/core/lib/common';
13+
import { Event } from '@theia/core/lib/common/event';
1314
import { MonitorModel } from '../../monitor-model';
1415
import { MonitorManagerProxyClient } from '../../../common/protocol';
1516

@@ -84,13 +85,13 @@ export class MonitorViewContribution
8485
id: 'monitor-autoscroll',
8586
render: () => this.renderAutoScrollButton(),
8687
isVisible: (widget) => widget instanceof MonitorWidget,
87-
onDidChange: this.model.onChange as any, // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/
88+
onDidChange: this.model.onChange as Event<unknown> as Event<void>,
8889
});
8990
registry.registerItem({
9091
id: 'monitor-timestamp',
9192
render: () => this.renderTimestampButton(),
9293
isVisible: (widget) => widget instanceof MonitorWidget,
93-
onDidChange: this.model.onChange as any, // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/
94+
onDidChange: this.model.onChange as Event<unknown> as Event<void>,
9495
});
9596
registry.registerItem({
9697
id: SerialMonitor.Commands.CLEAR_OUTPUT.id,
@@ -143,8 +144,7 @@ export class MonitorViewContribution
143144
protected async reset(): Promise<void> {
144145
const widget = this.tryGetWidget();
145146
if (widget) {
146-
widget.dispose();
147-
await this.openView({ activate: true, reveal: true });
147+
widget.reset();
148148
}
149149
}
150150

‎arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import * as React from '@theia/core/shared/react';
2-
import { injectable, inject } from '@theia/core/shared/inversify';
2+
import {
3+
injectable,
4+
inject,
5+
postConstruct,
6+
} from '@theia/core/shared/inversify';
37
import { Emitter } from '@theia/core/lib/common/event';
4-
import { Disposable } from '@theia/core/lib/common/disposable';
8+
import {
9+
Disposable,
10+
DisposableCollection,
11+
} from '@theia/core/lib/common/disposable';
512
import {
613
ReactWidget,
714
Message,
@@ -13,9 +20,13 @@ import { SerialMonitorSendInput } from './serial-monitor-send-input';
1320
import { SerialMonitorOutput } from './serial-monitor-send-output';
1421
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
1522
import { nls } from '@theia/core/lib/common';
16-
import { MonitorManagerProxyClient } from '../../../common/protocol';
23+
import {
24+
MonitorEOL,
25+
MonitorManagerProxyClient,
26+
} from '../../../common/protocol';
1727
import { MonitorModel } from '../../monitor-model';
1828
import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider';
29+
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
1930

2031
@injectable()
2132
export class MonitorWidget extends ReactWidget {
@@ -40,40 +51,46 @@ export class MonitorWidget extends ReactWidget {
4051
protected closing = false;
4152
protected readonly clearOutputEmitter = new Emitter<void>();
4253

43-
constructor(
44-
@inject(MonitorModel)
45-
protected readonly monitorModel: MonitorModel,
54+
@inject(MonitorModel)
55+
private readonly monitorModel: MonitorModel;
56+
@inject(MonitorManagerProxyClient)
57+
private readonly monitorManagerProxy: MonitorManagerProxyClient;
58+
@inject(BoardsServiceProvider)
59+
private readonly boardsServiceProvider: BoardsServiceProvider;
60+
@inject(FrontendApplicationStateService)
61+
private readonly appStateService: FrontendApplicationStateService;
4662

47-
@inject(MonitorManagerProxyClient)
48-
protected readonly monitorManagerProxy: MonitorManagerProxyClient,
63+
private readonly toDisposeOnReset: DisposableCollection;
4964

50-
@inject(BoardsServiceProvider)
51-
protected readonly boardsServiceProvider: BoardsServiceProvider
52-
) {
65+
constructor() {
5366
super();
5467
this.id = MonitorWidget.ID;
5568
this.title.label = MonitorWidget.LABEL;
5669
this.title.iconClass = 'monitor-tab-icon';
5770
this.title.closable = true;
5871
this.scrollOptions = undefined;
72+
this.toDisposeOnReset = new DisposableCollection();
5973
this.toDispose.push(this.clearOutputEmitter);
60-
this.toDispose.push(
61-
Disposable.create(() => this.monitorManagerProxy.disconnect())
62-
);
6374
}
6475

65-
protected override onBeforeAttach(msg: Message): void {
66-
this.update();
67-
this.toDispose.push(this.monitorModel.onChange(() => this.update()));
68-
this.getCurrentSettings().then(this.onMonitorSettingsDidChange.bind(this));
69-
this.monitorManagerProxy.onMonitorSettingsDidChange(
70-
this.onMonitorSettingsDidChange.bind(this)
71-
);
76+
@postConstruct()
77+
protected init(): void {
78+
this.toDisposeOnReset.dispose();
79+
this.toDisposeOnReset.pushAll([
80+
Disposable.create(() => this.monitorManagerProxy.disconnect()),
81+
this.monitorModel.onChange(() => this.update()),
82+
this.monitorManagerProxy.onMonitorSettingsDidChange((event) =>
83+
this.updateSettings(event)
84+
),
85+
]);
86+
this.startMonitor();
87+
}
7288

73-
this.monitorManagerProxy.startMonitor();
89+
reset(): void {
90+
this.init();
7491
}
7592

76-
onMonitorSettingsDidChange(settings: MonitorSettings): void {
93+
private updateSettings(settings: MonitorSettings): void {
7794
this.settings = {
7895
...this.settings,
7996
pluggableMonitorSettings: {
@@ -90,6 +107,7 @@ export class MonitorWidget extends ReactWidget {
90107
}
91108

92109
override dispose(): void {
110+
this.toDisposeOnReset.dispose();
93111
super.dispose();
94112
}
95113

@@ -122,7 +140,7 @@ export class MonitorWidget extends ReactWidget {
122140
this.update();
123141
}
124142

125-
protected onFocusResolved = (element: HTMLElement | undefined) => {
143+
protected onFocusResolved = (element: HTMLElement | undefined): void => {
126144
if (this.closing || !this.isAttached) {
127145
return;
128146
}
@@ -132,7 +150,7 @@ export class MonitorWidget extends ReactWidget {
132150
);
133151
};
134152

135-
protected get lineEndings(): SerialMonitorOutput.SelectOption<MonitorModel.EOL>[] {
153+
protected get lineEndings(): SerialMonitorOutput.SelectOption<MonitorEOL>[] {
136154
return [
137155
{
138156
label: nls.localize('arduino/serial/noLineEndings', 'No Line Ending'),
@@ -156,11 +174,23 @@ export class MonitorWidget extends ReactWidget {
156174
];
157175
}
158176

159-
private getCurrentSettings(): Promise<MonitorSettings> {
177+
private async startMonitor(): Promise<void> {
178+
await this.appStateService.reachedState('ready');
179+
await this.boardsServiceProvider.reconciled;
180+
await this.syncSettings();
181+
await this.monitorManagerProxy.startMonitor();
182+
}
183+
184+
private async syncSettings(): Promise<void> {
185+
const settings = await this.getCurrentSettings();
186+
this.updateSettings(settings);
187+
}
188+
189+
private async getCurrentSettings(): Promise<MonitorSettings> {
160190
const board = this.boardsServiceProvider.boardsConfig.selectedBoard;
161191
const port = this.boardsServiceProvider.boardsConfig.selectedPort;
162192
if (!board || !port) {
163-
return Promise.resolve(this.settings || {});
193+
return this.settings || {};
164194
}
165195
return this.monitorManagerProxy.getCurrentSettings(board, port);
166196
}
@@ -171,7 +201,7 @@ export class MonitorWidget extends ReactWidget {
171201
: undefined;
172202

173203
const baudrateOptions = baudrate?.values.map((b) => ({
174-
label: b + ' baud',
204+
label: nls.localize('arduino/monitor/baudRate', '{0} baud', b),
175205
value: b,
176206
}));
177207
const baudrateSelectedOption = baudrateOptions?.find(
@@ -181,7 +211,7 @@ export class MonitorWidget extends ReactWidget {
181211
const lineEnding =
182212
this.lineEndings.find(
183213
(item) => item.value === this.monitorModel.lineEnding
184-
) || this.lineEndings[1]; // Defaults to `\n`.
214+
) || MonitorEOL.DEFAULT;
185215

186216
return (
187217
<div className="serial-monitor">
@@ -228,13 +258,13 @@ export class MonitorWidget extends ReactWidget {
228258
);
229259
}
230260

231-
protected readonly onSend = (value: string) => this.doSend(value);
232-
protected async doSend(value: string): Promise<void> {
261+
protected readonly onSend = (value: string): void => this.doSend(value);
262+
protected doSend(value: string): void {
233263
this.monitorManagerProxy.send(value);
234264
}
235265

236266
protected readonly onChangeLineEnding = (
237-
option: SerialMonitorOutput.SelectOption<MonitorModel.EOL>
267+
option: SerialMonitorOutput.SelectOption<MonitorEOL>
238268
): void => {
239269
this.monitorModel.lineEnding = option.value;
240270
};

‎arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { DisposableCollection, nls } from '@theia/core/lib/common';
55
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
66
import { MonitorModel } from '../../monitor-model';
77
import { Unknown } from '../../../common/nls';
8+
import {
9+
isMonitorConnectionError,
10+
MonitorConnectionStatus,
11+
} from '../../../common/protocol';
812

913
class HistoryList {
1014
private readonly items: string[] = [];
@@ -62,7 +66,7 @@ export namespace SerialMonitorSendInput {
6266
}
6367
export interface State {
6468
text: string;
65-
connected: boolean;
69+
connectionStatus: MonitorConnectionStatus;
6670
history: HistoryList;
6771
}
6872
}
@@ -75,18 +79,27 @@ export class SerialMonitorSendInput extends React.Component<
7579

7680
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
7781
super(props);
78-
this.state = { text: '', connected: true, history: new HistoryList() };
82+
this.state = {
83+
text: '',
84+
connectionStatus: 'not-connected',
85+
history: new HistoryList(),
86+
};
7987
this.onChange = this.onChange.bind(this);
8088
this.onSend = this.onSend.bind(this);
8189
this.onKeyDown = this.onKeyDown.bind(this);
8290
}
8391

8492
override componentDidMount(): void {
85-
this.setState({ connected: this.props.monitorModel.connected });
93+
this.setState({
94+
connectionStatus: this.props.monitorModel.connectionStatus,
95+
});
8696
this.toDisposeBeforeUnmount.push(
8797
this.props.monitorModel.onChange(({ property }) => {
88-
if (property === 'connected')
89-
this.setState({ connected: this.props.monitorModel.connected });
98+
if (property === 'connected' || property === 'connectionStatus') {
99+
this.setState({
100+
connectionStatus: this.props.monitorModel.connectionStatus,
101+
});
102+
}
90103
})
91104
);
92105
}
@@ -97,44 +110,83 @@ export class SerialMonitorSendInput extends React.Component<
97110
}
98111

99112
override render(): React.ReactNode {
113+
const status = this.state.connectionStatus;
114+
const input = this.renderInput(status);
115+
if (status !== 'connecting') {
116+
return input;
117+
}
118+
return <label>{input}</label>;
119+
}
120+
121+
private renderInput(status: MonitorConnectionStatus): React.ReactNode {
122+
const inputClassName = this.inputClassName(status);
123+
const placeholder = this.placeholder;
124+
const readOnly = Boolean(inputClassName);
100125
return (
101126
<input
102127
ref={this.setRef}
103128
type="text"
104-
className={`theia-input ${this.shouldShowWarning() ? 'warning' : ''}`}
105-
placeholder={this.placeholder}
106-
value={this.state.text}
129+
className={`theia-input ${inputClassName}`}
130+
readOnly={readOnly}
131+
placeholder={placeholder}
132+
title={placeholder}
133+
value={readOnly ? '' : this.state.text} // always show the placeholder if cannot edit the <input>
107134
onChange={this.onChange}
108135
onKeyDown={this.onKeyDown}
109136
/>
110137
);
111138
}
112139

140+
private inputClassName(
141+
status: MonitorConnectionStatus
142+
): 'error' | 'warning' | '' {
143+
if (isMonitorConnectionError(status)) {
144+
return 'error';
145+
}
146+
if (status === 'connected') {
147+
return '';
148+
}
149+
return 'warning';
150+
}
151+
113152
protected shouldShowWarning(): boolean {
114153
const board = this.props.boardsServiceProvider.boardsConfig.selectedBoard;
115154
const port = this.props.boardsServiceProvider.boardsConfig.selectedPort;
116-
return !this.state.connected || !board || !port;
155+
return !this.state.connectionStatus || !board || !port;
117156
}
118157

119158
protected get placeholder(): string {
120-
if (this.shouldShowWarning()) {
159+
const status = this.state.connectionStatus;
160+
if (isMonitorConnectionError(status)) {
161+
return status.errorMessage;
162+
}
163+
if (status === 'not-connected') {
121164
return nls.localize(
122165
'arduino/serial/notConnected',
123166
'Not connected. Select a board and a port to connect automatically.'
124167
);
125168
}
126-
127169
const board = this.props.boardsServiceProvider.boardsConfig.selectedBoard;
128170
const port = this.props.boardsServiceProvider.boardsConfig.selectedPort;
171+
const boardLabel = board
172+
? Board.toString(board, {
173+
useFqbn: false,
174+
})
175+
: Unknown;
176+
const portLabel = port ? port.address : Unknown;
177+
if (status === 'connecting') {
178+
return nls.localize(
179+
'arduino/serial/connecting',
180+
"Connecting to '{0}' on '{1}'...",
181+
boardLabel,
182+
portLabel
183+
);
184+
}
129185
return nls.localize(
130186
'arduino/serial/message',
131187
"Message (Enter to send message to '{0}' on '{1}')",
132-
board
133-
? Board.toString(board, {
134-
useFqbn: false,
135-
})
136-
: Unknown,
137-
port ? port.address : Unknown
188+
boardLabel,
189+
portLabel
138190
);
139191
}
140192

‎arduino-ide-extension/src/browser/style/index.css

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
/* https://github.com/arduino/arduino-ide/pull/1662#issuecomment-1324997134 */
3030
body {
3131
--theia-icon-loading: url(../icons/loading-light.svg);
32+
--theia-icon-loading-warning: url(../icons/loading-dark.svg);
3233
}
3334
body.theia-dark {
3435
--theia-icon-loading: url(../icons/loading-dark.svg);
36+
--theia-icon-loading-warning: url(../icons/loading-light.svg);
3537
}
3638

3739
.theia-input.warning:focus {
@@ -48,22 +50,32 @@ body.theia-dark {
4850
}
4951

5052
.theia-input.warning::placeholder {
51-
/* Chrome, Firefox, Opera, Safari 10.1+ */
5253
color: var(--theia-warningForeground);
5354
background-color: var(--theia-warningBackground);
54-
opacity: 1; /* Firefox */
5555
}
5656

57-
.theia-input.warning:-ms-input-placeholder {
58-
/* Internet Explorer 10-11 */
59-
color: var(--theia-warningForeground);
60-
background-color: var(--theia-warningBackground);
57+
.hc-black.hc-theia.theia-hc .theia-input.warning,
58+
.hc-black.hc-theia.theia-hc .theia-input.warning::placeholder {
59+
color: var(--theia-warningBackground);
60+
background-color: var(--theia-warningForeground);
6161
}
6262

63-
.theia-input.warning::-ms-input-placeholder {
64-
/* Microsoft Edge */
65-
color: var(--theia-warningForeground);
66-
background-color: var(--theia-warningBackground);
63+
.theia-input.error:focus {
64+
outline-width: 1px;
65+
outline-style: solid;
66+
outline-offset: -1px;
67+
opacity: 1 !important;
68+
color: var(--theia-errorForeground);
69+
background-color: var(--theia-errorBackground);
70+
}
71+
72+
.theia-input.error {
73+
background-color: var(--theia-errorBackground);
74+
}
75+
76+
.theia-input.error::placeholder {
77+
color: var(--theia-errorForeground);
78+
background-color: var(--theia-errorBackground);
6779
}
6880

6981
/* Makes the sidepanel a bit wider when opening the widget */

‎arduino-ide-extension/src/browser/style/monitor.css

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,47 @@
2020

2121
.serial-monitor .head {
2222
display: flex;
23-
padding: 5px;
23+
padding: 0px 5px 5px 0px;
2424
height: 27px;
25+
background-color: var(--theia-activityBar-background);
2526
}
2627

2728
.serial-monitor .head .send {
2829
display: flex;
2930
flex: 1;
30-
margin-right: 2px;
3131
}
3232

33-
.serial-monitor .head .send > input {
33+
.serial-monitor .head .send > label:before {
34+
content: "";
35+
position: absolute;
36+
top: -1px;
37+
background: var(--theia-icon-loading-warning) center center no-repeat;
38+
animation: theia-spin 1.25s linear infinite;
39+
width: 30px;
40+
height: 30px;
41+
}
42+
43+
.serial-monitor .head .send > label {
44+
position: relative;
45+
width: 100%;
46+
display: flex;
47+
align-self: baseline;
48+
}
49+
50+
.serial-monitor .head .send > input,
51+
.serial-monitor .head .send > label > input {
3452
line-height: var(--theia-content-line-height);
53+
height: 27px;
3554
width: 100%;
3655
}
3756

38-
.serial-monitor .head .send > input:focus {
57+
.serial-monitor .head .send > label > input {
58+
padding-left: 30px;
59+
box-sizing: border-box;
60+
}
61+
62+
.serial-monitor .head .send > input:focus,
63+
.serial-monitor .head .send > label > input:focus {
3964
border-color: var(--theia-focusBorder);
4065
}
4166

‎arduino-ide-extension/src/common/protocol/core-service.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ export namespace CoreError {
7373
UploadUsingProgrammer: 4003,
7474
BurnBootloader: 4004,
7575
};
76-
export const VerifyFailed = create(Codes.Verify);
77-
export const UploadFailed = create(Codes.Upload);
78-
export const UploadUsingProgrammerFailed = create(
76+
export const VerifyFailed = declareCoreError(Codes.Verify);
77+
export const UploadFailed = declareCoreError(Codes.Upload);
78+
export const UploadUsingProgrammerFailed = declareCoreError(
7979
Codes.UploadUsingProgrammer
8080
);
81-
export const BurnBootloaderFailed = create(Codes.BurnBootloader);
81+
export const BurnBootloaderFailed = declareCoreError(Codes.BurnBootloader);
8282
export function is(
8383
error: unknown
8484
): error is ApplicationError<number, ErrorLocation[]> {
@@ -88,7 +88,7 @@ export namespace CoreError {
8888
Object.values(Codes).includes(error.code)
8989
);
9090
}
91-
function create(
91+
function declareCoreError(
9292
code: number
9393
): ApplicationError.Constructor<number, ErrorLocation[]> {
9494
return ApplicationError.declare(

‎arduino-ide-extension/src/common/protocol/monitor-service.ts

Lines changed: 165 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Event, JsonRpcServer } from '@theia/core';
1+
import { ApplicationError, Event, JsonRpcServer, nls } from '@theia/core';
22
import {
33
PluggableMonitorSettings,
44
MonitorSettings,
@@ -31,7 +31,7 @@ export interface MonitorManagerProxyClient {
3131
onMessagesReceived: Event<{ messages: string[] }>;
3232
onMonitorSettingsDidChange: Event<MonitorSettings>;
3333
onMonitorShouldReset: Event<void>;
34-
connect(addressPort: number): void;
34+
connect(addressPort: number): Promise<void>;
3535
disconnect(): void;
3636
getWebSocketPort(): number | undefined;
3737
isWSConnected(): Promise<boolean>;
@@ -46,7 +46,7 @@ export interface PluggableMonitorSetting {
4646
readonly id: string;
4747
// A human-readable label of the setting (to be displayed on the GUI)
4848
readonly label: string;
49-
// The setting type (at the moment only "enum" is avaiable)
49+
// The setting type (at the moment only "enum" is available)
5050
readonly type: string;
5151
// The values allowed on "enum" types
5252
readonly values: string[];
@@ -72,24 +72,168 @@ export namespace Monitor {
7272
};
7373
}
7474

75-
export interface Status {}
76-
export type OK = Status;
77-
export interface ErrorStatus extends Status {
78-
readonly message: string;
75+
export const MonitorErrorCodes = {
76+
ConnectionFailed: 6001,
77+
NotConnected: 6002,
78+
AlreadyConnected: 6003,
79+
MissingConfiguration: 6004,
80+
} as const;
81+
82+
export const ConnectionFailedError = declareMonitorError(
83+
MonitorErrorCodes.ConnectionFailed
84+
);
85+
export const NotConnectedError = declareMonitorError(
86+
MonitorErrorCodes.NotConnected
87+
);
88+
export const AlreadyConnectedError = declareMonitorError(
89+
MonitorErrorCodes.AlreadyConnected
90+
);
91+
export const MissingConfigurationError = declareMonitorError(
92+
MonitorErrorCodes.MissingConfiguration
93+
);
94+
95+
export function createConnectionFailedError(
96+
port: Port,
97+
details?: string
98+
): ApplicationError<number, PortDescriptor> {
99+
const { protocol, address } = port;
100+
let message;
101+
if (details) {
102+
const detailsWithPeriod = details.endsWith('.') ? details : `${details}.`;
103+
message = nls.localize(
104+
'arduino/monitor/connectionFailedErrorWithDetails',
105+
'{0} Could not connect to {1} {2} port.',
106+
detailsWithPeriod,
107+
address,
108+
protocol
109+
);
110+
} else {
111+
message = nls.localize(
112+
'arduino/monitor/connectionFailedError',
113+
'Could not connect to {0} {1} port.',
114+
address,
115+
protocol
116+
);
117+
}
118+
return ConnectionFailedError(message, { protocol, address });
119+
}
120+
export function createNotConnectedError(
121+
port: Port
122+
): ApplicationError<number, PortDescriptor> {
123+
const { protocol, address } = port;
124+
return NotConnectedError(
125+
nls.localize(
126+
'arduino/monitor/notConnectedError',
127+
'Not connected to {0} {1} port.',
128+
address,
129+
protocol
130+
),
131+
{ protocol, address }
132+
);
133+
}
134+
export function createAlreadyConnectedError(
135+
port: Port
136+
): ApplicationError<number, PortDescriptor> {
137+
const { protocol, address } = port;
138+
return AlreadyConnectedError(
139+
nls.localize(
140+
'arduino/monitor/alreadyConnectedError',
141+
'Could not connect to {0} {1} port. Already connected.',
142+
address,
143+
protocol
144+
),
145+
{ protocol, address }
146+
);
147+
}
148+
export function createMissingConfigurationError(
149+
port: Port
150+
): ApplicationError<number, PortDescriptor> {
151+
const { protocol, address } = port;
152+
return MissingConfigurationError(
153+
nls.localize(
154+
'arduino/monitor/missingConfigurationError',
155+
'Could not connect to {0} {1} port. The monitor configuration is missing.',
156+
address,
157+
protocol
158+
),
159+
{ protocol, address }
160+
);
161+
}
162+
163+
/**
164+
* Bare minimum representation of a port. Supports neither UI labels nor properties.
165+
*/
166+
interface PortDescriptor {
167+
readonly protocol: string;
168+
readonly address: string;
169+
}
170+
function declareMonitorError(
171+
code: number
172+
): ApplicationError.Constructor<number, PortDescriptor> {
173+
return ApplicationError.declare(
174+
code,
175+
(message: string, data: PortDescriptor) => ({ data, message })
176+
);
177+
}
178+
179+
export interface MonitorConnectionError {
180+
readonly errorMessage: string;
181+
}
182+
183+
export type MonitorConnectionStatus =
184+
| 'connecting'
185+
| 'connected'
186+
| 'not-connected'
187+
| MonitorConnectionError;
188+
189+
export function monitorConnectionStatusEquals(
190+
left: MonitorConnectionStatus,
191+
right: MonitorConnectionStatus
192+
): boolean {
193+
if (typeof left === 'object' && typeof right === 'object') {
194+
return left.errorMessage === right.errorMessage;
195+
}
196+
return left === right;
197+
}
198+
199+
/**
200+
* @deprecated see `MonitorState#connected`
201+
*/
202+
export function isMonitorConnected(
203+
status: MonitorConnectionStatus
204+
): status is 'connected' {
205+
return status === 'connected';
79206
}
80-
export namespace Status {
81-
export function isOK(status: Status & { message?: string }): status is OK {
82-
return !!status && typeof status.message !== 'string';
207+
208+
export function isMonitorConnectionError(
209+
status: MonitorConnectionStatus
210+
): status is MonitorConnectionError {
211+
return typeof status === 'object';
212+
}
213+
214+
export interface MonitorState {
215+
autoscroll: boolean;
216+
timestamp: boolean;
217+
lineEnding: MonitorEOL;
218+
interpolate: boolean;
219+
darkTheme: boolean;
220+
wsPort: number;
221+
serialPort: string;
222+
connectionStatus: MonitorConnectionStatus;
223+
/**
224+
* @deprecated This property is never get by IDE2 only set. This value is present to be backward compatible with the plotter app.
225+
* IDE2 uses `MonitorState#connectionStatus`.
226+
*/
227+
connected: boolean;
228+
}
229+
export namespace MonitorState {
230+
export interface Change<K extends keyof MonitorState> {
231+
readonly property: K;
232+
readonly value: MonitorState[K];
83233
}
84-
export const OK: OK = {};
85-
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
86-
export const ALREADY_CONNECTED: ErrorStatus = {
87-
message: 'Already connected.',
88-
};
89-
export const CONFIG_MISSING: ErrorStatus = {
90-
message: 'Serial Config missing.',
91-
};
92-
export const UPLOAD_IN_PROGRESS: ErrorStatus = {
93-
message: 'Upload in progress.',
94-
};
234+
}
235+
236+
export type MonitorEOL = '' | '\n' | '\r' | '\r\n';
237+
export namespace MonitorEOL {
238+
export const DEFAULT: MonitorEOL = '\n';
95239
}

‎arduino-ide-extension/src/node/core-service-impl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
UploadUsingProgrammerResponse,
2424
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
2525
import { ResponseService } from '../common/protocol/response-service';
26-
import { OutputMessage, Port, Status } from '../common/protocol';
26+
import { OutputMessage, Port } from '../common/protocol';
2727
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
2828
import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
2929
import { ApplicationError, CommandService, Disposable, nls } from '@theia/core';
@@ -392,7 +392,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
392392
}: {
393393
fqbn?: string | undefined;
394394
port?: Port | undefined;
395-
}): Promise<Status> {
395+
}): Promise<void> {
396396
this.boardDiscovery.setUploadInProgress(false);
397397
return this.monitorManager.notifyUploadFinished(fqbn, port);
398398
}

‎arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { inject, injectable } from '@theia/core/shared/inversify';
22
import {
33
MonitorManagerProxy,
44
MonitorManagerProxyClient,
5-
Status,
65
} from '../common/protocol';
76
import { Board, Port } from '../common/protocol';
87
import { MonitorManager } from './monitor-manager';
@@ -41,11 +40,16 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy {
4140
await this.changeMonitorSettings(board, port, settings);
4241
}
4342

44-
const connectToClient = (status: Status) => {
45-
if (status === Status.ALREADY_CONNECTED || status === Status.OK) {
46-
// Monitor started correctly, connect it with the frontend
47-
this.client.connect(this.manager.getWebsocketAddressPort(board, port));
43+
const connectToClient = async () => {
44+
const address = this.manager.getWebsocketAddressPort(board, port);
45+
if (!this.client) {
46+
throw new Error(
47+
`No client was connected to this monitor manager. Board: ${
48+
board.fqbn ?? board.name
49+
}, port: ${port.address}, address: ${address}`
50+
);
4851
}
52+
await this.client.connect(address);
4953
};
5054
return this.manager.startMonitor(board, port, connectToClient);
5155
}

‎arduino-ide-extension/src/node/monitor-manager.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { ILogger } from '@theia/core';
22
import { inject, injectable, named } from '@theia/core/shared/inversify';
3-
import { Board, BoardsService, Port, Status } from '../common/protocol';
3+
import {
4+
AlreadyConnectedError,
5+
Board,
6+
BoardsService,
7+
Port,
8+
} from '../common/protocol';
49
import { CoreClientAware } from './core-client-provider';
510
import { MonitorService } from './monitor-service';
611
import { MonitorServiceFactory } from './monitor-service-factory';
@@ -36,7 +41,7 @@ export class MonitorManager extends CoreClientAware {
3641
private monitorServiceStartQueue: {
3742
monitorID: string;
3843
serviceStartParams: [Board, Port];
39-
connectToClient: (status: Status) => void;
44+
connectToClient: () => Promise<void>;
4045
}[] = [];
4146

4247
@inject(MonitorServiceFactory)
@@ -104,7 +109,7 @@ export class MonitorManager extends CoreClientAware {
104109
async startMonitor(
105110
board: Board,
106111
port: Port,
107-
connectToClient: (status: Status) => void
112+
connectToClient: () => Promise<void>
108113
): Promise<void> {
109114
const monitorID = this.monitorID(board.fqbn, port);
110115

@@ -127,8 +132,14 @@ export class MonitorManager extends CoreClientAware {
127132
return;
128133
}
129134

130-
const result = await monitor.start();
131-
connectToClient(result);
135+
try {
136+
await connectToClient();
137+
await monitor.start();
138+
} catch (err) {
139+
if (!AlreadyConnectedError.is(err)) {
140+
throw err;
141+
}
142+
}
132143
}
133144

134145
/**
@@ -202,8 +213,7 @@ export class MonitorManager extends CoreClientAware {
202213
async notifyUploadFinished(
203214
fqbn?: string | undefined,
204215
port?: Port
205-
): Promise<Status> {
206-
let status: Status = Status.NOT_CONNECTED;
216+
): Promise<void> {
207217
let portDidChangeOnUpload = false;
208218

209219
// We have no way of knowing which monitor
@@ -214,7 +224,7 @@ export class MonitorManager extends CoreClientAware {
214224

215225
const monitor = this.monitorServices.get(monitorID);
216226
if (monitor) {
217-
status = await monitor.start();
227+
await monitor.start();
218228
}
219229

220230
// this monitorID will only be present in "disposedForUpload"
@@ -232,7 +242,6 @@ export class MonitorManager extends CoreClientAware {
232242
}
233243

234244
await this.startQueuedServices(portDidChangeOnUpload);
235-
return status;
236245
}
237246

238247
async startQueuedServices(portDidChangeOnUpload: boolean): Promise<void> {
@@ -246,7 +255,7 @@ export class MonitorManager extends CoreClientAware {
246255

247256
for (const {
248257
monitorID,
249-
serviceStartParams: [_, port],
258+
serviceStartParams: [, port],
250259
connectToClient,
251260
} of queued) {
252261
const boardsState = await this.boardsService.getState();
@@ -261,8 +270,8 @@ export class MonitorManager extends CoreClientAware {
261270
const monitorService = this.monitorServices.get(monitorID);
262271

263272
if (monitorService) {
264-
const result = await monitorService.start();
265-
connectToClient(result);
273+
await connectToClient();
274+
await monitorService.start();
266275
}
267276
}
268277
}

‎arduino-ide-extension/src/node/monitor-service.ts

Lines changed: 217 additions & 155 deletions
Large diffs are not rendered by default.

‎arduino-ide-extension/src/node/monitor-settings/monitor-settings-provider.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { MonitorModel } from '../../browser/monitor-model';
2-
import { PluggableMonitorSetting } from '../../common/protocol';
1+
import { MonitorState, PluggableMonitorSetting } from '../../common/protocol';
32

43
export type PluggableMonitorSettings = Record<string, PluggableMonitorSetting>;
54
export interface MonitorSettings {
65
pluggableMonitorSettings?: PluggableMonitorSettings;
7-
monitorUISettings?: Partial<MonitorModel.State>;
6+
monitorUISettings?: Partial<MonitorState>;
87
}
98

109
export const MonitorSettingsProvider = Symbol('MonitorSettingsProvider');

‎i18n/en.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,13 @@
328328
"tools": "Tools"
329329
},
330330
"monitor": {
331+
"alreadyConnectedError": "Could not connect to {0} {1} port. Already connected.",
332+
"baudRate": "{0} baud",
333+
"connectionFailedError": "Could not connect to {0} {1} port.",
334+
"connectionFailedErrorWithDetails": "{0} Could not connect to {1} {2} port.",
335+
"connectionTimeout": "Timeout. The IDE has not received the 'success' message from the monitor after successfully connecting to it",
336+
"missingConfigurationError": "Could not connect to {0} {1} port. The monitor configuration is missing.",
337+
"notConnectedError": "Not connected to {0} {1} port.",
331338
"unableToCloseWebSocket": "Unable to close websocket",
332339
"unableToConnectToWebSocket": "Unable to connect to websocket"
333340
},
@@ -408,6 +415,7 @@
408415
"serial": {
409416
"autoscroll": "Autoscroll",
410417
"carriageReturn": "Carriage Return",
418+
"connecting": "Connecting to '{0}' on '{1}'...",
411419
"message": "Message (Enter to send message to '{0}' on '{1}')",
412420
"newLine": "New Line",
413421
"newLineCarriageReturn": "Both NL & CR",

0 commit comments

Comments
 (0)
Please sign in to comment.