// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../../../ui/kit/kit.js';
import './ExtensionView.js';
import * as Host from '../../../core/host/host.js';
import * as i18n from '../../../core/i18n/i18n.js';
import * as Platform from '../../../core/platform/platform.js';
import * as SDK from '../../../core/sdk/sdk.js';
import type * as PublicExtensions from '../../../models/extensions/extensions.js';
import * as CodeMirror from '../../../third_party/codemirror.next/codemirror.next.js';
import type * as PuppeteerReplay from '../../../third_party/puppeteer-replay/puppeteer-replay.js';
import * as Buttons from '../../../ui/components/buttons/buttons.js';
import * as CodeHighlighter from '../../../ui/components/code_highlighter/code_highlighter.js';
import * as Dialogs from '../../../ui/components/dialogs/dialogs.js';
import * as Input from '../../../ui/components/input/input.js';
import type * as Menus from '../../../ui/components/menus/menus.js';
import * as TextEditor from '../../../ui/components/text_editor/text_editor.js';
import * as UI from '../../../ui/legacy/legacy.js';
import * as Lit from '../../../ui/lit/lit.js';
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
import type * as Converters from '../converters/converters.js';
import type * as Extensions from '../extensions/extensions.js';
import * as Models from '../models/models.js';
import {PlayRecordingSpeed} from '../models/RecordingPlayer.js';
import * as Actions from '../recorder-actions/recorder-actions.js';
import {ControlButton} from './ControlButton.js';
import recordingViewStyles from './recordingView.css.js';
import {ReplaySection} from './ReplaySection.js';
import {
type CopyStepEvent,
State,
StepView,
} from './StepView.js';
const {html} = Lit;
const UIStrings = {
/**
* @description Depicts that the recording was done on a mobile device (e.g., a smartphone or tablet).
*/
mobile: 'Mobile',
/**
* @description Depicts that the recording was done on a desktop device (e.g., on a PC or laptop).
*/
desktop: 'Desktop',
/**
* @description Network latency in milliseconds.
* @example {10} value
*/
latency: 'Latency: {value} ms',
/**
* @description Upload speed.
* @example {42 kB} value
*/
upload: 'Upload: {value}',
/**
* @description Download speed.
* @example {8 kB} value
*/
download: 'Download: {value}',
/**
* @description Title of the button to edit replay settings.
*/
editReplaySettings: 'Edit replay settings',
/**
* @description Title of the section that contains replay settings.
*/
replaySettings: 'Replay settings',
/**
* @description The string is shown when a default value is used for some replay settings.
*/
default: 'Default',
/**
* @description The title of the section with environment settings.
*/
environment: 'Environment',
/**
* @description The title of the screenshot image that is shown for every section in the recordign view.
*/
screenshotForSection: 'Screenshot for this section',
/**
* @description The title of the button that edits the current recording's title.
*/
editTitle: 'Edit title',
/**
* @description The error for when the title is missing.
*/
requiredTitleError: 'Title is required',
/**
* @description The status text that is shown while the recording is ongoing.
*/
recording: 'Recording…',
/**
* @description The title of the button to end the current recording.
*/
endRecording: 'End recording',
/**
* @description The title of the button while the recording is being ended.
*/
recordingIsBeingStopped: 'Stopping recording…',
/**
* @description The text that describes a timeout setting of {value} milliseconds.
* @example {1000} value
*/
timeout: 'Timeout: {value} ms',
/**
* @description The label for the input that allows entering network throttling configuration.
*/
network: 'Network',
/**
* @description The label for the input that allows entering timeout (a number in ms) configuration.
*/
timeoutLabel: 'Timeout',
/**
* @description The text in a tooltip for the timeout input that explains what timeout settings do.
*/
timeoutExplanation:
'The timeout setting (in milliseconds) applies to every action when replaying the recording. For example, if a DOM element identified by a CSS selector does not appear on the page within the specified timeout, the replay fails with an error.',
/**
* @description The label for the button that cancels replaying.
*/
cancelReplay: 'Cancel replay',
/**
* @description Button title that shows the code view when clicked.
*/
showCode: 'Show code',
/**
* @description Button title that hides the code view when clicked.
*/
hideCode: 'Hide code',
/**
* @description Button title that adds an assertion to the step editor.
*/
addAssertion: 'Add assertion',
/**
* @description The title of the button that open current recording in Performance panel.
*/
performancePanel: 'Performance panel',
/**
* @description The announcement when the code sidebar is opened.
*/
codeSidebarOpened: 'Code sidebar opened',
/**
* @description The announcement when the code sidebar is closed.
*/
codeSidebarClosed: 'Code sidebar closed'
} as const;
const str_ = i18n.i18n.registerUIStrings(
'panels/recorder/components/RecordingView.ts',
UIStrings,
);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
declare global {
interface HTMLElementTagNameMap {
'devtools-recording-view': RecordingView;
}
}
export interface ReplayState {
isPlaying: boolean; // Replay is in progress
isPausedOnBreakpoint: boolean; // Replay is in progress and is in stopped state
}
export const enum TargetPanel {
PERFORMANCE_PANEL = 'timeline',
DEFAULT = 'chrome-recorder',
}
export interface PlayRecordingEvent {
targetPanel: TargetPanel;
speed: PlayRecordingSpeed;
extension?: Extensions.ExtensionManager.Extension;
}
const networkConditionPresets = [
SDK.NetworkManager.NoThrottlingConditions,
SDK.NetworkManager.OfflineConditions,
SDK.NetworkManager.Slow3GConditions,
SDK.NetworkManager.Slow4GConditions,
SDK.NetworkManager.Fast4GConditions,
];
function renderSettings({
settings,
replaySettingsExpanded,
onSelectMenuLabelClick,
onNetworkConditionsChange,
onTimeoutInput,
isRecording,
replayState,
onReplaySettingsKeydown,
onToggleReplaySettings
}: ViewInput): Lit.LitTemplate {
if (!settings) {
return Lit.nothing;
}
const environmentFragments = [];
if (settings.viewportSettings) {
// clang-format off
environmentFragments.push(
html`
`,
);
// clang-format on
}
const replaySettingsFragments = [];
if (!replaySettingsExpanded) {
if (settings.networkConditionsSettings) {
if (settings.networkConditionsSettings.title) {
// clang-format off
replaySettingsFragments.push(
html`
${
settings.networkConditionsSettings.title
}
`,
);
// clang-format on
} else {
// clang-format off
replaySettingsFragments.push(html`
`;
/* eslint-enable @devtools/no-deprecated-component-usages */
// clang-format on
}
function renderTextEditor(input: ViewInput, output: ViewOutput): Lit.TemplateResult {
if (!input.editorState) {
throw new Error('Unexpected: trying to render the text editor without editorState');
}
// clang-format off
return html`