agl 23.0.0 → 24.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +11 -0
- package/dist_serve/bundle.js +811 -808
- package/dist_serve/bundle.js.map +1 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.authstore.js +2 -2
- package/dist_ts/classes.browserresourcehost.d.ts +1 -0
- package/dist_ts/classes.browserresourcehost.js +7 -1
- package/dist_ts/classes.controller.d.ts +35 -5
- package/dist_ts/classes.controller.js +994 -229
- package/dist_ts/classes.validation.d.ts +9 -1
- package/dist_ts/classes.validation.js +62 -7
- package/dist_ts_interfaces/browsertransport.d.ts +72 -0
- package/dist_ts_interfaces/browsertransport.js +47 -0
- package/dist_ts_interfaces/index.d.ts +1 -0
- package/dist_ts_interfaces/index.js +2 -1
- package/dist_ts_interfaces/interfaces.d.ts +10 -6
- package/dist_ts_interfaces/interfaces.js +2 -3
- package/dist_ts_interfaces/requests.d.ts +20 -21
- package/package.json +5 -5
- package/readme.md +7 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.authstore.ts +1 -1
- package/ts/classes.browserresourcehost.ts +7 -0
- package/ts/classes.controller.ts +1367 -279
- package/ts/classes.validation.ts +77 -6
- package/ts_interfaces/browsertransport.ts +126 -0
- package/ts_interfaces/index.ts +1 -0
- package/ts_interfaces/interfaces.ts +11 -11
- package/ts_interfaces/requests.ts +23 -30
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/classes.browserviewtransport.ts +532 -0
- package/ts_web/classes.controllersocketclient.ts +6 -17
- package/ts_web/elements.harnesscontrollerapp.ts +450 -115
- package/ts_web/index.ts +1 -0
- package/ts_web/plugins.ts +2 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import * as interfaces from '../ts_interfaces/index.js';
|
|
3
3
|
import { commitinfo } from './00_commitinfo_data.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
type TControllerBrowserViewEventMessage,
|
|
7
|
-
} from './classes.controllersocketclient.js';
|
|
4
|
+
import { ControllerSocketClient } from './classes.controllersocketclient.js';
|
|
5
|
+
import { ControllerBrowserViewTransportClient } from './classes.browserviewtransport.js';
|
|
8
6
|
import {
|
|
9
7
|
SessionDraftSync,
|
|
10
8
|
type ISessionDraftSyncState,
|
|
@@ -459,6 +457,7 @@ const layoutRequestTimeoutMs = 10_000;
|
|
|
459
457
|
const providerLoginPollTimeoutMs = 15 * 60 * 1000;
|
|
460
458
|
const providerRefreshPollTimeoutMs = 5 * 60 * 1000;
|
|
461
459
|
const providerOpenCodeSwitchTimeoutMs = 6 * 60 * 1000;
|
|
460
|
+
const browserRendererLifecycleTimeoutMs = 1_000;
|
|
462
461
|
const maximumSlashSuggestions = 12;
|
|
463
462
|
const maximumWorkspaceNoticeCharacters = 500;
|
|
464
463
|
|
|
@@ -621,6 +620,25 @@ const errorMessage = (errorArg: unknown): string => {
|
|
|
621
620
|
return String(errorArg || 'The operation failed.');
|
|
622
621
|
};
|
|
623
622
|
|
|
623
|
+
const waitForBrowserRendererLifecycle = async (
|
|
624
|
+
operationArg: Promise<void>,
|
|
625
|
+
actionArg: string,
|
|
626
|
+
): Promise<void> => {
|
|
627
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
628
|
+
try {
|
|
629
|
+
await Promise.race([
|
|
630
|
+
operationArg,
|
|
631
|
+
new Promise<never>((_resolve, reject) => {
|
|
632
|
+
timeout = setTimeout(() => {
|
|
633
|
+
reject(new Error(`Browser renderer ${actionArg} timed out.`));
|
|
634
|
+
}, browserRendererLifecycleTimeoutMs);
|
|
635
|
+
}),
|
|
636
|
+
]);
|
|
637
|
+
} finally {
|
|
638
|
+
if (timeout) clearTimeout(timeout);
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
624
642
|
const formatPercentage = (valueArg: number | null | undefined): string => (
|
|
625
643
|
typeof valueArg === 'number' && Number.isFinite(valueArg)
|
|
626
644
|
? `${Math.round(valueArg)}%`
|
|
@@ -716,14 +734,27 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
716
734
|
private browserNavigationRequestId = 0;
|
|
717
735
|
private browserViewOpenAbortController: AbortController | undefined;
|
|
718
736
|
private browserNavigationAbortController: AbortController | undefined;
|
|
737
|
+
private browserViewRecoveryAbortController: AbortController | undefined;
|
|
719
738
|
private browserViewId = '';
|
|
720
739
|
private browserViewConnectionGeneration = -1;
|
|
721
|
-
private
|
|
740
|
+
private browserViewStreamGeneration = -1;
|
|
741
|
+
private browserViewActivated = false;
|
|
742
|
+
private browserViewPendingClose: {
|
|
743
|
+
viewId: string;
|
|
744
|
+
streamGeneration: number;
|
|
745
|
+
connectionGeneration: number;
|
|
746
|
+
attempts: number;
|
|
747
|
+
} | undefined;
|
|
748
|
+
private browserViewCloseTask: Promise<void> | undefined;
|
|
749
|
+
private browserViewCloseRetryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
722
750
|
private browserViewResourceId = '';
|
|
723
751
|
private browserViewAttachmentRevision = -1;
|
|
724
|
-
private browserViewState: interfaces.IControllerBrowserViewState | undefined;
|
|
725
752
|
private readonly browserViewListeners = new Set<plugins.TLiveBrowserEventListener>();
|
|
753
|
+
private browserViewTransport?: ControllerBrowserViewTransportClient;
|
|
754
|
+
private browserViewTransportRecoveryTask?: Promise<void>;
|
|
726
755
|
private browserRenderer?: plugins.LiveBrowserCanvasRenderer;
|
|
756
|
+
private browserAddressDraft = '';
|
|
757
|
+
private browserAddressEditing = false;
|
|
727
758
|
private suggestRequestId = 0;
|
|
728
759
|
private suggestDebounce: ReturnType<typeof setTimeout> | undefined;
|
|
729
760
|
private controllerStatus: interfaces.IControllerStatus | undefined;
|
|
@@ -937,8 +968,7 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
937
968
|
@plugins.deesElement.state()
|
|
938
969
|
accessor staleBundleVersion = '';
|
|
939
970
|
|
|
940
|
-
|
|
941
|
-
accessor systemMetrics: interfaces.IControllerSystemMetrics | undefined;
|
|
971
|
+
public systemMetrics: interfaces.IControllerSystemMetrics | undefined;
|
|
942
972
|
|
|
943
973
|
@plugins.deesElement.state()
|
|
944
974
|
accessor highCpuWarning = false;
|
|
@@ -1015,6 +1045,9 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
1015
1045
|
@plugins.deesElement.state()
|
|
1016
1046
|
accessor browserViewLoading = false;
|
|
1017
1047
|
|
|
1048
|
+
@plugins.deesElement.state()
|
|
1049
|
+
accessor browserViewState: interfaces.IControllerBrowserViewState | undefined;
|
|
1050
|
+
|
|
1018
1051
|
@plugins.deesElement.state()
|
|
1019
1052
|
accessor selectedTerminalId: TBrowserTerminalId | undefined;
|
|
1020
1053
|
|
|
@@ -1036,7 +1069,6 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
1036
1069
|
onStatus: (statusArg) => this.handleConnectionStatus(statusArg),
|
|
1037
1070
|
onControllerEvent: (eventArg) => this.handleControllerEvent(eventArg),
|
|
1038
1071
|
onTerminalOutput: (outputArg) => this.handleTerminalOutput(outputArg),
|
|
1039
|
-
onBrowserViewEvent: (eventArg) => this.handleBrowserViewEvent(eventArg),
|
|
1040
1072
|
onTransportClose: (closeCodeArg, reasonArg) => {
|
|
1041
1073
|
this.recordTransportClose(closeCodeArg, reasonArg);
|
|
1042
1074
|
console.error(`Controller transport closed (code ${closeCodeArg}${reasonArg ? `: ${reasonArg}` : ''}).`);
|
|
@@ -1045,6 +1077,13 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
1045
1077
|
? 'The connection was closed because a response exceeded the transport frame limit. Reconnecting…'
|
|
1046
1078
|
: `The connection was closed by the transport (code ${closeCodeArg}${reasonArg ? `: ${reasonArg}` : ''}). Reconnecting…`;
|
|
1047
1079
|
},
|
|
1080
|
+
onReconnectExhausted: (attemptsArg, endpointArg) => {
|
|
1081
|
+
if (!this.componentConnected) return;
|
|
1082
|
+
this.connectionError = `Automatic reconnection stopped after ${attemptsArg} attempts to ${endpointArg}.`;
|
|
1083
|
+
void this.socketClient.stop().finally(() => {
|
|
1084
|
+
if (this.componentConnected) this.connectionStatus = 'disconnected';
|
|
1085
|
+
});
|
|
1086
|
+
},
|
|
1048
1087
|
});
|
|
1049
1088
|
this.sessionDraftSync = new SessionDraftSync({
|
|
1050
1089
|
transport: {
|
|
@@ -2074,6 +2113,8 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
2074
2113
|
|
|
2075
2114
|
this.connectionStatus = statusArg;
|
|
2076
2115
|
this.connectionGeneration += 1;
|
|
2116
|
+
if (this.browserViewCloseRetryTimer) clearTimeout(this.browserViewCloseRetryTimer);
|
|
2117
|
+
this.browserViewCloseRetryTimer = undefined;
|
|
2077
2118
|
if (this.browserViewPendingClose?.connectionGeneration !== this.connectionGeneration) {
|
|
2078
2119
|
this.browserViewPendingClose = undefined;
|
|
2079
2120
|
}
|
|
@@ -2092,6 +2133,8 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
2092
2133
|
|
|
2093
2134
|
private invalidateConnectionState(): void {
|
|
2094
2135
|
this.connectionGeneration += 1;
|
|
2136
|
+
if (this.browserViewCloseRetryTimer) clearTimeout(this.browserViewCloseRetryTimer);
|
|
2137
|
+
this.browserViewCloseRetryTimer = undefined;
|
|
2095
2138
|
if (this.browserViewPendingClose?.connectionGeneration !== this.connectionGeneration) {
|
|
2096
2139
|
this.browserViewPendingClose = undefined;
|
|
2097
2140
|
}
|
|
@@ -2609,12 +2652,54 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
2609
2652
|
|
|
2610
2653
|
this.lastCpuSampleAt = sampledAt;
|
|
2611
2654
|
this.systemMetrics = metricsArg;
|
|
2655
|
+
this.updateSystemMetricsDom();
|
|
2612
2656
|
}
|
|
2613
2657
|
|
|
2614
2658
|
private resetHighCpuWarning(): void {
|
|
2615
2659
|
this.highCpuStartedAt = undefined;
|
|
2616
2660
|
this.lastCpuSampleAt = undefined;
|
|
2617
2661
|
this.highCpuWarning = false;
|
|
2662
|
+
this.updateSystemMetricsDom();
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
private updateSystemMetricsDom(): void {
|
|
2666
|
+
const metrics = this.systemMetrics;
|
|
2667
|
+
const memoryPercent = metrics?.memoryUsedBytes !== null
|
|
2668
|
+
&& metrics?.memoryUsedBytes !== undefined
|
|
2669
|
+
&& metrics.memoryTotalBytes !== null
|
|
2670
|
+
&& metrics.memoryTotalBytes !== undefined
|
|
2671
|
+
&& metrics.memoryTotalBytes > 0
|
|
2672
|
+
? (metrics.memoryUsedBytes / metrics.memoryTotalBytes) * 100
|
|
2673
|
+
: null;
|
|
2674
|
+
const cpu = this.shadowRoot?.querySelector<HTMLElement>('.cpuMetric');
|
|
2675
|
+
const memory = this.shadowRoot?.querySelector<HTMLElement>('.memoryMetric');
|
|
2676
|
+
const network = this.shadowRoot?.querySelector<HTMLElement>('.networkMetric');
|
|
2677
|
+
const cpuValue = cpu?.querySelector<HTMLElement>('.metricValue');
|
|
2678
|
+
const memoryValue = memory?.querySelector<HTMLElement>('.metricValue');
|
|
2679
|
+
const networkReceive = network?.querySelector<HTMLElement>('.networkReceive');
|
|
2680
|
+
const networkTransmit = network?.querySelector<HTMLElement>('.networkTransmit');
|
|
2681
|
+
if (cpu) {
|
|
2682
|
+
cpu.classList.toggle('highCpuWarning', this.highCpuWarning);
|
|
2683
|
+
cpu.title = this.highCpuWarning
|
|
2684
|
+
? 'Whole-system CPU usage has remained above 50% for at least 10 seconds'
|
|
2685
|
+
: 'Whole-system CPU usage';
|
|
2686
|
+
}
|
|
2687
|
+
if (cpuValue) cpuValue.textContent = formatPercentage(metrics?.cpuUsagePercent);
|
|
2688
|
+
if (memory) {
|
|
2689
|
+
memory.title = metrics?.memoryUsedBytes !== null
|
|
2690
|
+
&& metrics?.memoryUsedBytes !== undefined
|
|
2691
|
+
&& metrics.memoryTotalBytes !== null
|
|
2692
|
+
&& metrics.memoryTotalBytes !== undefined
|
|
2693
|
+
? `${formatBytes(metrics.memoryUsedBytes)} used of ${formatBytes(metrics.memoryTotalBytes)}`
|
|
2694
|
+
: 'Memory usage unavailable';
|
|
2695
|
+
}
|
|
2696
|
+
if (memoryValue) memoryValue.textContent = formatPercentage(memoryPercent);
|
|
2697
|
+
if (networkReceive) {
|
|
2698
|
+
networkReceive.textContent = `in ${formatByteRate(metrics?.networkReceiveBytesPerSecond)}`;
|
|
2699
|
+
}
|
|
2700
|
+
if (networkTransmit) {
|
|
2701
|
+
networkTransmit.textContent = `out ${formatByteRate(metrics?.networkTransmitBytesPerSecond)}`;
|
|
2702
|
+
}
|
|
2618
2703
|
}
|
|
2619
2704
|
|
|
2620
2705
|
private readonly showLegalDisclosure = (): void => {
|
|
@@ -5400,6 +5485,18 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5400
5485
|
const resourceId = eventArg.detail?.resource?.id;
|
|
5401
5486
|
const resource = this.resources.find((candidate) => candidate.id === resourceId);
|
|
5402
5487
|
if (!resource || resource.lifecycle !== 'active') return;
|
|
5488
|
+
if (
|
|
5489
|
+
resource.kind === 'browser'
|
|
5490
|
+
&& this.selectedResourceId === resource.id
|
|
5491
|
+
&& (
|
|
5492
|
+
this.browserViewLoading
|
|
5493
|
+
|| (
|
|
5494
|
+
this.browserViewResourceId === resource.id
|
|
5495
|
+
&& this.browserViewAttachmentRevision === resource.attachment.revision
|
|
5496
|
+
&& this.browserViewId !== ''
|
|
5497
|
+
)
|
|
5498
|
+
)
|
|
5499
|
+
) return;
|
|
5403
5500
|
this.invalidateSlashCatalog();
|
|
5404
5501
|
this.selectedResourceId = resource.id;
|
|
5405
5502
|
if (resource.kind === 'browser') {
|
|
@@ -5435,6 +5532,7 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5435
5532
|
this.browserViewLoading = true;
|
|
5436
5533
|
this.workspaceError = '';
|
|
5437
5534
|
let openedViewId = '';
|
|
5535
|
+
let openedStreamGeneration = -1;
|
|
5438
5536
|
try {
|
|
5439
5537
|
const response = await this.socketClient.fire<interfaces.IReq_ControllerBrowserViewOpen>(
|
|
5440
5538
|
'controller.browser.view.open',
|
|
@@ -5446,6 +5544,7 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5446
5544
|
{ maxRetries: 0, abortSignal: openAbortController.signal },
|
|
5447
5545
|
);
|
|
5448
5546
|
openedViewId = response.viewId;
|
|
5547
|
+
openedStreamGeneration = response.streamGeneration;
|
|
5449
5548
|
if (
|
|
5450
5549
|
generation !== this.browserViewGeneration
|
|
5451
5550
|
|| connectionGeneration !== this.connectionGeneration
|
|
@@ -5453,7 +5552,11 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5453
5552
|
|| response.resourceId !== resourceArg.id
|
|
5454
5553
|
|| response.attachmentRevision !== resourceArg.attachment.revision
|
|
5455
5554
|
) {
|
|
5456
|
-
void this.requestBrowserViewClose(
|
|
5555
|
+
void this.requestBrowserViewClose(
|
|
5556
|
+
response.viewId,
|
|
5557
|
+
response.streamGeneration,
|
|
5558
|
+
connectionGeneration,
|
|
5559
|
+
);
|
|
5457
5560
|
return;
|
|
5458
5561
|
}
|
|
5459
5562
|
if (
|
|
@@ -5462,29 +5565,49 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5462
5565
|
) this.browserViewPendingClose = undefined;
|
|
5463
5566
|
this.browserViewId = response.viewId;
|
|
5464
5567
|
this.browserViewConnectionGeneration = connectionGeneration;
|
|
5568
|
+
this.browserViewStreamGeneration = response.streamGeneration;
|
|
5569
|
+
this.browserViewActivated = false;
|
|
5465
5570
|
this.browserViewResourceId = response.resourceId;
|
|
5466
5571
|
this.browserViewAttachmentRevision = response.attachmentRevision;
|
|
5467
|
-
this.
|
|
5572
|
+
this.applyBrowserViewState(response.state);
|
|
5573
|
+
const transport = this.createBrowserViewTransportClient(
|
|
5574
|
+
response.streamGeneration,
|
|
5575
|
+
response.events,
|
|
5576
|
+
response.operations,
|
|
5577
|
+
generation,
|
|
5578
|
+
);
|
|
5579
|
+
this.browserViewTransport = transport;
|
|
5580
|
+
const transportStart = transport.start();
|
|
5468
5581
|
this.browserViewLoading = false;
|
|
5469
5582
|
this.requestUpdate();
|
|
5470
|
-
await this.updateComplete;
|
|
5583
|
+
await Promise.all([this.updateComplete, transportStart]);
|
|
5471
5584
|
const rendererReady = await this.mountBrowserRenderer(generation);
|
|
5472
5585
|
if (
|
|
5473
5586
|
!rendererReady
|
|
5474
5587
|
|| generation !== this.browserViewGeneration
|
|
5475
5588
|
|| connectionGeneration !== this.connectionGeneration
|
|
5476
5589
|
|| response.viewId !== this.browserViewId
|
|
5590
|
+
|| response.streamGeneration !== this.browserViewStreamGeneration
|
|
5477
5591
|
) return;
|
|
5478
5592
|
await this.socketClient.fire<interfaces.IReq_ControllerBrowserViewActivate>(
|
|
5479
5593
|
'controller.browser.view.activate',
|
|
5480
|
-
{ viewId: response.viewId },
|
|
5594
|
+
{ viewId: response.viewId, streamGeneration: response.streamGeneration },
|
|
5481
5595
|
{ maxRetries: 0, abortSignal: openAbortController.signal },
|
|
5482
5596
|
);
|
|
5597
|
+
if (
|
|
5598
|
+
generation === this.browserViewGeneration
|
|
5599
|
+
&& response.viewId === this.browserViewId
|
|
5600
|
+
&& response.streamGeneration === this.browserViewStreamGeneration
|
|
5601
|
+
) this.browserViewActivated = true;
|
|
5483
5602
|
} catch (errorArg) {
|
|
5484
5603
|
if (openedViewId && openedViewId === this.browserViewId) {
|
|
5485
5604
|
await this.closeBrowserView(false);
|
|
5486
5605
|
} else if (openedViewId && this.socketClient.isConnected) {
|
|
5487
|
-
await this.requestBrowserViewClose(
|
|
5606
|
+
await this.requestBrowserViewClose(
|
|
5607
|
+
openedViewId,
|
|
5608
|
+
openedStreamGeneration,
|
|
5609
|
+
connectionGeneration,
|
|
5610
|
+
);
|
|
5488
5611
|
}
|
|
5489
5612
|
if (
|
|
5490
5613
|
generation === this.browserViewGeneration
|
|
@@ -5507,39 +5630,80 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5507
5630
|
this.browserViewOpenAbortController = undefined;
|
|
5508
5631
|
this.browserNavigationAbortController?.abort(new Error('Browser view changed.'));
|
|
5509
5632
|
this.browserNavigationAbortController = undefined;
|
|
5633
|
+
this.browserViewRecoveryAbortController?.abort(new Error('Browser view changed.'));
|
|
5634
|
+
this.browserViewRecoveryAbortController = undefined;
|
|
5510
5635
|
const renderer = this.browserRenderer;
|
|
5511
|
-
|
|
5512
|
-
await renderer.stop().then(
|
|
5513
|
-
() => {
|
|
5514
|
-
if (this.browserRenderer === renderer) this.browserRenderer = undefined;
|
|
5515
|
-
},
|
|
5516
|
-
(errorArg) => {
|
|
5517
|
-
this.workspaceError = `Browser renderer cleanup remains pending: ${errorMessage(errorArg)}`;
|
|
5518
|
-
},
|
|
5519
|
-
);
|
|
5520
|
-
}
|
|
5636
|
+
const transport = this.browserViewTransport;
|
|
5521
5637
|
const viewId = this.browserViewId;
|
|
5638
|
+
const viewStreamGeneration = this.browserViewStreamGeneration;
|
|
5522
5639
|
const viewConnectionGeneration = this.browserViewConnectionGeneration;
|
|
5523
5640
|
this.browserViewId = '';
|
|
5524
5641
|
this.browserViewConnectionGeneration = -1;
|
|
5642
|
+
this.browserViewStreamGeneration = -1;
|
|
5643
|
+
this.browserViewActivated = false;
|
|
5525
5644
|
this.browserViewResourceId = '';
|
|
5526
5645
|
this.browserViewAttachmentRevision = -1;
|
|
5527
5646
|
this.browserViewState = undefined;
|
|
5647
|
+
this.browserAddressDraft = '';
|
|
5648
|
+
this.browserAddressEditing = false;
|
|
5528
5649
|
this.browserViewLoading = false;
|
|
5529
5650
|
this.browserViewListeners.clear();
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5651
|
+
const remoteCloseTask = (
|
|
5652
|
+
viewId
|
|
5653
|
+
&& viewStreamGeneration > 0
|
|
5654
|
+
&& viewConnectionGeneration === this.connectionGeneration
|
|
5655
|
+
)
|
|
5656
|
+
? this.requestBrowserViewClose(
|
|
5657
|
+
viewId,
|
|
5658
|
+
viewStreamGeneration,
|
|
5659
|
+
viewConnectionGeneration,
|
|
5660
|
+
)
|
|
5661
|
+
: this.flushPendingBrowserViewClose();
|
|
5662
|
+
const rendererStopTask = renderer
|
|
5663
|
+
? this.stopBrowserRenderer(renderer)
|
|
5664
|
+
: Promise.resolve();
|
|
5665
|
+
const transportStopTask = transport
|
|
5666
|
+
? transport.stop(new Error('Browser view changed.')).then(() => {
|
|
5667
|
+
if (this.browserViewTransport === transport) this.browserViewTransport = undefined;
|
|
5668
|
+
})
|
|
5669
|
+
: Promise.resolve();
|
|
5670
|
+
await Promise.all([remoteCloseTask, rendererStopTask, transportStopTask]);
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5673
|
+
private async stopBrowserRenderer(rendererArg: plugins.LiveBrowserCanvasRenderer): Promise<void> {
|
|
5674
|
+
let stopTask: Promise<void>;
|
|
5675
|
+
try {
|
|
5676
|
+
stopTask = rendererArg.stop();
|
|
5677
|
+
} catch (errorArg) {
|
|
5678
|
+
if (this.browserRenderer === rendererArg) {
|
|
5679
|
+
this.workspaceError = `Browser renderer cleanup remains pending: ${errorMessage(errorArg)}`;
|
|
5680
|
+
}
|
|
5681
|
+
return;
|
|
5682
|
+
}
|
|
5683
|
+
void stopTask.then(() => {
|
|
5684
|
+
if (this.browserRenderer === rendererArg) this.browserRenderer = undefined;
|
|
5685
|
+
}, () => undefined);
|
|
5686
|
+
try {
|
|
5687
|
+
await waitForBrowserRendererLifecycle(stopTask, 'stop');
|
|
5688
|
+
} catch (errorArg) {
|
|
5689
|
+
if (this.browserRenderer === rendererArg) {
|
|
5690
|
+
this.workspaceError = `Browser renderer cleanup remains pending: ${errorMessage(errorArg)}`;
|
|
5691
|
+
}
|
|
5534
5692
|
}
|
|
5535
5693
|
}
|
|
5536
5694
|
|
|
5537
5695
|
private async requestBrowserViewClose(
|
|
5538
5696
|
viewIdArg: string,
|
|
5697
|
+
streamGenerationArg: number,
|
|
5539
5698
|
connectionGenerationArg: number,
|
|
5540
5699
|
): Promise<void> {
|
|
5541
5700
|
if (connectionGenerationArg !== this.connectionGeneration) return;
|
|
5542
|
-
const pendingClose = {
|
|
5701
|
+
const pendingClose = {
|
|
5702
|
+
viewId: viewIdArg,
|
|
5703
|
+
streamGeneration: streamGenerationArg,
|
|
5704
|
+
connectionGeneration: connectionGenerationArg,
|
|
5705
|
+
attempts: 0,
|
|
5706
|
+
};
|
|
5543
5707
|
this.browserViewPendingClose = pendingClose;
|
|
5544
5708
|
await this.flushPendingBrowserViewClose();
|
|
5545
5709
|
}
|
|
@@ -5551,70 +5715,190 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5551
5715
|
|| pendingClose.connectionGeneration !== this.connectionGeneration
|
|
5552
5716
|
|| !this.socketClient.isConnected
|
|
5553
5717
|
) return;
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5718
|
+
if (this.browserViewCloseTask) return this.browserViewCloseTask;
|
|
5719
|
+
pendingClose.attempts += 1;
|
|
5720
|
+
const closeTask = this.socketClient.fire<interfaces.IReq_ControllerBrowserViewClose>(
|
|
5721
|
+
'controller.browser.view.close',
|
|
5722
|
+
{
|
|
5723
|
+
viewId: pendingClose.viewId,
|
|
5724
|
+
expectedStreamGeneration: pendingClose.streamGeneration,
|
|
5725
|
+
},
|
|
5726
|
+
{ maxRetries: 0 },
|
|
5727
|
+
).then(() => {
|
|
5728
|
+
if (this.browserViewPendingClose === pendingClose) {
|
|
5729
|
+
this.browserViewPendingClose = undefined;
|
|
5730
|
+
}
|
|
5731
|
+
}).catch(() => {
|
|
5732
|
+
if (
|
|
5733
|
+
this.browserViewPendingClose === pendingClose
|
|
5734
|
+
&& pendingClose.attempts >= 3
|
|
5735
|
+
) {
|
|
5736
|
+
this.browserViewPendingClose = undefined;
|
|
5737
|
+
this.workspaceError = 'The browser view close request failed after three attempts.';
|
|
5738
|
+
return;
|
|
5739
|
+
}
|
|
5740
|
+
if (
|
|
5741
|
+
this.browserViewPendingClose !== pendingClose
|
|
5742
|
+
|| pendingClose.connectionGeneration !== this.connectionGeneration
|
|
5743
|
+
) return;
|
|
5744
|
+
if (this.browserViewCloseRetryTimer) clearTimeout(this.browserViewCloseRetryTimer);
|
|
5745
|
+
this.browserViewCloseRetryTimer = setTimeout(() => {
|
|
5746
|
+
this.browserViewCloseRetryTimer = undefined;
|
|
5747
|
+
void this.flushPendingBrowserViewClose();
|
|
5748
|
+
}, 250 * pendingClose.attempts);
|
|
5749
|
+
}).finally(() => {
|
|
5750
|
+
if (this.browserViewCloseTask === closeTask) this.browserViewCloseTask = undefined;
|
|
5751
|
+
if (
|
|
5752
|
+
this.browserViewPendingClose
|
|
5753
|
+
&& this.browserViewPendingClose !== pendingClose
|
|
5754
|
+
) void this.flushPendingBrowserViewClose();
|
|
5755
|
+
});
|
|
5756
|
+
this.browserViewCloseTask = closeTask;
|
|
5757
|
+
return closeTask;
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
private createBrowserViewTransportClient(
|
|
5761
|
+
streamGenerationArg: number,
|
|
5762
|
+
eventsArg: plugins.typedrequestInterfaces.TVirtualStream<'receive'>,
|
|
5763
|
+
operationsArg: plugins.typedrequestInterfaces.TVirtualStream<'send'>,
|
|
5764
|
+
viewGenerationArg: number,
|
|
5765
|
+
): ControllerBrowserViewTransportClient {
|
|
5766
|
+
return new ControllerBrowserViewTransportClient({
|
|
5767
|
+
streamGeneration: streamGenerationArg,
|
|
5768
|
+
events: eventsArg,
|
|
5769
|
+
operations: operationsArg,
|
|
5770
|
+
onEvent: (eventArg) => {
|
|
5771
|
+
if (
|
|
5772
|
+
viewGenerationArg !== this.browserViewGeneration
|
|
5773
|
+
|| streamGenerationArg !== this.browserViewStreamGeneration
|
|
5774
|
+
) return;
|
|
5775
|
+
this.handleBrowserViewEvent(eventArg);
|
|
5776
|
+
},
|
|
5777
|
+
onFailure: (errorArg, recoverableArg) => {
|
|
5778
|
+
this.handleBrowserTransportFailure(
|
|
5779
|
+
viewGenerationArg,
|
|
5780
|
+
streamGenerationArg,
|
|
5781
|
+
errorArg,
|
|
5782
|
+
recoverableArg,
|
|
5783
|
+
);
|
|
5784
|
+
},
|
|
5785
|
+
});
|
|
5786
|
+
}
|
|
5787
|
+
|
|
5788
|
+
private handleBrowserViewEvent(eventArg: interfaces.TControllerBrowserViewEvent): void {
|
|
5789
|
+
if (eventArg.type === 'state') this.applyBrowserViewState(eventArg.state);
|
|
5790
|
+
if (eventArg.type === 'error' && eventArg.error.fatal) this.workspaceError = eventArg.error.message;
|
|
5791
|
+
for (const listener of this.browserViewListeners) listener(eventArg);
|
|
5792
|
+
}
|
|
5793
|
+
|
|
5794
|
+
private applyBrowserViewState(stateArg: interfaces.IControllerBrowserViewState): boolean {
|
|
5795
|
+
if (this.browserViewState && stateArg.revision <= this.browserViewState.revision) return false;
|
|
5796
|
+
this.browserViewState = stateArg;
|
|
5797
|
+
if (!this.browserAddressEditing) {
|
|
5798
|
+
this.browserAddressDraft = stateArg.tabs.find((tabArg) => tabArg.active)?.url ?? '';
|
|
5799
|
+
}
|
|
5800
|
+
return true;
|
|
5801
|
+
}
|
|
5802
|
+
|
|
5803
|
+
private handleBrowserTransportFailure(
|
|
5804
|
+
viewGenerationArg: number,
|
|
5805
|
+
streamGenerationArg: number,
|
|
5806
|
+
errorArg: unknown,
|
|
5807
|
+
recoverableArg: boolean,
|
|
5808
|
+
): void {
|
|
5809
|
+
if (
|
|
5810
|
+
viewGenerationArg !== this.browserViewGeneration
|
|
5811
|
+
|| streamGenerationArg !== this.browserViewStreamGeneration
|
|
5812
|
+
|| !this.browserViewId
|
|
5813
|
+
) return;
|
|
5814
|
+
if (!recoverableArg || !this.browserViewActivated || !this.browserRenderer) {
|
|
5815
|
+
this.workspaceError = `Browser transport failed: ${errorMessage(errorArg)}`;
|
|
5816
|
+
void this.closeBrowserView();
|
|
5561
5817
|
return;
|
|
5562
5818
|
}
|
|
5563
|
-
if (this.
|
|
5819
|
+
if (this.browserViewTransportRecoveryTask) return;
|
|
5820
|
+
const recovery = this.recoverBrowserViewTransport(
|
|
5821
|
+
viewGenerationArg,
|
|
5822
|
+
streamGenerationArg,
|
|
5823
|
+
errorArg,
|
|
5824
|
+
).finally(() => {
|
|
5825
|
+
if (this.browserViewTransportRecoveryTask === recovery) {
|
|
5826
|
+
this.browserViewTransportRecoveryTask = undefined;
|
|
5827
|
+
}
|
|
5828
|
+
});
|
|
5829
|
+
this.browserViewTransportRecoveryTask = recovery;
|
|
5830
|
+
void recovery.catch(() => undefined);
|
|
5564
5831
|
}
|
|
5565
5832
|
|
|
5566
|
-
private async
|
|
5567
|
-
|
|
5833
|
+
private async recoverBrowserViewTransport(
|
|
5834
|
+
viewGenerationArg: number,
|
|
5835
|
+
streamGenerationArg: number,
|
|
5836
|
+
reasonArg: unknown,
|
|
5568
5837
|
): Promise<void> {
|
|
5569
|
-
const
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
);
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5838
|
+
const viewId = this.browserViewId;
|
|
5839
|
+
const connectionGeneration = this.connectionGeneration;
|
|
5840
|
+
const oldTransport = this.browserViewTransport;
|
|
5841
|
+
const renderer = this.browserRenderer;
|
|
5842
|
+
if (!viewId || !oldTransport || !renderer) return;
|
|
5843
|
+
const recoveryAbortController = new AbortController();
|
|
5844
|
+
this.browserViewRecoveryAbortController = recoveryAbortController;
|
|
5845
|
+
this.browserViewActivated = false;
|
|
5846
|
+
try {
|
|
5847
|
+
await waitForBrowserRendererLifecycle(renderer.suspend(), 'suspension');
|
|
5848
|
+
await oldTransport.stop(reasonArg);
|
|
5849
|
+
const response = await this.socketClient.fire<interfaces.IReq_ControllerBrowserViewRecover>(
|
|
5850
|
+
'controller.browser.view.recover',
|
|
5851
|
+
{ viewId, expectedStreamGeneration: streamGenerationArg },
|
|
5852
|
+
{ maxRetries: 0, abortSignal: recoveryAbortController.signal },
|
|
5853
|
+
);
|
|
5854
|
+
if (
|
|
5855
|
+
viewGenerationArg !== this.browserViewGeneration
|
|
5856
|
+
|| connectionGeneration !== this.connectionGeneration
|
|
5857
|
+
|| viewId !== this.browserViewId
|
|
5858
|
+
|| streamGenerationArg !== this.browserViewStreamGeneration
|
|
5859
|
+
|| response.viewId !== viewId
|
|
5860
|
+
|| response.resourceId !== this.browserViewResourceId
|
|
5861
|
+
|| response.attachmentRevision !== this.browserViewAttachmentRevision
|
|
5862
|
+
|| response.streamGeneration !== streamGenerationArg + 1
|
|
5863
|
+
) throw new Error('The recovered browser stream generation is stale.');
|
|
5864
|
+
const transport = this.createBrowserViewTransportClient(
|
|
5865
|
+
response.streamGeneration,
|
|
5866
|
+
response.events,
|
|
5867
|
+
response.operations,
|
|
5868
|
+
viewGenerationArg,
|
|
5869
|
+
);
|
|
5870
|
+
this.browserViewTransport = transport;
|
|
5871
|
+
this.browserViewStreamGeneration = response.streamGeneration;
|
|
5872
|
+
this.applyBrowserViewState(response.state);
|
|
5873
|
+
await transport.start();
|
|
5874
|
+
await waitForBrowserRendererLifecycle(renderer.resume(), 'resume');
|
|
5875
|
+
await this.socketClient.fire<interfaces.IReq_ControllerBrowserViewActivate>(
|
|
5876
|
+
'controller.browser.view.activate',
|
|
5877
|
+
{ viewId, streamGeneration: response.streamGeneration },
|
|
5878
|
+
{ maxRetries: 0, abortSignal: recoveryAbortController.signal },
|
|
5879
|
+
);
|
|
5880
|
+
if (
|
|
5881
|
+
viewGenerationArg === this.browserViewGeneration
|
|
5882
|
+
&& viewId === this.browserViewId
|
|
5883
|
+
&& response.streamGeneration === this.browserViewStreamGeneration
|
|
5884
|
+
) {
|
|
5885
|
+
this.browserViewActivated = true;
|
|
5886
|
+
this.workspaceError = '';
|
|
5585
5887
|
}
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
}
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
if (!isCurrent()) {
|
|
5599
|
-
await reject(new Error('The browser frame became stale during transfer.'));
|
|
5600
|
-
return;
|
|
5601
|
-
}
|
|
5602
|
-
data = firstChunk;
|
|
5603
|
-
await dataStream.accept();
|
|
5604
|
-
} catch (errorArg) {
|
|
5605
|
-
await reject(errorArg);
|
|
5606
|
-
throw errorArg;
|
|
5888
|
+
} catch (errorArg) {
|
|
5889
|
+
if (
|
|
5890
|
+
viewGenerationArg === this.browserViewGeneration
|
|
5891
|
+
&& connectionGeneration === this.connectionGeneration
|
|
5892
|
+
&& viewId === this.browserViewId
|
|
5893
|
+
) {
|
|
5894
|
+
this.workspaceError = `Browser transport recovery failed: ${errorMessage(errorArg)}`;
|
|
5895
|
+
await this.closeBrowserView();
|
|
5896
|
+
}
|
|
5897
|
+
} finally {
|
|
5898
|
+
if (this.browserViewRecoveryAbortController === recoveryAbortController) {
|
|
5899
|
+
this.browserViewRecoveryAbortController = undefined;
|
|
5607
5900
|
}
|
|
5608
|
-
if (!isCurrent()) return;
|
|
5609
|
-
const event = { type: 'frame' as const, frame: { ...frame, data } };
|
|
5610
|
-
for (const listener of this.browserViewListeners) listener(event);
|
|
5611
|
-
return;
|
|
5612
5901
|
}
|
|
5613
|
-
if (!isCurrent()) return;
|
|
5614
|
-
const event = messageArg.event;
|
|
5615
|
-
if (event.type === 'state') this.browserViewState = event.state;
|
|
5616
|
-
if (event.type === 'error' && event.error.fatal) this.workspaceError = event.error.message;
|
|
5617
|
-
for (const listener of this.browserViewListeners) listener(event);
|
|
5618
5902
|
}
|
|
5619
5903
|
|
|
5620
5904
|
private async browserViewOperation(
|
|
@@ -5623,14 +5907,19 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5623
5907
|
abortSignalArg?: AbortSignal,
|
|
5624
5908
|
): Promise<interfaces.IControllerBrowserViewState> {
|
|
5625
5909
|
const viewId = this.browserViewId;
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5910
|
+
const transport = this.browserViewTransport;
|
|
5911
|
+
if (!viewId || !transport) throw new Error('The browser view is unavailable.');
|
|
5912
|
+
const result = await transport.operate(operationArg, abortSignalArg);
|
|
5913
|
+
if (
|
|
5914
|
+
result.status === 'succeeded'
|
|
5915
|
+
&& result.state
|
|
5916
|
+
&& viewId === this.browserViewId
|
|
5917
|
+
) {
|
|
5918
|
+
if (!publishStateArg) return result.state;
|
|
5919
|
+
this.applyBrowserViewState(result.state);
|
|
5920
|
+
}
|
|
5921
|
+
if (!this.browserViewState) throw new Error('The browser view state is unavailable.');
|
|
5922
|
+
return this.browserViewState;
|
|
5634
5923
|
}
|
|
5635
5924
|
|
|
5636
5925
|
private async navigateBrowserAddress(formArg: HTMLFormElement): Promise<void> {
|
|
@@ -5643,6 +5932,7 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5643
5932
|
const input = formArg.querySelector<HTMLInputElement>('input');
|
|
5644
5933
|
try {
|
|
5645
5934
|
const url = controllerNormalizeBrowserAddress(input?.value ?? '');
|
|
5935
|
+
this.browserAddressDraft = input?.value ?? '';
|
|
5646
5936
|
this.workspaceError = '';
|
|
5647
5937
|
const state = await this.browserViewOperation(
|
|
5648
5938
|
{ type: 'navigate', url },
|
|
@@ -5653,7 +5943,7 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5653
5943
|
requestId === this.browserNavigationRequestId
|
|
5654
5944
|
&& viewGeneration === this.browserViewGeneration
|
|
5655
5945
|
&& viewId === this.browserViewId
|
|
5656
|
-
) this.
|
|
5946
|
+
) this.applyBrowserViewState(state);
|
|
5657
5947
|
} catch (errorArg) {
|
|
5658
5948
|
if (
|
|
5659
5949
|
requestId === this.browserNavigationRequestId
|
|
@@ -5678,6 +5968,23 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5678
5968
|
if (form) void this.navigateBrowserAddress(form);
|
|
5679
5969
|
};
|
|
5680
5970
|
|
|
5971
|
+
private readonly handleBrowserAddressFocus = (eventArg: Event): void => {
|
|
5972
|
+
const input = eventArg.currentTarget;
|
|
5973
|
+
if (!(input instanceof HTMLInputElement)) return;
|
|
5974
|
+
this.browserAddressEditing = true;
|
|
5975
|
+
this.browserAddressDraft = input.value;
|
|
5976
|
+
};
|
|
5977
|
+
|
|
5978
|
+
private readonly handleBrowserAddressInput = (eventArg: Event): void => {
|
|
5979
|
+
const input = eventArg.currentTarget;
|
|
5980
|
+
if (input instanceof HTMLInputElement) this.browserAddressDraft = input.value;
|
|
5981
|
+
};
|
|
5982
|
+
|
|
5983
|
+
private readonly handleBrowserAddressBlur = (): void => {
|
|
5984
|
+
this.browserAddressEditing = false;
|
|
5985
|
+
this.requestUpdate();
|
|
5986
|
+
};
|
|
5987
|
+
|
|
5681
5988
|
private browserCanvasClient(): plugins.ILiveBrowserCanvasClient {
|
|
5682
5989
|
return {
|
|
5683
5990
|
getState: () => {
|
|
@@ -5689,12 +5996,13 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5689
5996
|
return () => this.browserViewListeners.delete(listenerArg);
|
|
5690
5997
|
},
|
|
5691
5998
|
acknowledgeFrame: async (acknowledgement, options) => {
|
|
5692
|
-
const
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
{
|
|
5999
|
+
const transport = this.browserViewTransport;
|
|
6000
|
+
if (!transport) throw new Error('The browser view is unavailable.');
|
|
6001
|
+
const result = await transport.operate(
|
|
6002
|
+
{ type: 'acknowledgeFrame', acknowledgement },
|
|
6003
|
+
options.signal,
|
|
5696
6004
|
);
|
|
5697
|
-
return { accepted:
|
|
6005
|
+
return { accepted: result.status === 'succeeded' && result.accepted === true };
|
|
5698
6006
|
},
|
|
5699
6007
|
setViewport: async (viewport, options) => {
|
|
5700
6008
|
await this.browserViewOperation({ type: 'setViewport', viewport }, true, options.signal);
|
|
@@ -5725,14 +6033,30 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
5725
6033
|
focusTarget: surface,
|
|
5726
6034
|
resizeTarget: surface,
|
|
5727
6035
|
onError: (errorArg) => {
|
|
5728
|
-
if (generationArg
|
|
6036
|
+
if (generationArg !== this.browserViewGeneration) return;
|
|
6037
|
+
this.workspaceError = errorArg.message;
|
|
6038
|
+
if (
|
|
6039
|
+
errorArg.code === 'frame_acknowledgement_failed'
|
|
6040
|
+
|| errorArg.code === 'frame_render_failed'
|
|
6041
|
+
) {
|
|
6042
|
+
this.handleBrowserTransportFailure(
|
|
6043
|
+
generationArg,
|
|
6044
|
+
this.browserViewStreamGeneration,
|
|
6045
|
+
errorArg,
|
|
6046
|
+
true,
|
|
6047
|
+
);
|
|
6048
|
+
}
|
|
5729
6049
|
},
|
|
5730
6050
|
});
|
|
5731
6051
|
this.browserRenderer = renderer;
|
|
5732
|
-
await
|
|
6052
|
+
await this.startBrowserRenderer(renderer);
|
|
5733
6053
|
return generationArg === this.browserViewGeneration && this.browserRenderer === renderer;
|
|
5734
6054
|
}
|
|
5735
6055
|
|
|
6056
|
+
private startBrowserRenderer(rendererArg: plugins.LiveBrowserCanvasRenderer): Promise<void> {
|
|
6057
|
+
return waitForBrowserRendererLifecycle(rendererArg.start(), 'start');
|
|
6058
|
+
}
|
|
6059
|
+
|
|
5736
6060
|
private readonly handleResourceAssociationRequest = (
|
|
5737
6061
|
eventArg: CustomEvent<plugins.deesCatalog.THarnessResourceAssociationRequestDetail>,
|
|
5738
6062
|
): void => {
|
|
@@ -7518,7 +7842,7 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
7518
7842
|
) return;
|
|
7519
7843
|
this.resources = Array.isArray(response.resources) ? response.resources : [];
|
|
7520
7844
|
this.resourcesLoadedForProjectId = projectId;
|
|
7521
|
-
const viewedBrowserResource = this.resources.find((resource) => (
|
|
7845
|
+
const viewedBrowserResource = this.resources.find((resource): resource is interfaces.IControllerBrowserResource => (
|
|
7522
7846
|
resource.id === this.browserViewResourceId && resource.kind === 'browser'
|
|
7523
7847
|
));
|
|
7524
7848
|
if (
|
|
@@ -7528,7 +7852,22 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
7528
7852
|
|| viewedBrowserResource.lifecycle !== 'active'
|
|
7529
7853
|
|| viewedBrowserResource.attachment.revision !== this.browserViewAttachmentRevision
|
|
7530
7854
|
)
|
|
7531
|
-
)
|
|
7855
|
+
) {
|
|
7856
|
+
const reopen = Boolean(
|
|
7857
|
+
viewedBrowserResource
|
|
7858
|
+
&& viewedBrowserResource.lifecycle === 'active'
|
|
7859
|
+
&& viewedBrowserResource.browserRuntimeState === 'available'
|
|
7860
|
+
&& this.selectedResourceId === viewedBrowserResource.id,
|
|
7861
|
+
);
|
|
7862
|
+
await this.closeBrowserView();
|
|
7863
|
+
if (
|
|
7864
|
+
reopen
|
|
7865
|
+
&& viewedBrowserResource
|
|
7866
|
+
&& this.selectedResourceId === viewedBrowserResource.id
|
|
7867
|
+
&& this.selectedProjectId === projectId
|
|
7868
|
+
&& this.isCurrentConnection(generation)
|
|
7869
|
+
) await this.openBrowserView(viewedBrowserResource);
|
|
7870
|
+
}
|
|
7532
7871
|
if (
|
|
7533
7872
|
this.selectedResourceId
|
|
7534
7873
|
&& !this.resources.some((resource) => resource.id === this.selectedResourceId)
|
|
@@ -11864,9 +12203,12 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
11864
12203
|
<dees-icon icon="lucide:Globe2" iconSize="16"></dees-icon>
|
|
11865
12204
|
<input
|
|
11866
12205
|
aria-label="Browser address"
|
|
11867
|
-
.value=${activeTab?.url ?? ''}
|
|
12206
|
+
.value=${this.browserAddressEditing ? this.browserAddressDraft : activeTab?.url ?? ''}
|
|
11868
12207
|
placeholder="https://example.com"
|
|
11869
12208
|
?disabled=${!this.browserViewId}
|
|
12209
|
+
@focus=${this.handleBrowserAddressFocus}
|
|
12210
|
+
@input=${this.handleBrowserAddressInput}
|
|
12211
|
+
@blur=${this.handleBrowserAddressBlur}
|
|
11870
12212
|
/>
|
|
11871
12213
|
<dees-button
|
|
11872
12214
|
type="highlighted"
|
|
@@ -12559,13 +12901,6 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
12559
12901
|
? (this.authenticated ? 'authenticated' : 'connected')
|
|
12560
12902
|
: pending ? 'connecting' : 'offline';
|
|
12561
12903
|
const metrics = this.systemMetrics;
|
|
12562
|
-
const memoryPercent = metrics?.memoryUsedBytes !== null
|
|
12563
|
-
&& metrics?.memoryUsedBytes !== undefined
|
|
12564
|
-
&& metrics.memoryTotalBytes !== null
|
|
12565
|
-
&& metrics.memoryTotalBytes !== undefined
|
|
12566
|
-
&& metrics.memoryTotalBytes > 0
|
|
12567
|
-
? (metrics.memoryUsedBytes / metrics.memoryTotalBytes) * 100
|
|
12568
|
-
: null;
|
|
12569
12904
|
const memoryTitle = metrics?.memoryUsedBytes !== null
|
|
12570
12905
|
&& metrics?.memoryUsedBytes !== undefined
|
|
12571
12906
|
&& metrics.memoryTotalBytes !== null
|
|
@@ -12589,19 +12924,19 @@ export class HarnessControllerApp extends plugins.deesElement.DeesElement {
|
|
|
12589
12924
|
: 'Whole-system CPU usage'}
|
|
12590
12925
|
>
|
|
12591
12926
|
<span class="metricLabel">CPU</span>
|
|
12592
|
-
<span
|
|
12927
|
+
<span class="metricValue">--</span>
|
|
12593
12928
|
</span>
|
|
12594
12929
|
<span class="statusMetric memoryMetric" title=${memoryTitle}>
|
|
12595
12930
|
<span class="metricLabel">RAM</span>
|
|
12596
|
-
<span
|
|
12931
|
+
<span class="metricValue">--</span>
|
|
12597
12932
|
</span>
|
|
12598
12933
|
<span
|
|
12599
12934
|
class="statusMetric networkMetric"
|
|
12600
12935
|
title="Aggregate traffic across all system network interfaces"
|
|
12601
12936
|
>
|
|
12602
12937
|
<span class="metricLabel">NET</span>
|
|
12603
|
-
<span>in
|
|
12604
|
-
<span>out
|
|
12938
|
+
<span class="networkReceive">in --</span>
|
|
12939
|
+
<span class="networkTransmit">out --</span>
|
|
12605
12940
|
</span>
|
|
12606
12941
|
</div>
|
|
12607
12942
|
`
|